Back to index

plt-scheme  4.2.1
xwMenu.c
Go to the documentation of this file.
00001 /***********************************************************
00002 Copyright 2004-2009 PLT Scheme Inc.
00003 Copyright 1995 by Markus Holzem
00004 
00005                         All Rights Reserved
00006 
00007 Permission to use, copy, modify, and distribute this software and its 
00008 documentation for any purpose and without fee is hereby granted, 
00009 provided that the above copyright notice appear in all copies and that
00010 both that copyright notice and this permission notice appear in 
00011 supporting documentation, and that the names of Digital or MIT not be
00012 used in advertising or publicity pertaining to distribution of the
00013 software without specific, written prior permission.  
00014 
00015 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00016 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00017 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00018 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00019 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00020 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00021 SOFTWARE.
00022 
00023 ******************************************************************/
00024 
00025 /*
00026  * Menu.c - Menu widget
00027  *
00028  */
00029 
00030 #include <stdio.h>          /* for debugging purpose */
00031 
00032 #include <ctype.h>
00033 
00034 #include <X11/IntrinsicP.h>
00035 #include <X11/StringDefs.h>
00036 #include <X11/keysym.h>
00037 
00038 #include <xwMenuP.h>
00039 #include <xwTools3d.h>
00040 #include <xwTabString.h>
00041 
00042 #include <wxtimeout.h>
00043 
00044 #include "wx_visual.h"
00045 #include "wxAllocColor.h"
00046 #include "xwGray.h"
00047 
00048 extern int wxUseMenuHiliteBorder();
00049 
00050 #define XtNtopShadowPixmap       "topShadowPixmap"
00051 #define XtCTopShadowPixmap       "TopShadowPixmap"
00052 #define XtNbottomShadowPixmap    "bottomShadowPixmap"
00053 #define XtCBottomShadowPixmap    "BottomShadowPixmap"
00054 #define XtNindicatorPixmap "indicatorShadowPixmap"
00055 #define XtCIndicatorPixmap "IndicatorShadowPixmap"
00056 
00057 #define offset(field) XtOffset(MenuWidget, field)
00058 static XtResource MenuResources[] =
00059 { 
00060     /* cursor */
00061     {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
00062         offset(menu.cursor), XtRString, (XtPointer)"left_ptr"},
00063     /* font */
00064     {XtNfont,  XtCFont, XtRFontStruct, sizeof(XFontStruct *),
00065         offset(menu.font),XtRString, "XtDefaultFont"},
00066 #ifdef WX_USE_XFT
00067     {XtNmenuXftFont,  XtCMenuXftFont, XtRPointer, sizeof(void*),
00068         offset(menu.xft_font),XtRPointer, (XtPointer)0},
00069 #endif
00070 
00071     /* border and shadow width */
00072     {XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
00073        XtOffsetOf(RectObjRec,rectangle.border_width), XtRImmediate,
00074        (XtPointer)0},
00075     {XtNshadowWidth, XtCShadowWidth, XtRDimension, sizeof(Dimension),
00076        offset(menu.shadow_width), XtRImmediate, (XtPointer) 2},
00077     {XtNrequestedWidth, XtCRequestedWidth, XtRDimension, sizeof(Dimension),
00078        offset(menu.requested_width), XtRImmediate, (XtPointer) 0},
00079 
00080     /* Foreground Colour */
00081     {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
00082         offset(menu.foreground), XtRString, "XtDefaultForeground"},
00083 
00084     /* Data to compute the top and bottom shadow GCs */
00085     {XtNbeNiceToColormap, XtCBeNiceToColormap, XtRBoolean, sizeof(Boolean),
00086        offset(menu.be_nice_to_cmap), XtRImmediate, (XtPointer) False},
00087     {XtNtopShadowPixel, XtCTopShadowPixel, XtRPixel, sizeof(Pixel),
00088        offset(menu.top_shadow_pixel), XtRImmediate, (XtPointer)-1},
00089     {XtNtopShadowPixmap, XtCTopShadowPixmap, XtRPixmap, sizeof(Pixmap),
00090        offset(menu.top_shadow_pxmap), XtRImmediate, (XtPointer)0},
00091     {XtNtopShadowContrast, XtCTopShadowContrast, XtRInt, sizeof(int),
00092        offset(menu.top_shadow_contrast), XtRImmediate, (XtPointer)120},
00093     {XtNbottomShadowPixel, XtCBottomShadowPixel, XtRPixel, sizeof(Pixel),
00094        offset(menu.bot_shadow_pixel), XtRImmediate, (XtPointer)-1},
00095     {XtNbottomShadowPixmap, XtCBottomShadowPixmap, XtRPixmap, sizeof(Pixmap),
00096        offset(menu.bot_shadow_pxmap), XtRImmediate, (XtPointer)0},
00097     {XtNbottomShadowContrast, XtCBottomShadowContrast, XtRInt, sizeof(int),
00098        offset(menu.bot_shadow_contrast), XtRImmediate, (XtPointer)60},
00099 
00100     /* data for toggle, radio and cascade indicators */
00101     {XtNindicatorPixel, XtCIndicatorPixel, XtRPixel, sizeof(Pixel),
00102          offset(menu.indicator_pixel), XtRImmediate, (XtPointer)-1},
00103     {XtNindicatorPixmap, XtCIndicatorPixmap, XtRPixmap, sizeof(Pixmap),
00104          offset(menu.indicator_pxmap), XtRImmediate, (XtPointer)0},
00105     {XtNindicatorContrast, XtCIndicatorContrast, XtRInt, sizeof(int),
00106          offset(menu.indicator_contrast), XtRImmediate, (XtPointer)85},
00107     {XtNindicatorSize, XtCIndicatorSize, XtRDimension, sizeof(Dimension),
00108         offset(menu.indicator_size), XtRImmediate, (XtPointer)0},
00109 
00110     {XtNextraLeft, XtCExtraLeft, XtRDimension, sizeof(Dimension),
00111        offset(menu.extra_left), XtRImmediate, (XtPointer)0},
00112     {XtNextraTop, XtCExtraTop, XtRDimension, sizeof(Dimension),
00113        offset(menu.extra_top), XtRImmediate, (XtPointer)0},
00114     {XtNextraRight, XtCExtraRight, XtRDimension, sizeof(Dimension),
00115        offset(menu.extra_right), XtRImmediate, (XtPointer)0},
00116     {XtNextraBottom, XtCExtraBottom, XtRDimension, sizeof(Dimension),
00117        offset(menu.extra_bottom), XtRImmediate, (XtPointer)0},
00118 
00119     {XtNhighlightPixel, XtCHighlightPixel, XtRPixel, sizeof(Pixel),
00120          offset(menu.highlight_pixel), XtRImmediate, (XtPointer)-1},
00121 
00122     {XtNhighlightShadowPixel, XtCHighlightShadowPixel, XtRPixel, sizeof(Pixel),
00123          offset(menu.highlight_top_pixel), XtRImmediate, (XtPointer)-1},
00124 
00125     /* margins around the menu items */
00126     {XtNhMargin, XtCHMargin, XtRDimension, sizeof(Dimension),
00127         offset(menu.hmargin), XtRImmediate, (XtPointer)1},
00128 
00129     {XtNhorizontal, XtCHorizontal, XtRBoolean, sizeof(Boolean),
00130        offset(menu.horizontal), XtRImmediate, (XtPointer) True},
00131     {XtNforChoice, XtCForChoice, XtRBoolean, sizeof(Boolean),
00132        offset(menu.forChoice), XtRImmediate, (XtPointer) False},
00133     {XtNforPopup, XtCForPopup, XtRBoolean, sizeof(Boolean),
00134        offset(menu.forPopup), XtRImmediate, (XtPointer) False},
00135 
00136     /* menu structure */
00137     {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer),
00138         offset(menu.contents), XtRImmediate, (XtPointer)NULL},
00139     {XtNrefresh, XtCRefresh, XtRBoolean, sizeof(Boolean),
00140         offset(menu.refresh), XtRBoolean, (XtPointer)False},
00141 
00142     /* callbacks called on item change and item select */
00143     {XtNonNewItem, XtCCallback, XtRCallback, sizeof(XtPointer), 
00144         offset(menu.on_new_item), XtRCallback, (XtPointer)NULL},
00145     {XtNonSelect, XtCCallback, XtRCallback, sizeof(XtPointer), 
00146         offset(menu.on_select), XtRCallback, (XtPointer)NULL},
00147     {XtNonNoSelect, XtCCallback, XtRCallback, sizeof(XtPointer), 
00148         offset(menu.on_no_select), XtRCallback, (XtPointer)NULL},
00149     {XtNonMDestroy, XtCCallback, XtRCallback, sizeof(XtPointer), 
00150         offset(menu.on_destroy), XtRCallback, (XtPointer)NULL},
00151 };
00152 #undef offset
00153 
00154 #define VMARGIN 2
00155 #define ISPACING 4
00156 #define CHOICE_LEFT 1
00157 #define CHOICE_RIGHT 13
00158 
00159 static void    MenuClassInitialize();
00160 static void    MenuDestroy();
00161 static void    MenuInitialize();
00162 static void    MenuRealize();
00163 static void    MenuRedisplay();
00164 static void    MenuResize();
00165 static Boolean MenuSetValues();
00166 
00167 static void Start();
00168 static void Drag();
00169 static void Select();
00170 static void Motion();
00171 static void Key();
00172 
00173 static XtActionsRec MenuActionsList [] = {
00174     {"start", Start  },
00175     {"drag",  Drag   },
00176     {"select",       Select },
00177     {"motion",       Motion },
00178     {"key",   Key }
00179 };
00180 
00181 static char MenuTranslations [] = 
00182 "<BtnDown>:          start()\n\
00183 <Motion>:             motion()\n\
00184 Button1 <Motion>:    drag()\n\
00185 Button2 <Motion>:    drag()\n\
00186 Button3 <Motion>:    drag()\n\
00187 <BtnUp>:             select()\n\
00188 <KeyPress>:          key()\n\
00189 ";
00190 
00191 #define SuperClass ((CoreWidgetClass)&coreClassRec)
00192 
00193 MenuClassRec menuClassRec = {
00194   {
00195 /* core_class fields */     
00196     /* superclass           */     (WidgetClass) SuperClass,
00197     /* class_name           */     "Menu",
00198     /* widget_size          */     sizeof(MenuRec),
00199     /* class_initialize     */     MenuClassInitialize,
00200     /* class_part_initialize       */     NULL,
00201     /* class_inited         */     FALSE,
00202     /* initialize           */     MenuInitialize,
00203     /* initialize_hook      */     NULL,
00204     /* realize              */     MenuRealize,
00205     /* actions              */     MenuActionsList,
00206     /* num_actions   */     XtNumber(MenuActionsList),
00207     /* resources            */     MenuResources,
00208     /* num_resources */     XtNumber(MenuResources),
00209     /* xrm_class            */     NULLQUARK,
00210     /* compress_motion      */     TRUE,
00211     /* compress_exposure    */     TRUE,
00212     /* compress_enterleave  */     TRUE,
00213     /* visible_interest     */     FALSE,
00214     /* destroy              */     MenuDestroy,
00215     /* resize        */     MenuResize,
00216     /* expose        */     MenuRedisplay,
00217     /* set_values           */     MenuSetValues,
00218     /* set_values_hook      */     NULL,
00219     /* set_values_almost    */     XtInheritSetValuesAlmost,
00220     /* get_values_hook      */     NULL,
00221     /* accept_focus  */     NULL,
00222     /* version              */     XtVersion,
00223     /* callback_private     */     NULL,
00224     /* tm_table             */     MenuTranslations,
00225     /* query_geometry       */     XtInheritQueryGeometry,
00226     /* display_accelerator  */     XtInheritDisplayAccelerator,
00227     /* extension            */     NULL
00228   },
00229 /* Menu class fields initialization */
00230   {
00231     /* ignore               */     0
00232   }
00233 };
00234 
00235 WidgetClass menuWidgetClass = (WidgetClass) &menuClassRec;
00236 
00237 void FreeTimer(long timer)
00238 {
00239   if (timer) {
00240     wxRemoveTimeOut(timer);
00241   }
00242 }
00243 
00244 static int HasHotKey(char *l, int key)
00245 {
00246   int i;
00247 
00248   if (!l)
00249     return 0;
00250 
00251   for (i = 0; l[i]; i++) {
00252     if ((l[i] == '&') 
00253        && (((l[i+1] > 0)
00254             && (key > 0)
00255             && (key < 128)
00256             && (tolower(l[i+1]) == tolower(key)))
00257            || (l[i+1] == key)))
00258       return 1;
00259   }
00260 
00261   return 0;
00262 }
00263 
00264 
00265 /******************************************************************************
00266  *
00267  * Intrinsic Methods
00268  *
00269  *****************************************************************************/
00270 
00271 static void CreateGCs(MenuWidget mw);
00272 static void CreateShadowGCs(MenuWidget mw);
00273 static void ReleaseGCs(MenuWidget mw);
00274 static void ReleaseShadowGCs(MenuWidget mw);
00275 static void ComputeMenuSize(MenuWidget mw, menu_state *ms);
00276 static void DisplayMenu(MenuWidget mw, menu_state *ms);
00277 
00278 /* ARGSUSED */
00279 static void MenuInitialize(request, new, args, num_args)
00280     Widget request, new;
00281     ArgList args;
00282     Cardinal *num_args;
00283 {
00284     MenuWidget mw   = (MenuWidget)new;
00285 
00286     CreateGCs(mw);
00287     CreateShadowGCs(mw);
00288 
00289     if (!mw->menu.indicator_size
00290        ||  mw->menu.indicator_size > wx_ASCENT(mw->menu.font, mw->menu.xft_font))
00291       mw->menu.indicator_size = wx_ASCENT(mw->menu.font, mw->menu.xft_font);
00292 
00293     mw->menu.popped_up       = FALSE;
00294     mw->menu.state           = (menu_state*)XtMalloc(sizeof(menu_state));
00295     mw->menu.state->menu     = mw->menu.contents;
00296     mw->menu.state->selected = NULL;
00297     mw->menu.state->prev     = NULL;
00298     mw->menu.state->delta    = 0;
00299     mw->menu.state->scrolled = 0;
00300     mw->menu.state->timer    = 0;
00301 
00302     mw->menu.moused_out = 0;
00303     mw->menu.grabbed = FALSE;
00304 
00305     ComputeMenuSize(mw, mw->menu.state);
00306     mw->core.width  = mw->menu.state->w;
00307     mw->core.height = mw->menu.state->h;
00308 }
00309 
00310 /* ARGSUSED */
00311 static void MenuClassInitialize()
00312 {
00313 }
00314 
00315 /* ARGSUSED */
00316 static void MenuRealize(w, value_mask, window_attributes)
00317     Widget               w;
00318     Mask                 *value_mask;
00319     XSetWindowAttributes *window_attributes;
00320 {
00321     MenuWidget mw             = (MenuWidget)w;
00322     XSetWindowAttributes xswa;
00323 
00324     (*menuWidgetClass->core_class.superclass->core_class.realize)
00325        (w, value_mask, window_attributes);
00326     xswa.save_under = TRUE;
00327     xswa.cursor     = mw->menu.cursor;
00328     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
00329                          CWSaveUnder|CWCursor, &xswa);
00330     mw->menu.state->win = XtWindow(w);
00331     mw->menu.state->w   = w->core.width;
00332     mw->menu.state->h   = w->core.height;
00333 }
00334 
00335 /* ARGSUSED */
00336 static void MenuRedisplay(w, ev, region)
00337     Widget  w;
00338     XEvent  *ev;
00339     Region  region;
00340 {
00341     MenuWidget  mw = (MenuWidget)w;
00342     menu_state  *ms;
00343 
00344 /* MATTHEW: Only translate menu bar.
00345     for (ms=mw->menu.state; ms->prev; ms=ms->prev);
00346     XtTranslateCoords(w, w->core.x, w->core.y, &ms->x, &ms->y);
00347 */
00348 
00349     for (ms=mw->menu.state; ms; ms=ms->prev)
00350        DisplayMenu(mw, ms);
00351 }
00352 
00353 /* ARGSUSED */
00354 static void MenuDestroy(w)
00355     Widget  w;
00356 {
00357     MenuWidget  mw  = (MenuWidget)w;
00358     menu_state  *ms = mw->menu.state,
00359                *last;
00360 
00361     ReleaseGCs(mw);
00362     ReleaseShadowGCs(mw);
00363     while (ms != mw->menu.state) { /* don't destroy the widget's window! */
00364        XDestroyWindow(XtDisplay(mw), ms->win);
00365        last = ms;
00366        ms   = ms->prev;
00367        FreeTimer(last->timer);
00368        XtFree((char*)last);
00369     }
00370     FreeTimer(ms->timer);
00371     XtFree((char*)ms); /* free menu_state of widget's window */
00372 
00373     XtCallCallbackList(w, mw->menu.on_destroy, (XtPointer)NULL);
00374 }
00375 
00376 #define CHANGED_bg_  (new->core.background_pixel != old->core.background_pixel)
00377 #define CHANGED(val) (new->menu.val != old->menu.val)
00378 #define NEW(val)     (new->menu.val)
00379 #define OLD(val)     (old->menu.val)
00380 
00381 /* ARGUSED */
00382 static Boolean MenuSetValues(gcurrent, grequest, gnew)
00383     Widget gcurrent, grequest, gnew;
00384 {
00385     MenuWidget  old = (MenuWidget)gcurrent;
00386     MenuWidget  new = (MenuWidget)gnew;
00387     Boolean     redisplay;
00388 
00389     redisplay = (*SuperClass->core_class.set_values)(gcurrent, grequest, gnew, NULL, 0);
00390 
00391     {
00392       int ow, oh;
00393       ow = new->menu.state->w;
00394       oh = new->menu.state->h;
00395       new->menu.state->menu = new->menu.contents;
00396       ComputeMenuSize(new, new->menu.state);
00397       new->menu.state->w = new->core.width;
00398       new->menu.state->h = new->core.height;
00399       if ((ow != new->menu.state->w)
00400          || (oh != new->menu.state->h)) {
00401        redisplay = TRUE;
00402       }
00403     }
00404 
00405     if (new->menu.refresh) {
00406       new->menu.refresh = 0;
00407       redisplay = TRUE;
00408     }
00409 
00410     if (CHANGED_bg_
00411        || CHANGED(top_shadow_pixel) || CHANGED(top_shadow_contrast)
00412        || CHANGED(bot_shadow_pixel) || CHANGED(bot_shadow_contrast)) {
00413        ReleaseShadowGCs(new);
00414        CreateShadowGCs(new);
00415        redisplay = TRUE;
00416     }
00417     if (CHANGED_bg_
00418        || CHANGED(foreground) || CHANGED(font)
00419 #ifdef WX_USE_XFT
00420        || CHANGED(xft_font)
00421 #endif
00422        || CHANGED(indicator_pixel)  || CHANGED(indicator_contrast)) {
00423        ReleaseGCs(new);
00424        CreateGCs(new);
00425        redisplay = TRUE;
00426     }
00427 
00428     return redisplay;
00429 }
00430 
00431 #undef CHANGED_bg_
00432 #undef CHANGED
00433 #undef NEW
00434 #undef OLD
00435 
00436 /* ARGUSED */
00437 static void MenuResize(w)
00438     Widget w;
00439 {
00440     MenuWidget  mw = (MenuWidget)w;
00441 
00442     ComputeMenuSize(mw, mw->menu.state);
00443     mw->menu.state->w = mw->core.width;
00444     mw->menu.state->h = mw->core.height;
00445 }
00446     
00447 
00448 /******************************************************************************
00449  *
00450  * Action Procedures
00451  *
00452  *****************************************************************************/
00453 
00454 static int HandleMotionEvent(MenuWidget mw, XMotionEvent *ev, int is_click);
00455 static void UnhighlightItem(MenuWidget mw, menu_state *ms, menu_item *item);
00456 static void HighlightItem(MenuWidget mw, menu_state *ms, menu_item *item);
00457 static void MoveSelection(MenuWidget mw, int direction);
00458 
00459 static void DoSelect(Widget w, Time time, int force);
00460 
00461 extern void wxAddGrab(Widget);
00462 extern void wxRemoveGrab(Widget);
00463 
00464 /* ARGSUSED */
00465 static void Start(w, ev, params, num_params)
00466     Widget    w;
00467     XEvent    *ev;
00468     String    *params;
00469     Cardinal  *num_params;
00470 {
00471     MenuWidget  mw = (MenuWidget)w;
00472 
00473     XSync(XtDisplay(mw), FALSE);
00474 
00475     if (!mw->menu.state->prev) {
00476       mw->menu.state->x        = ev->xmotion.x_root - ev->xmotion.x;
00477       mw->menu.state->y        = ev->xmotion.y_root - ev->xmotion.y;
00478     }
00479 
00480     if (!mw->menu.grabbed) {
00481       XtGrabPointer((Widget)mw, FALSE,
00482                   (ButtonMotionMask | PointerMotionMask |
00483                    ButtonReleaseMask | ButtonPressMask),
00484                   GrabModeAsync, GrabModeAsync,
00485                   None, mw->menu.cursor, 
00486                   /* ev->xmotion.time */ CurrentTime);
00487       XtGrabKeyboard((Widget)mw, FALSE,
00488                    GrabModeAsync, GrabModeAsync,
00489                    /* ev->xmotion.time */ CurrentTime);
00490       wxAddGrab((Widget)mw);
00491       mw->menu.grabbed = TRUE;
00492     }
00493 
00494     if (!HandleMotionEvent(mw, &ev->xmotion, 1)
00495         /* *num_params is positive when called to start a popup menu */
00496         && !*num_params)
00497       DoSelect(w, CurrentTime, 1);
00498 }
00499 
00500 /* ARGSUSED */
00501 static void Drag(w, event, params, num_params)
00502     Widget    w;
00503     XEvent    *event;
00504     String    *params;
00505     Cardinal  *num_params;
00506 {
00507     MenuWidget    mw    = (MenuWidget)w;
00508     XMotionEvent  *ev   = &event->xmotion;
00509     int           x     = ev->x_root;
00510     int           y     = ev->y_root;
00511     int           state = ev->state;
00512 
00513     HandleMotionEvent(mw, ev, 0);
00514     XSync(XtDisplay(mw), FALSE);
00515     /* allow motion events to be generated again */
00516     if ((!ev->is_hint
00517         || XQueryPointer(XtDisplay(mw), ev->window,
00518                        &ev->root, &ev->subwindow,
00519                        &ev->x_root, &ev->y_root,
00520                        &ev->x, &ev->y, &ev->state))
00521        && ev->state == state
00522        && (ev->x_root != x || ev->y_root != y)) {
00523       HandleMotionEvent(mw, ev, 0);
00524       XSync(XtDisplay(mw), FALSE);
00525     }
00526 }
00527 
00528 /* ARGSUSED */
00529 static void DoSelect(w, time, force)
00530     Widget    w;
00531     Time      time;
00532     int       force;
00533 {
00534     MenuWidget  mw = (MenuWidget)w;
00535     menu_item   *selected_item = mw->menu.state->selected;
00536     menu_state  *ms;
00537 
00538     if (!force && !selected_item)
00539       return;
00540 
00541     if (mw->menu.grabbed) {
00542       XtUngrabPointer(w,time);
00543       XtUngrabKeyboard(w, time);
00544       wxRemoveGrab(w);
00545       mw->menu.grabbed = FALSE;
00546     }
00547 
00548     for (ms=mw->menu.state; ms->prev!=NULL; ms=ms->prev)
00549        ;
00550     UnhighlightItem(mw, ms, ms->selected);
00551     ms->selected = NULL;
00552     ms->delta = 0;
00553     if (mw->menu.popped_up) {
00554        mw->menu.popped_up = FALSE;
00555        XtPopdown(XtParent(mw));
00556     }
00557 
00558     XFlush(XtDisplay(mw));
00559 
00560     if (selected_item
00561        && selected_item->enabled 
00562        && selected_item->type != MENU_TEXT
00563        && selected_item->type != MENU_SEPARATOR
00564        && selected_item->type != MENU_PUSHRIGHT)
00565       XtCallCallbackList(w, mw->menu.on_select, (XtPointer)selected_item);
00566     else
00567       XtCallCallbackList(w, mw->menu.on_no_select, (XtPointer)NULL);
00568 }
00569 
00570 static void Select(w, event, params, num_params)
00571     Widget    w;
00572     XEvent    *event;
00573     String    *params;
00574     Cardinal  *num_params;
00575 {
00576   MenuWidget  mw = (MenuWidget)w;
00577   XMotionEvent  *ev   = event ? &event->xmotion : NULL;
00578   int force;
00579 
00580   mw->menu.moused_out = 0;
00581 
00582   if (!mw->menu.forPopup 
00583       && (!mw->menu.state || !mw->menu.state->selected))
00584     return;
00585 
00586   force = !HandleMotionEvent(mw, ev, 0);
00587   if (!force)
00588     force = mw->menu.moused_out;
00589   
00590   DoSelect(w, event ? event->xmotion.time : 0L, force);
00591 }
00592 
00593 static void Motion(w, event, params, num_params)
00594     Widget    w;
00595     XEvent    *event;
00596     String    *params;
00597     Cardinal  *num_params;
00598 {
00599   MenuWidget  mw = (MenuWidget)w;
00600 
00601   if (mw->menu.grabbed)
00602     Drag(w, event, params, num_params);
00603 }
00604 
00605 static void Key(w, event, params, num_params)
00606     Widget    w;
00607     XEvent    *event;
00608     String    *params;
00609     Cardinal  *num_params;
00610 {
00611   MenuWidget  mw = (MenuWidget)w;
00612   XKeyEvent  *ev   = &event->xkey;
00613   KeySym         keysym;
00614 
00615   (void)XLookupString(ev, NULL, 0, &keysym, NULL);
00616 
00617   switch (keysym) {
00618   case XK_Right:
00619   case XK_Left:
00620     if (mw->menu.state && mw->menu.state->prev && mw->menu.state->prev->prev) {
00621       /* In a submenu */
00622       if (keysym == XK_Right) {
00623        if (!mw->menu.state->selected) {
00624          /* select first in submenu */
00625          if (mw->menu.state->menu)
00626            HighlightItem(mw, mw->menu.state, mw->menu.state->menu);
00627        }
00628       } else {
00629        if (mw->menu.state->selected) {
00630          UnhighlightItem(mw, mw->menu.state, mw->menu.state->selected);
00631        }
00632       }
00633     } else {
00634       /* Change top-level menu: */
00635       if (mw->menu.state && mw->menu.state->prev && mw->menu.state->prev->selected) {
00636        menu_item *item, *orig_item;
00637        item = orig_item = mw->menu.state->prev->selected;
00638        do {
00639          if (keysym == XK_Right) {
00640            if (item->next)
00641              item = item->next;
00642            else
00643              item = mw->menu.state->prev->menu;
00644          } else {
00645            if (item->prev)
00646              item = item->prev;
00647            else {
00648              while (item->next)
00649               item = item->next;
00650            }
00651          }
00652        } while (item && (item != orig_item) && !item->enabled);
00653        if (item && (item != orig_item)) {
00654          UnhighlightItem(mw, mw->menu.state->prev, mw->menu.state->prev->selected);
00655          HighlightItem(mw, mw->menu.state, item);
00656        }
00657       }
00658     }
00659     break;
00660   case XK_Down:
00661     MoveSelection(mw, 1);
00662     break;
00663   case XK_Up:
00664     MoveSelection(mw, -1);
00665     break;
00666   case XK_Escape:
00667     if (mw->menu.state->selected)
00668       UnhighlightItem(mw, mw->menu.state, mw->menu.state->selected);
00669     DoSelect(w, event ? event->xkey.time : 0L, 1);
00670     break;
00671   case XK_Return:
00672     DoSelect(w, event ? event->xkey.time : 0L, 1);
00673     break;
00674   default:
00675     /* Look for menu to select */
00676     {
00677       menu_item *item;
00678       if (mw->menu.state->prev)
00679        item = mw->menu.state->prev->selected->contents;
00680       else
00681        item = NULL;
00682       for (; item; item = item->next) {
00683        if (item->enabled && HasHotKey(item->label, keysym)) {
00684          if (mw->menu.state->selected != item) {
00685            UnhighlightItem(mw, mw->menu.state, mw->menu.state->selected);
00686            HighlightItem(mw, mw->menu.state, item);
00687          }
00688          if (!item->contents)
00689            DoSelect(w, event ? event->xkey.time : 0L, 1);
00690          break;
00691        }
00692       }
00693     }
00694     break;
00695   }
00696 }
00697 
00698 
00699 /******************************************************************************
00700  *
00701  * Create and Release GCs Code
00702  *
00703  *****************************************************************************/
00704 
00705 extern Boolean  get_scaled_color(Widget,float ,Pixel ,Pixel *);
00706 
00707 static void CreateGCs(MenuWidget mw)
00708 {
00709     Display    *dpy = XtDisplay((Widget)mw);
00710     Screen     *scr = XtScreen((Widget)mw);
00711     Window     win  = RootWindowOfScreen(DefaultScreenOfDisplay(dpy));
00712     XGCValues  xgcv;
00713     int        gcf_flag = 0;
00714 
00715     mw->menu.stipple_pxmap = XCreatePixmapFromBitmapData(dpy, win,
00716                              wx_gray_bits, wx_gray_width, wx_gray_height, 1, 0, 1);
00717 
00718     if (mw->menu.font) {
00719       xgcv.font = mw->menu.font->fid;
00720       gcf_flag = GCFont;
00721     }
00722 
00723     xgcv.foreground = mw->core.background_pixel;
00724     xgcv.background = mw->menu.foreground;
00725     mw->menu.erase_GC = XtGetGC((Widget)mw,
00726                             gcf_flag|GCForeground|GCBackground,
00727                             &xgcv);
00728 
00729     xgcv.foreground = mw->menu.foreground;
00730     xgcv.background = mw->core.background_pixel;
00731     mw->menu.normal_GC = XtGetGC((Widget)mw,
00732                              gcf_flag|GCForeground|GCBackground,
00733                              &xgcv);
00734     
00735     if (wx_enough_colors(scr)) {
00736       Pixel r;
00737       get_scaled_color((Widget)mw, 0.6, xgcv.background, &r);
00738       xgcv.foreground = r;
00739       mw->menu.inactive_GC = XtGetGC((Widget)mw,
00740                                  gcf_flag|GCForeground|GCBackground,
00741                                  &xgcv);
00742     } else {
00743       xgcv.fill_style = FillStippled;
00744       xgcv.stipple    = mw->menu.stipple_pxmap;
00745       mw->menu.inactive_GC = XtGetGC((Widget)mw,
00746                                  gcf_flag|GCForeground|GCBackground|
00747                                  GCFillStyle|GCStipple,
00748                                  &xgcv);
00749     }
00750 
00751     if (DefaultDepthOfScreen(scr) == 1) {
00752        mw->menu.indicator_pxmap = Xaw3dAllocPixmap((Widget)mw,
00753                                               mw->core.background_pixel,
00754                                               GRAY);
00755     } else {
00756        if (mw->menu.indicator_pixel == -1) {
00757          Pixel res;
00758          get_scaled_color((Widget)mw, 
00759                         mw->menu.indicator_contrast/100.0, 
00760                         mw->core.background_pixel, &res);
00761          mw->menu.indicator_pixel = res;
00762          mw->menu.indicator_pxmap = (Pixmap)0;
00763        }
00764        if (mw->menu.highlight_pixel == -1) {
00765          XColor color;
00766          color.red = 0;
00767          color.green = 0;
00768          color.blue = 180 << 8;
00769          wxAllocColor(XtDisplay(mw), wx_default_colormap, &color);
00770          mw->menu.highlight_pixel = color.pixel;
00771        }
00772        if (mw->menu.highlight_top_pixel == -1) {
00773          Pixel res;
00774          get_scaled_color((Widget)mw, 1.35, mw->menu.highlight_pixel, &res);
00775          mw->menu.highlight_top_pixel = res;
00776        }
00777     }
00778     mw->menu.indicator_GC = Xaw3dGetGC((Widget)mw, 0,
00779                                    mw->menu.indicator_pxmap,
00780                                    mw->menu.indicator_pixel);
00781     mw->menu.highlight_GC = Xaw3dGetGC((Widget)mw, 0,
00782                                    0,
00783                                    mw->menu.highlight_pixel);
00784     mw->menu.highlight_top_GC = Xaw3dGetGC((Widget)mw, 0,
00785                                       0,
00786                                       mw->menu.highlight_top_pixel);
00787 }
00788 
00789 static void CreateShadowGCs(MenuWidget mw)
00790 {
00791     Screen  *scr = XtScreen((Widget)mw);
00792     Pixel   bg   = mw->core.background_pixel, res;
00793 
00794     if (DefaultDepthOfScreen (scr) == 1) {
00795        mw->menu.top_shadow_pxmap = Xaw3dAllocPixmap((Widget)mw, bg, LIGHTER);
00796        mw->menu.bot_shadow_pxmap = Xaw3dAllocPixmap((Widget)mw, bg, DARKER);
00797     } else {
00798        if (mw->menu.top_shadow_pixel == -1) {
00799          get_scaled_color((Widget)mw, mw->menu.top_shadow_contrast/100.0, bg, &res);
00800          mw->menu.top_shadow_pixel = res;
00801          mw->menu.top_shadow_pxmap = (Pixmap)0;
00802        }
00803        if (mw->menu.bot_shadow_pixel == -1) {
00804          get_scaled_color((Widget)mw, mw->menu.bot_shadow_contrast/100.0, bg, &res);
00805          mw->menu.bot_shadow_pixel = res;
00806          mw->menu.bot_shadow_pxmap = (Pixmap)0;
00807        }
00808     }
00809     mw->menu.top_shadow_GC = Xaw3dGetGC((Widget)mw, 0,
00810                                    mw->menu.top_shadow_pxmap,
00811                                    mw->menu.top_shadow_pixel);
00812     mw->menu.bot_shadow_GC = Xaw3dGetGC((Widget)mw, 0,
00813                                    mw->menu.bot_shadow_pxmap,
00814                                    mw->menu.bot_shadow_pixel);
00815 }
00816 
00817 static void ReleaseGCs(MenuWidget mw)
00818 {
00819     XtReleaseGC((Widget)mw, mw->menu.erase_GC);
00820     XtReleaseGC((Widget)mw, mw->menu.normal_GC);
00821     XtReleaseGC((Widget)mw, mw->menu.inactive_GC);
00822     XFreePixmap(XtDisplay(mw), mw->menu.stipple_pxmap);
00823     Xaw3dReleaseGC(mw, mw->menu.indicator_GC);
00824     Xaw3dFreePixmap(mw, mw->menu.indicator_pxmap);
00825     Xaw3dReleaseGC(mw, mw->menu.highlight_GC);
00826 }
00827 
00828 static void ReleaseShadowGCs(MenuWidget mw)
00829 {
00830     Xaw3dReleaseGC(mw, mw->menu.top_shadow_GC);
00831     Xaw3dReleaseGC(mw, mw->menu.bot_shadow_GC);
00832     Xaw3dFreePixmap(mw, mw->menu.top_shadow_pxmap);
00833     Xaw3dFreePixmap(mw, mw->menu.bot_shadow_pxmap);
00834     Xaw3dReleaseGC(mw, mw->menu.highlight_top_GC);
00835 }
00836 
00837 
00838 /******************************************************************************
00839  *
00840  * Size Code
00841  *
00842  *****************************************************************************/
00843 
00844 /* Utilities */
00845 
00846 static unsigned StringWidth(MenuWidget mw, char *s)
00847 {
00848     return XfwfTextWidth(XtDisplay(mw),
00849                       mw->menu.font, wxEXT_FONT(mw->menu.xft_font), 
00850                       s, strlen(s), NULL);
00851 }
00852 
00853 static void GetResourceName(char *in, char *out)
00854 {
00855     char *first = out;
00856 
00857     while (*in)
00858        if (isalnum((unsigned char)*in) || (*in)=='_')
00859            *out++ = *in++;
00860        else
00861            in++;
00862     *first = tolower(*first);
00863     *out   = '\0';
00864 }
00865 
00866 /*
00867 Moved to Menu.c
00868 
00869 typedef enum _e_Subresource {
00870     SUBRESOURCE_LABEL = 0,
00871     SUBRESOURCE_HELP = 1,
00872     SUBRESOURCE_KEY = 2
00873 } Subresource;
00874 */
00875 
00876 char *ResourcedText(MenuWidget mw, menu_item *item, Subresource type)
00877 {
00878     static XtResource labelResource[] = {
00879        { "label", "Label", XtRString, sizeof(String), 0, XtRImmediate, 0 },
00880        { "help",  "Help",  XtRString, sizeof(String), 0, XtRImmediate, 0 },
00881        { "key",   "Key",   XtRString, sizeof(String), 0, XtRImmediate, 0 },
00882     };
00883 
00884     char resource_name[1024];
00885     char *resourced_text=NULL;
00886 
00887     GetResourceName(item->label, resource_name);
00888     XtGetSubresources((Widget)mw, (XtPointer)(&resourced_text),
00889                     resource_name, resource_name, &labelResource[type],
00890                     1, NULL, 0);
00891     if (!resourced_text)
00892        switch (type) {
00893        case SUBRESOURCE_LABEL:  return item->label;
00894        case SUBRESOURCE_HELP:   return item->help_text;
00895        case SUBRESOURCE_KEY:    return item->key_binding;
00896        }
00897     return (resourced_text);
00898 }
00899 
00900 /* Size Functions */
00901 
00902 static void MenuTextSize(MenuWidget mw, menu_item *item, Boolean in_menubar,
00903                       unsigned *l, unsigned *m, unsigned *r, unsigned *h)
00904 {
00905   *h = (wx_ASCENT(mw->menu.font, mw->menu.xft_font) 
00906        + wx_DESCENT(mw->menu.font, mw->menu.xft_font)
00907        + 2*VMARGIN + 2*mw->menu.shadow_width);
00908   *l = *r = mw->menu.hmargin + mw->menu.shadow_width;
00909   if (mw->menu.forChoice) {
00910     *l += CHOICE_LEFT;
00911     *r += CHOICE_RIGHT;
00912   }
00913   *m = StringWidth(mw, ResourcedText(mw, item, SUBRESOURCE_LABEL));
00914 }
00915 
00916 static void MenuButtonSize(MenuWidget mw, menu_item *item, Boolean in_menubar,
00917                         unsigned *l, unsigned *m, unsigned *r, unsigned *h)
00918 {
00919     MenuTextSize(mw, item, in_menubar, l, m, r, h);
00920     if (!in_menubar && item->key_binding)
00921        *r += StringWidth(mw, ResourcedText(mw, item, SUBRESOURCE_KEY)) 
00922              + (3 * ISPACING);
00923 }
00924 
00925 static void MenuToggleSize(MenuWidget mw, menu_item *item, Boolean in_menubar,
00926                         unsigned *l, unsigned *m, unsigned *r, unsigned *h)
00927 {
00928     MenuButtonSize(mw, item, in_menubar, l, m, r, h);
00929     *l += mw->menu.indicator_size + ISPACING;
00930 }
00931 
00932 static void MenuCascadeSize(MenuWidget mw, menu_item *item, Boolean in_menubar,
00933                          unsigned *l, unsigned *m, unsigned *r, unsigned *h)
00934 {
00935     MenuTextSize(mw, item, in_menubar, l, m, r, h);
00936     if (!in_menubar)
00937        *r += mw->menu.indicator_size + ISPACING;
00938 }
00939 
00940 static void MenuPushrightSize(MenuWidget mw,menu_item *item,Boolean in_menubar,
00941                            unsigned *l,unsigned *m,unsigned *r,unsigned *h)
00942 {
00943     *l = *m =  *r = *h = 0;
00944 }
00945 
00946 static void MenuSeparatorSize(MenuWidget mw,menu_item *item,Boolean in_menubar,
00947                            unsigned *l,unsigned *m,unsigned *r,unsigned *h)
00948 {
00949     *l = *m =  *r = *h = 0;
00950     if (!in_menubar) {
00951        *h = mw->menu.shadow_width;
00952        *m = 1;
00953     }
00954 }
00955 
00956 typedef void (*SizeFunction)(MenuWidget, menu_item*, Boolean, unsigned*,
00957                           unsigned*, unsigned*, unsigned*);
00958 static SizeFunction SizeFunctionList[] = {
00959     MenuTextSize,
00960     MenuButtonSize,
00961     MenuToggleSize,
00962     MenuToggleSize,
00963     MenuCascadeSize,
00964     MenuSeparatorSize,
00965     MenuPushrightSize,
00966     MenuCascadeSize
00967 };
00968 
00969 /* Compute Menu */
00970 
00971 #define TOO_TALL_SCROLL_HEIGHT 14
00972 
00973 static void ComputeMenuSize(MenuWidget mw, menu_state *ms)
00974 {
00975     unsigned  left_width, label_width, right_width, height;
00976     unsigned  max_left_width, max_label_width, max_right_width, max_height;
00977     unsigned  screen_h, usable_h, vis_count = 0, item_count = 0;
00978     Boolean   in_menubar = (mw->menu.horizontal && !ms->prev);
00979     menu_item *item, *pushright_item=NULL;
00980 
00981     screen_h = HeightOfScreen(XtScreen(mw));
00982     usable_h = screen_h - (2*mw->menu.shadow_width + 2*TOO_TALL_SCROLL_HEIGHT);
00983 
00984     ms->too_tall = 0;
00985 
00986     max_left_width = max_label_width = max_right_width = max_height = 0;
00987     for (item=ms->menu; item; item=item->next) {
00988        SizeFunctionList[item->type](mw, item, in_menubar, &left_width,
00989                                  &label_width, &right_width, &height);
00990 #       define SET_MAX_VALUE(val) if (val > max_##val) max_##val = val;      
00991        if (in_menubar) {
00992            if (!pushright_item && item->type == MENU_PUSHRIGHT)
00993               pushright_item = item;
00994            if ((item->type == MENU_HELP) && !item->next 
00995               && (mw->core.parent->core.parent->core.width > max_label_width + left_width + label_width + right_width)) {
00996              item->start      = mw->core.parent->core.parent->core.width - (left_width + label_width + right_width) - mw->menu.shadow_width;
00997              item->end        = item->start + left_width + label_width + right_width;
00998              max_label_width  = mw->core.parent->core.parent->core.width;
00999            } else {
01000              item->start      = max_label_width + mw->menu.shadow_width;
01001              max_label_width += left_width + label_width + right_width;
01002              item->end        = max_label_width + mw->menu.shadow_width;
01003            }
01004            SET_MAX_VALUE(height);
01005        } else {
01006            SET_MAX_VALUE(left_width);
01007            SET_MAX_VALUE(label_width);
01008            SET_MAX_VALUE(right_width);
01009            if ((max_height + height) < usable_h) {
01010              vis_count++;
01011            } else if (!ms->too_tall) {
01012              screen_h = max_height + 2*TOO_TALL_SCROLL_HEIGHT;
01013              ms->too_tall = 1;
01014            }
01015            item->start  = max_height + mw->menu.shadow_width;
01016            max_height  += height;
01017            item->end    = max_height + mw->menu.shadow_width;
01018            item_count++;
01019        }
01020 #       undef SET_MAX_VALUE
01021     }
01022 
01023     if (ms->too_tall)
01024       max_height = screen_h;
01025 
01026     if (!max_height && in_menubar) {
01027       /* For menu bar: make it at least as tall as with an item */
01028       max_height = (wx_ASCENT(mw->menu.font, mw->menu.xft_font) 
01029                   + wx_DESCENT(mw->menu.font, mw->menu.xft_font)
01030                   + 2*VMARGIN + 2*mw->menu.shadow_width);
01031     }
01032     ms->w       = max_left_width + max_label_width + max_right_width
01033                  + 2*mw->menu.shadow_width;
01034     if (ms->w < mw->menu.requested_width)
01035       ms->w = mw->menu.requested_width;
01036     ms->h       = max_height + 2*mw->menu.shadow_width;
01037     ms->wLeft   = max_left_width;
01038     ms->wMiddle = max_label_width;
01039     if (in_menubar) {
01040        if (pushright_item)  pushright_item->end = ms->w - pushright_item->end;
01041        ms->wLeft = mw->menu.hmargin + mw->menu.shadow_width;
01042     }
01043 }
01044 
01045 
01046 /******************************************************************************
01047  *
01048  * Display Code
01049  *
01050  *****************************************************************************/
01051 
01052 /* Draw Functions */
01053 
01054 static void DrawTextItem(MenuWidget mw, menu_state *ms, menu_item *item,
01055                       unsigned x, unsigned y)
01056 {
01057     Boolean    in_menubar = (mw->menu.horizontal && !ms->prev);
01058     Dimension  extra_x = 0;
01059     char       *label;
01060     int on, width, height;
01061 
01062     if (in_menubar) {
01063        if (item->type == MENU_TOGGLE || item->type == MENU_RADIO) {
01064            extra_x = mw->menu.indicator_size + ISPACING;
01065        }
01066     }
01067 
01068     on = ((ms->selected==item) && (item->enabled));
01069 
01070     width = (in_menubar? item->end-item->start : ms->w-2*mw->menu.shadow_width);
01071     height = (in_menubar? ms->h-2*mw->menu.shadow_width : item->end-item->start);
01072 
01073     XFillRectangle(XtDisplay(mw), ms->win, 
01074                  (on ? mw->menu.highlight_GC : mw->menu.erase_GC), 
01075                  x, y, width, height);
01076 
01077     if ((label=ResourcedText(mw, item, SUBRESOURCE_LABEL))) {
01078       XfwfDrawString(XtDisplay(mw), ms->win,
01079                    (wxEXT_FONT(mw->menu.xft_font)
01080                     ? (on
01081                       ? mw->menu.highlight_GC
01082                       : mw->menu.erase_GC)
01083                     : ((item->enabled || item->type==MENU_TEXT) 
01084                       ? (on
01085                          ? mw->menu.erase_GC 
01086                          : mw->menu.normal_GC)
01087                       : mw->menu.inactive_GC)),
01088                    x+ms->wLeft+extra_x,
01089                    y+mw->menu.shadow_width+VMARGIN+wx_ASCENT(mw->menu.font,
01090                                                         mw->menu.xft_font),
01091                    label, strlen(label), NULL,
01092                    mw->menu.font, wxEXT_FONT(mw->menu.xft_font), 
01093                    (on ? -1 : (item->enabled || item->type==MENU_TEXT)), 
01094                    1, NULL, 1);
01095     }
01096 
01097     if (wxUseMenuHiliteBorder()) {
01098       if (item->enabled && item->type!=MENU_TEXT)
01099        Xaw3dDrawRectangle(
01100            XtDisplay((Widget)mw), ms->win,
01101            (on
01102             ? mw->menu.highlight_top_GC
01103             : mw->menu.top_shadow_GC),
01104            mw->menu.bot_shadow_GC,
01105            (on
01106             ? mw->menu.highlight_GC
01107             : mw->menu.erase_GC),
01108            mw->menu.indicator_GC,
01109            x,
01110            y,
01111            width,
01112            height,
01113            mw->menu.shadow_width,
01114            (ms->selected==item) ? XAW3D_OUT_HARD : XAW3D_BACKGROUND);
01115     }
01116 }
01117 
01118 static void DrawButtonItem(MenuWidget mw, menu_state *ms, menu_item *item,
01119                         unsigned x, unsigned y)
01120 {
01121     char *key;
01122 
01123     DrawTextItem(mw, ms, item, x, y);
01124     if ((!mw->menu.horizontal || ms->prev)
01125        && (key=ResourcedText(mw, item, SUBRESOURCE_KEY))) {
01126       int on;
01127       on = ((ms->selected==item) && (item->enabled));
01128       XfwfDrawString(XtDisplay(mw), ms->win,
01129                    (wxEXT_FONT(mw->menu.xft_font)
01130                     ? (on
01131                       ? mw->menu.highlight_GC 
01132                       : mw->menu.erase_GC)
01133                     : (item->enabled 
01134                       ? (on
01135                          ? mw->menu.erase_GC 
01136                          : mw->menu.normal_GC)
01137                       : mw->menu.inactive_GC)),
01138                    x+ms->wLeft+ms->wMiddle+(3 * ISPACING),
01139                    y+mw->menu.shadow_width+VMARGIN+wx_ASCENT(mw->menu.font,
01140                                                         mw->menu.xft_font),
01141                    key, strlen(key), NULL, mw->menu.font, 
01142                    wxEXT_FONT(mw->menu.xft_font),
01143                    (on ? -1 : item->enabled), 1, NULL, 1);
01144     }
01145 }
01146 
01147 static void DrawRadioItem(MenuWidget mw, menu_state *ms, menu_item *item,
01148                        unsigned x, unsigned y)
01149 {
01150     DrawButtonItem(mw, ms, item, x, y);
01151     Xaw3dDrawRadio(XtDisplay((Widget)mw), ms->win,
01152                  mw->menu.top_shadow_GC,
01153                  mw->menu.bot_shadow_GC,
01154                  mw->menu.indicator_GC,
01155                  mw->menu.erase_GC,
01156                  item->enabled ? mw->menu.normal_GC : mw->menu.inactive_GC,
01157                  x+mw->menu.shadow_width+mw->menu.hmargin,
01158                  y+mw->menu.shadow_width+VMARGIN
01159                  +(wx_ASCENT(mw->menu.font,
01160                             mw->menu.xft_font)
01161                    +wx_DESCENT(mw->menu.font,
01162                              mw->menu.xft_font)
01163                    -mw->menu.indicator_size)/2,
01164                  mw->menu.indicator_size, mw->menu.shadow_width, 
01165                  item->set);
01166 }
01167 
01168 static void DrawToggleItem(MenuWidget mw, menu_state *ms, menu_item *item,
01169                         unsigned x, unsigned y)
01170 {
01171     DrawButtonItem(mw, ms, item, x, y);
01172     if (item->set) {
01173       Display *dpy = XtDisplay((Widget)mw);
01174       Window win = ms->win;
01175       GC gc;
01176       int h, h2, h4, h34;
01177 
01178       x += mw->menu.shadow_width + mw->menu.hmargin;
01179       y += (mw->menu.shadow_width + VMARGIN
01180            + (wx_ASCENT(mw->menu.font,
01181                       mw->menu.xft_font)
01182               + wx_DESCENT(mw->menu.font,
01183                         mw->menu.xft_font)
01184               - mw->menu.indicator_size)/2) + 1;
01185       h = mw->menu.indicator_size - 2;
01186       h2 = h / 2;
01187       h4 = h / 4;
01188       h34 = h - h4;
01189 
01190       gc = (item->enabled 
01191            ? ((ms->selected==item)
01192               ? mw->menu.erase_GC 
01193               : mw->menu.normal_GC)
01194            : mw->menu.inactive_GC);
01195 
01196       XDrawLine(dpy, win, gc, x + h4, y + h34, x + h2, y + h);
01197       XDrawLine(dpy, win, gc, x + h2, y + h, x + h, y);
01198 
01199       x++;
01200 
01201       XDrawLine(dpy, win, gc, x + h4, y + h34, x + h2, y + h);
01202       XDrawLine(dpy, win, gc, x + h2, y + h, x + h, y);
01203     }
01204 }
01205 
01206 static void DrawCascadeItem(MenuWidget mw, menu_state *ms, menu_item *item,
01207                          unsigned x, unsigned y)
01208 {
01209     DrawTextItem(mw, ms, item, x, y);
01210     if (!mw->menu.horizontal || ms->prev) {
01211       int on, size;
01212       on = item->enabled && ms->selected==item;
01213       size = mw->menu.indicator_size;
01214       if (size & 0x1) size--;
01215       Xaw3dDrawArrow(XtDisplay((Widget)mw), ms->win,
01216                    mw->menu.top_shadow_GC,
01217                    mw->menu.bot_shadow_GC,
01218                    (on ? mw->menu.top_shadow_GC : mw->menu.normal_GC),
01219                    (on ? mw->menu.top_shadow_GC : mw->menu.normal_GC),
01220                    x+ms->w
01221                    -(3*mw->menu.shadow_width
01222                      +mw->menu.hmargin
01223                      +mw->menu.indicator_size),
01224                    y+mw->menu.shadow_width+VMARGIN
01225                    +(wx_ASCENT(mw->menu.font,
01226                              mw->menu.xft_font)
01227                      + wx_DESCENT(mw->menu.font,
01228                                 mw->menu.xft_font)
01229                      - size)/2,
01230                    size, size, 0,
01231                    RIGHT, 0);
01232     }
01233 }
01234 
01235 static void DrawSeparatorItem(MenuWidget mw, menu_state *ms, menu_item *item,
01236                            unsigned x, unsigned y)
01237 {
01238     if (!mw->menu.horizontal ||ms->prev)
01239        Xaw3dDrawLine(
01240            XtDisplay((Widget)mw), ms->win,
01241            mw->menu.top_shadow_GC,
01242            mw->menu.bot_shadow_GC,
01243            mw->menu.normal_GC,
01244            x, y, ms->w, mw->menu.shadow_width, FALSE, XAW3D_ETCHED_IN);
01245 }
01246 
01247 static void DrawPushrightItem(MenuWidget mw, menu_state *ms, menu_item *item,
01248                            unsigned x, unsigned y)
01249 {
01250 }
01251 
01252 typedef void (*DrawFunction)(MenuWidget, menu_state*, menu_item*,
01253                           unsigned, unsigned);
01254 static DrawFunction DrawFunctionList[] = {
01255     DrawTextItem,
01256     DrawButtonItem,
01257     DrawRadioItem,
01258     DrawToggleItem,
01259     DrawCascadeItem,
01260     DrawSeparatorItem,
01261     DrawPushrightItem,
01262     DrawCascadeItem
01263 };
01264 
01265 /* Draw Menu */
01266 
01267 static void DisplayMenu(MenuWidget mw, menu_state *ms)
01268 {
01269     menu_item *item;
01270     unsigned  x, y;
01271     int s, final;
01272     Boolean   in_menubar = (mw->menu.horizontal && !ms->prev);
01273 
01274     x = y = mw->menu.shadow_width;
01275     item = ms->menu;
01276     if (ms->too_tall) {
01277       if (ms->scrolled) {
01278        Xaw3dDrawArrow(XtDisplay((Widget)mw), ms->win,
01279                      mw->menu.top_shadow_GC,
01280                      mw->menu.bot_shadow_GC,
01281                      mw->menu.normal_GC,
01282                      mw->menu.normal_GC,
01283                      x + ((ms->w - TOO_TALL_SCROLL_HEIGHT) / 2),
01284                      y + 2,
01285                      TOO_TALL_SCROLL_HEIGHT - 4, TOO_TALL_SCROLL_HEIGHT - 4,
01286                      0,
01287                      UP,
01288                      0);
01289       }
01290       y += TOO_TALL_SCROLL_HEIGHT;
01291       for (s = ms->scrolled; s--; ) {
01292        if (item) {
01293          y = item->end + ms->delta;
01294          item = item->next;
01295        }
01296       }
01297       final = (ms->h - (TOO_TALL_SCROLL_HEIGHT + mw->menu.shadow_width)) - ms->delta;
01298     } else
01299       final = 35000;
01300     for (; item && (item->end < final); item=item->next) {
01301         if (item->type == MENU_HELP)
01302          x = item->start;
01303        DrawFunctionList[item->type](mw, ms, item, x, y);
01304        if (in_menubar) {
01305            if (item->type == MENU_PUSHRIGHT) {
01306               if (x+item->end <= ms->w)
01307                   x = ms->w - item->end;
01308            } else
01309               x = item->end;
01310        } else {
01311          y = item->end + ms->delta;
01312        }
01313     }
01314 
01315     ms->arrow_start = y;
01316     if (ms->too_tall && item) {
01317       y = ms->h - (TOO_TALL_SCROLL_HEIGHT + mw->menu.shadow_width);
01318       Xaw3dDrawArrow(XtDisplay((Widget)mw), ms->win,
01319                    mw->menu.top_shadow_GC,
01320                    mw->menu.bot_shadow_GC,
01321                    mw->menu.normal_GC,
01322                    mw->menu.normal_GC,
01323                    x + ((ms->w - TOO_TALL_SCROLL_HEIGHT) / 2),
01324                    y + 2,
01325                    TOO_TALL_SCROLL_HEIGHT - 4, TOO_TALL_SCROLL_HEIGHT - 4,
01326                    0,
01327                    DOWN,
01328                    0);
01329       ms->can_go_down = 1;
01330     } else
01331       ms->can_go_down = 0;
01332 
01333     Xaw3dDrawRectangle(
01334        XtDisplay((Widget)mw), ms->win,
01335        mw->menu.top_shadow_GC,
01336        mw->menu.bot_shadow_GC,
01337        mw->menu.erase_GC,
01338        mw->menu.indicator_GC,
01339        0, 0, ms->w, ms->h, 
01340        in_menubar ? 1 : mw->menu.shadow_width,
01341        in_menubar ? XAW3D_OUT : XAW3D_OUT_HARD);
01342 }
01343 
01344 
01345 /******************************************************************************
01346  *
01347  * Highlight and Unhighlight Code
01348  *
01349  *****************************************************************************/
01350 
01351 /* Utilities */
01352 
01353 static void ComputeItemPos(MenuWidget mw, menu_state *ms, menu_item *item, 
01354                         unsigned *x, unsigned *y)
01355 {
01356     if (!ms->prev && mw->menu.horizontal) { /* in menubar ? */
01357        menu_item  *i;
01358        Dimension  pushright = 0;
01359        for (i=ms->menu; i && i!=item; i=i->next)
01360            if (!pushright && i->type==MENU_PUSHRIGHT)
01361               pushright = ms->w - i->end - i->start;
01362        *x = item->start + pushright;
01363        *y = mw->menu.shadow_width;
01364     } else {
01365        *x = mw->menu.shadow_width;
01366        *y = item->start + ms->delta;
01367     }
01368 }
01369 
01370 static void MakeNewMenuWindow(MenuWidget mw, menu_state *prev, menu_item *item,
01371                            unsigned x, unsigned y)
01372 {
01373     int        scr_width  = WidthOfScreen(XtScreen(mw));
01374     int        scr_height = HeightOfScreen(XtScreen(mw));
01375     menu_state *new       = (menu_state*)XtMalloc(sizeof(menu_state));
01376     int        mask;
01377     XSetWindowAttributes xswa;
01378 
01379     if (mw->menu.state->timer) {
01380       FreeTimer(mw->menu.state->timer);
01381       mw->menu.state->timer = 0;
01382     }
01383 
01384     /* Create new menu_state, initialize it and compute menu size */
01385     new->menu      = item->contents;
01386     new->selected  = NULL;
01387     new->prev      = prev;
01388     new->timer     = 0;
01389     mw->menu.state = new;
01390     ComputeMenuSize(mw, new);
01391     new->delta     = (new->too_tall ? TOO_TALL_SCROLL_HEIGHT : 0);
01392     new->scrolled  = 0;
01393     new->scroll_top = new->menu;
01394 
01395     /* position window on screen */
01396     if (mw->menu.horizontal && !prev->prev) { /* item in menubar? */
01397        new->x = prev->x + x;
01398        if (new->x + new->w > scr_width)
01399            new->x = scr_width -  new->w;
01400        new->y = prev->y + prev->h - mw->menu.shadow_width;
01401        if (new->y + new->h > scr_height) /* menu doesn't below menubar -> */
01402            if (new->y > scr_height/2) /* is more place above the menubar ?*/
01403               new->y = prev->y - new->h +mw->menu.shadow_width;
01404     } else {
01405        if (prev->x + prev->w + new->w < scr_width) /* place right of menu? */
01406            new->x = prev->x + prev->w;
01407        else if (prev->x - new->w > 0)              /* place left of menu? */
01408            new->x = prev->x - new->w;
01409        else                                     /* place on screen border*/
01410            new->x = scr_width - new->w;
01411 
01412        new->y = prev->y + y - mw->menu.shadow_width;
01413        if (new->y + new->h > scr_height)
01414            new->y = scr_height - new->h;
01415     }
01416 
01417     /* Create new window */
01418     xswa.save_under        = TRUE;
01419     xswa.override_redirect = TRUE;
01420     xswa.background_pixel  = mw->core.background_pixel;
01421     xswa.border_pixel      = mw->core.background_pixel;
01422     xswa.event_mask        = ExposureMask | ButtonMotionMask | 
01423                             PointerMotionMask | ButtonReleaseMask |
01424                             ButtonPressMask;
01425     xswa.cursor            = mw->menu.cursor;
01426     xswa.colormap          = wx_default_colormap;
01427     mask                   = (CWSaveUnder | CWOverrideRedirect | CWBackPixel
01428                            | CWBorderPixel | CWEventMask | CWCursor
01429                            | CWColormap);
01430 
01431     new->win = XCreateWindow(XtDisplay(mw),
01432                           RootWindowOfScreen(DefaultScreenOfDisplay(XtDisplay(mw))),
01433                           new->x, new->y, new->w, new->h, 0,
01434                           wx_visual_depth, InputOutput, wxAPP_VISUAL,
01435                           mask, &xswa);
01436 }
01437 
01438 static void HighlightItem(MenuWidget mw, menu_state *ms, menu_item *item)
01439 {
01440     unsigned x, y;
01441 
01442     if (!item) /* nothing to highlight */
01443        return;
01444     ms->selected = item;
01445     ComputeItemPos(mw, ms, item, &x, &y);
01446     DrawFunctionList[item->type](mw, ms, item, x, y);
01447     if (((item->type == MENU_CASCADE) || (item->type == MENU_HELP)) && item->enabled) {
01448        MakeNewMenuWindow(mw, ms, item, x, y);
01449        XClearWindow(XtDisplay(mw), mw->menu.state->win);
01450        XMapRaised(XtDisplay(mw), mw->menu.state->win);
01451        DisplayMenu(mw, mw->menu.state);
01452     }
01453 }
01454 
01455 static void UnhighlightItem(MenuWidget mw, menu_state *ms, menu_item *item)
01456 {
01457     unsigned   x, y;
01458     menu_state *state, *last;
01459 
01460     if (!item) /* nothing to unhighlight */
01461        return;
01462     ms->selected = NULL;
01463     ComputeItemPos(mw, ms, item, &x, &y);
01464     DrawFunctionList[item->type](mw, ms, item, x, y);
01465     if (((item->type == MENU_CASCADE) || (item->type == MENU_HELP)) && item->enabled) {
01466        state = mw->menu.state;
01467        while (state != ms) {
01468            XDestroyWindow(XtDisplay(mw), state->win);
01469            last  = state;
01470            state = state->prev;
01471            FreeTimer(last->timer);
01472            XtFree((char*)last);
01473        }
01474        mw->menu.state = ms;
01475     }
01476 }
01477 
01478 static void timer_callback(XtPointer client_data, XtIntervalId * timer)
01479 {
01480   MenuWidget mw = (MenuWidget)client_data;
01481   XMotionEvent ev;
01482 
01483   XQueryPointer(XtDisplay(mw), XtWindow(mw),
01484               &ev.root, &ev.subwindow,
01485               &ev.x_root, &ev.y_root,
01486               &ev.x, &ev.y, &ev.state);
01487 
01488   HandleMotionEvent(mw, &ev, 0);
01489 }
01490 
01491 static int HandleMotionEvent(MenuWidget mw, XMotionEvent *ev, int is_click)
01492 {
01493     menu_state *ms = NULL;
01494     menu_item  *item = NULL;
01495     Dimension  pushright = 0;
01496     Boolean    foundone = 0;
01497     int        scroll = 0, in_extra_region = 0;
01498 
01499     if (mw->menu.state->timer) {
01500       FreeTimer(mw->menu.state->timer);
01501       mw->menu.state->timer = 0;
01502     }
01503 
01504     /* find menu_state belonging to event */
01505     if (ev) {
01506       for (ms = mw->menu.state; ms; ms = ms->prev) {
01507        if (ms->x <= ev->x_root && ev->x_root <= ms->x + ms->w
01508            &&  ms->y <= ev->y_root && ev->y_root <= ms->y + ms->h) {
01509          if (ms->too_tall) {
01510            if (ev->y_root <= (ms->y + TOO_TALL_SCROLL_HEIGHT + mw->menu.shadow_width))
01511              scroll = -1;
01512            else if (ev->y_root >= ms->arrow_start)
01513              scroll = 1;
01514          }
01515 
01516          if (!scroll) {
01517            foundone = 1;
01518            /* find menu_item belonging to event */
01519            for (item = ms->menu; item; item = item->next)
01520              if (mw->menu.horizontal && !ms->prev) {
01521               if (!pushright && item->type == MENU_PUSHRIGHT)
01522                 pushright = ms->w - item->end - item->start;
01523               else if (ms->x + pushright + item->start <= ev->x_root
01524                       && ev->x_root < ms->x + pushright + item->end)
01525                 break;
01526              } else {
01527               if (ms->y + item->start + ms->delta <= ev->y_root
01528                   &&  ev->y_root < ms->y + item->end + ms->delta)
01529                 break;
01530              }
01531          }
01532          break;
01533        }
01534       }      
01535     }
01536 
01537     {
01538       menu_state *tms = NULL;
01539       for (tms = mw->menu.state; tms && tms->prev; tms = tms->prev) {
01540       }
01541       if (tms && ev) {
01542        if (!(tms->x <= ev->x_root 
01543              && ev->x_root <= tms->x + tms->w
01544              && tms->y <= ev->y_root 
01545              && ev->y_root <= tms->y + tms->h)) {
01546          if (tms->x - mw->menu.extra_left <= ev->x_root 
01547              && ev->x_root <= tms->x + tms->w + mw->menu.extra_right
01548              && tms->y - mw->menu.extra_top <= ev->y_root 
01549              && ev->y_root <= tms->y + tms->h + mw->menu.extra_bottom) {
01550            in_extra_region = 1;
01551          }
01552        }
01553       }
01554     }
01555     
01556     if (!foundone)
01557       mw->menu.moused_out = !in_extra_region;
01558 
01559     if (!mw->menu.forPopup 
01560         && (is_click && ms && (item == ms->selected))) { /* pointer on the same item and a click */
01561       return 0;
01562     }
01563 
01564     if (!item) { /* if pointer not on menu_item unhighlight last selected */
01565       UnhighlightItem(mw, mw->menu.state, mw->menu.state->selected);
01566       if (scroll) {
01567        if (scroll < 0) {
01568          if (ms->scrolled) {
01569            ms->scrolled -= 1;
01570            ms->scroll_top = ms->scroll_top->prev;
01571            ms->delta += (ms->scroll_top->end - ms->scroll_top->start);
01572          } else
01573            scroll = 0;
01574        } else {
01575          if (ms->can_go_down) {
01576            ms->scrolled += 1;
01577            ms->delta -= (ms->scroll_top->end - ms->scroll_top->start);
01578            ms->scroll_top = ms->scroll_top->next;
01579          }  else
01580            scroll = 0;
01581        }
01582 
01583        if (scroll) {
01584          XFillRectangle(XtDisplay((Widget)mw), ms->win, 
01585                       mw->menu.erase_GC,
01586                       0, 0, ms->w, ms->h);
01587          DisplayMenu(mw, mw->menu.state);
01588 
01589          ms->timer = wxAppAddTimeOut(XtWidgetToApplicationContext((Widget)mw),
01590                                   100, timer_callback, mw, (Widget)mw);
01591        }
01592       }
01593       return in_extra_region;
01594     }
01595 
01596     if (item == ms->selected) { /* pointer on the same item (and not a click) */
01597       return 1;
01598     }
01599 
01600     /* unhighlight old item on same level (ms!) and highlight new item */
01601     UnhighlightItem(mw, ms, ms->selected);
01602     HighlightItem(mw, ms, item);
01603     if (item->enabled 
01604        && item->type != MENU_TEXT
01605        && item->type != MENU_SEPARATOR
01606        && item->type != MENU_PUSHRIGHT)
01607        XtCallCallbackList((Widget)mw, mw->menu.on_new_item, (XtPointer)item);
01608         /* XtCallCallbackList((Widget)mw, mw->menu.on_new_item,
01609           (XtPointer)ResourcedText(mw, item, SUBRESOURCE_HELP)); */
01610 
01611     return 1;
01612 }
01613 
01614 static void MoveSelection(MenuWidget mw, int direction)
01615 {
01616   menu_state *ms = mw->menu.state;
01617 
01618   if (!ms)
01619     return;
01620 
01621   if (!ms->selected && ms->prev && ms->prev->prev) {
01622     /* Submenu popped up, nothing selected. */
01623     ms = ms->prev;
01624   }
01625 
01626   if (ms->selected) {
01627     menu_item  *item = ms->selected;
01628 
01629     do {
01630       if (direction > 0)
01631        item = item->next;
01632       else
01633        item = item->prev;
01634     } while (item && ((item->type == MENU_SEPARATOR)
01635                     || !item->enabled));
01636 
01637     if (!item) {
01638       /* Wraparound: highlight first/last: */
01639       if (direction > 0)
01640        item = ms->menu;
01641       else {
01642        item = ms->menu;
01643        while (item->next)
01644          item = item->next;
01645       }
01646 
01647       while (item && ((item->type == MENU_SEPARATOR)
01648                     || !item->enabled)) {
01649        if (direction > 0)
01650          item = item->next;
01651        else
01652          item = item->prev;
01653       }
01654     }
01655 
01656     if (item) {
01657       UnhighlightItem(mw, ms, ms->selected);
01658       HighlightItem(mw, ms, item);
01659     }
01660   } else if (direction > 0) {
01661     menu_item  *item = ms->menu;
01662 
01663     while (item && ((item->type == MENU_SEPARATOR)
01664                   || !item->enabled))
01665       item = item->next;
01666 
01667     if (item)
01668       HighlightItem(mw, ms, item);
01669   } else {
01670     menu_item  *item = ms->menu;
01671     if (item) {
01672       while (item->next)
01673        item = item->next;
01674       while (item && ((item->type == MENU_SEPARATOR)
01675                     || !item->enabled))
01676        item = item->prev;
01677       
01678       if (item)
01679        HighlightItem(mw, ms, item);
01680     }
01681   }
01682 }
01683 
01684 
01685 /******************************************************************************
01686  *
01687  * Special Code to Popup a Menu
01688  *
01689  *****************************************************************************/
01690 
01691 void Xaw3dPopupMenu(MenuWidget mw, Widget calling_widget)
01692 {
01693     XButtonPressedEvent ev;
01694 
01695     /* get position of pointer in calling widget */
01696     ev.type = ButtonPress;
01697     ev.serial = 0;
01698     ev.send_event = 0;
01699     ev.display = XtDisplay(calling_widget);
01700     ev.window = XtWindow(calling_widget);
01701     ev.time = CurrentTime;
01702     ev.button = 0;
01703     XQueryPointer(ev.display, ev.window, &ev.root,
01704                   &ev.subwindow, &ev.x_root, &ev.y_root,
01705                   &ev.x, &ev.y, &ev.state);
01706     Xaw3dPopupMenuAtPos(mw, ev.x_root, ev.y_root);
01707 }
01708 
01709 void Xaw3dPopupMenuAtPos(MenuWidget mw, int x, int y)
01710 {
01711     Screen  *scr         = XtScreen(mw);
01712     Widget  popup_shell  = XtParent(mw);
01713     int     border_width = popup_shell->core.border_width;
01714     int     w, h;
01715     XMotionEvent ev;
01716 
01717     /* compute size and position of popup menu */
01718     mw->menu.popped_up = TRUE;
01719     mw->menu.horizontal = FALSE;
01720     ComputeMenuSize(mw, mw->menu.state);
01721     mw->menu.state->delta     = (mw->menu.state->too_tall ? TOO_TALL_SCROLL_HEIGHT : 0);
01722     mw->menu.state->scrolled  = 0;
01723     mw->menu.state->scroll_top = mw->menu.state->menu;
01724     w = mw->menu.state->w;
01725     h = mw->menu.state->h;
01726     if (x + w > WidthOfScreen(scr))
01727        x = WidthOfScreen(scr) - w - 2*border_width;
01728     if (y + h > HeightOfScreen(scr))
01729        y = HeightOfScreen(scr) - h - 2*border_width;
01730     x = (x > border_width ? x - border_width : border_width);
01731     y = (y > border_width ? y - border_width : border_width);
01732     XtConfigureWidget(popup_shell, x, y, w, h, border_width);
01733     /* popup, display and handle menu */
01734     XtPopup(popup_shell, XtGrabNone);
01735     DisplayMenu(mw, mw->menu.state);
01736     mw->menu.state->x = x+border_width;
01737     mw->menu.state->y = y+border_width;
01738     /* init first motion event */
01739     ev.x_root = x; ev.y_root = y;
01740     HandleMotionEvent(mw, (XMotionEvent*)&ev, 0);
01741 }
01742 
01743 int xwMenuIsPoppedUp(Widget w)
01744 {
01745   return ((MenuWidget)w)->menu.grabbed;
01746 }