Skip to content

AFNI/NIfTI Server

Sections
Personal tools
You are here: Home » AFNI » Documentation

Doxygen Source Code Documentation


Main Page   Alphabetical List   Data Structures   File List   Data Fields   Globals   Search  

glut_menu.c

Go to the documentation of this file.
00001 
00002 /* Copyright (c) Mark J. Kilgard, 1994. */
00003 
00004 /* This program is freely distributable without licensing fees
00005    and is provided without guarantee or warrantee expressed or
00006    implied. This program is -not- in the public domain. */
00007 
00008 #include <stdlib.h>
00009 #include <string.h>
00010 #include <stdio.h>
00011 #include <errno.h>
00012 #include <unistd.h>
00013 #include <assert.h>
00014 
00015 #include <X11/Xlib.h>
00016 #include <X11/cursorfont.h>  /* for XC_arrow */
00017 
00018 #include <GL/glut.h>
00019 #include "glutint.h"
00020 #include "layerutil.h"
00021 
00022 GLUTmenu *__glutCurrentMenu = NULL;
00023 void (*__glutMenuStatusFunc) (int, int, int);
00024 GLUTmenu *__glutMappedMenu;
00025 GLUTwindow *__glutMenuWindow;
00026 GLUTmenuItem *__glutItemSelected;
00027 
00028 static GLUTmenu **menuList = NULL;
00029 static int menuListSize = 0;
00030 static XFontStruct *menuFont = NULL;
00031 static Cursor menuCursor;
00032 static Colormap menuColormap;
00033 static Visual *menuVisual;
00034 static int menuDepth;
00035 static int fontHeight;
00036 static GC blackGC, grayGC, whiteGC;
00037 static unsigned long menuBlack, menuWhite, menuGray;
00038 static unsigned long useSaveUnders;
00039 
00040 /* A replacement for XAllocColor (originally by Brian Paul).
00041    This  function should never fail to allocate a color.  When
00042    XAllocColor fails, we return the nearest matching color.  If
00043    we have to allocate many colors this function isn't a great
00044    solution; the XQueryColors() could be done just once.  */
00045 static void
00046 noFaultXAllocColor(Display * dpy, Colormap cmap, int cmapSize,
00047   XColor * color)
00048 {
00049   XColor *ctable, subColor;
00050   int i, bestmatch;
00051   double mindist;       /* 3*2^16^2 exceeds long int precision. 
00052                          */
00053 
00054   for (;;) {
00055     /* First try just using XAllocColor. */
00056     if (XAllocColor(dpy, cmap, color))
00057       return;
00058 
00059     /* Retrieve color table entries. */
00060     /* XXX alloca canidate. */
00061     ctable = (XColor *) malloc(cmapSize * sizeof(XColor));
00062     for (i = 0; i < cmapSize; i++)
00063       ctable[i].pixel = i;
00064     XQueryColors(dpy, cmap, ctable, cmapSize);
00065 
00066     /* Find best match. */
00067     bestmatch = -1;
00068     mindist = 0.0;
00069     for (i = 0; i < cmapSize; i++) {
00070       double dr = (double) color->red - (double) ctable[i].red;
00071       double dg = (double) color->green - (double) ctable[i].green;
00072       double db = (double) color->blue - (double) ctable[i].blue;
00073       double dist = dr * dr + dg * dg + db * db;
00074       if (bestmatch < 0 || dist < mindist) {
00075         bestmatch = i;
00076         mindist = dist;
00077       }
00078     }
00079 
00080     /* Return result. */
00081     subColor.red = ctable[bestmatch].red;
00082     subColor.green = ctable[bestmatch].green;
00083     subColor.blue = ctable[bestmatch].blue;
00084     free(ctable);
00085     if (XAllocColor(dpy, cmap, &subColor)) {
00086       *color = subColor;
00087       return;
00088     }
00089     /* Extremely unlikely, but possibly color was deallocated
00090        and reallocated by someone else before we could
00091        XAllocColor the color cell we located.  If so, loop
00092        again... */
00093   }
00094 }
00095 
00096 static void
00097 menuVisualSetup(void)
00098 {
00099   XLayerVisualInfo template, *visual, *overlayVisuals;
00100   XColor color;
00101   Status status;
00102   Bool presumablyMesa;
00103   int layer, nVisuals, i, dummy;
00104 
00105   for (layer = 3; layer > 0; layer--) {
00106     template.layer = layer;
00107     template.vinfo.screen = __glutScreen;
00108     overlayVisuals = __glutXGetLayerVisualInfo(__glutDisplay,
00109       VisualScreenMask | VisualLayerMask, &template, &nVisuals);
00110     if (overlayVisuals) {
00111       for (i = 0; i < nVisuals; i++) {
00112         visual = &overlayVisuals[i];
00113         if (visual->vinfo.colormap_size >= 3) {
00114           menuColormap = XCreateColormap(__glutDisplay, __glutRoot,
00115             visual->vinfo.visual, AllocNone);
00116           /* Allocate overlay colormap cells in defined order:
00117              gray, black, white to match the IRIS GL allocation
00118              scheme.  Increases likelihood of less overlay
00119              colormap flashing. */
00120           /* XXX nice if these 3 AllocColor's could be done in
00121              one protocol round-trip */
00122           color.red = color.green = color.blue = 0xaa00;
00123           status = XAllocColor(__glutDisplay,
00124             menuColormap, &color);
00125           if (!status) {
00126             XFreeColormap(__glutDisplay, menuColormap);
00127             continue;
00128           }
00129           menuGray = color.pixel;
00130           color.red = color.green = color.blue = 0x0000;
00131           status = XAllocColor(__glutDisplay,
00132             menuColormap, &color);
00133           if (!status) {
00134             XFreeColormap(__glutDisplay, menuColormap);
00135             continue;
00136           }
00137           menuBlack = color.pixel;
00138           color.red = color.green = color.blue = 0xffff;
00139           status = XAllocColor(__glutDisplay,
00140             menuColormap, &color);
00141           if (!status) {
00142             XFreeColormap(__glutDisplay, menuColormap);
00143             continue;
00144           }
00145           menuWhite = color.pixel;
00146           menuVisual = visual->vinfo.visual;
00147           menuDepth = visual->vinfo.depth;
00148           /* If using overlays, avoid requesting "save unders". 
00149            */
00150           useSaveUnders = 0;
00151           XFree(overlayVisuals);
00152           return;
00153         }
00154       }
00155       XFree(overlayVisuals);
00156     }
00157   }
00158   /* settle for default visual */
00159   menuVisual = DefaultVisual(__glutDisplay, __glutScreen);
00160   menuDepth = DefaultDepth(__glutDisplay, __glutScreen);
00161   menuColormap = DefaultColormap(__glutDisplay, __glutScreen);
00162   menuBlack = BlackPixel(__glutDisplay, __glutScreen);
00163   menuWhite = WhitePixel(__glutDisplay, __glutScreen);
00164   color.red = color.green = color.blue = 0xaa00;
00165   noFaultXAllocColor(__glutDisplay, menuColormap,
00166     menuVisual->map_entries, &color);
00167   menuGray = color.pixel;
00168 
00169   /* When no overlays are supported, we would like to use X
00170      "save unders" to avoid exposes to windows obscured by
00171      pop-up menus.  However, OpenGL's direct rendering support
00172      means OpenGL interacts poorly with X backing store and
00173      save unders.  X servers do not (in implementation
00174      practice) redirect OpenGL rendering destined to obscured
00175      window regions into backing store.
00176 
00177      Implementation solutions exist for this problem, but they
00178      are expensive and high-end OpenGL implementations
00179      typically provide fast rendering and/or overlays to
00180      obviate the problem associated of user interfaces (pop-up
00181      menus) forcing redraws of complex normal plane scenes.
00182      (See support for overlays pop-up menus above.)
00183 
00184      Mesa 3D, however, does not support direct rendering.
00185      Overlays are often unavailable to Mesa, and Mesa is also
00186      relatively slow.  For these reasons, Mesa-rendering GLUT
00187      programs can and should use X save unders.
00188 
00189      Look for the GLX extension.  If _not_ supported, we are
00190      presumably using Mesa so enable save unders. */
00191 
00192   presumablyMesa = !XQueryExtension(__glutDisplay, "GLX",
00193     &dummy, &dummy, &dummy);
00194 
00195   if (presumablyMesa)
00196     useSaveUnders = CWSaveUnder;
00197   else
00198     useSaveUnders = 0;
00199 }
00200 
00201 static void
00202 menuSetup(void)
00203 {
00204   if (menuFont) {
00205     /* menuFont overload to indicate menu initalization */
00206     return;
00207   }
00208   menuFont = XLoadQueryFont(__glutDisplay,
00209     "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
00210   if (!menuFont) {
00211     /* try back up font */
00212     menuFont = XLoadQueryFont(__glutDisplay, "fixed");
00213   }
00214   if (!menuFont) {
00215     __glutFatalError("could not load font.");
00216   }
00217   menuVisualSetup();
00218   fontHeight = menuFont->ascent + menuFont->descent;
00219   menuCursor = XCreateFontCursor(__glutDisplay, XC_arrow);
00220 }
00221 
00222 static void
00223 menuGraphicsContextSetup(Window win)
00224 {
00225   XGCValues gcvals;
00226 
00227   if (blackGC != None)
00228     return;
00229   gcvals.font = menuFont->fid;
00230   gcvals.foreground = menuBlack;
00231   blackGC = XCreateGC(__glutDisplay, win,
00232     GCFont | GCForeground, &gcvals);
00233   gcvals.foreground = menuGray;
00234   grayGC = XCreateGC(__glutDisplay, win, GCForeground, &gcvals);
00235   gcvals.foreground = menuWhite;
00236   whiteGC = XCreateGC(__glutDisplay, win, GCForeground, &gcvals);
00237 }
00238 
00239 /* OBSOLETE - use glutMenuStatusFunc instead. */
00240 void
00241 glutMenuStateFunc(GLUTmenuStateCB menuStateFunc)
00242 {
00243   __glutMenuStatusFunc = (GLUTmenuStatusCB) menuStateFunc;
00244 }
00245 
00246 void
00247 glutMenuStatusFunc(GLUTmenuStatusCB menuStatusFunc)
00248 {
00249   __glutMenuStatusFunc = menuStatusFunc;
00250 }
00251 
00252 void
00253 __glutSetMenu(GLUTmenu * menu)
00254 {
00255   __glutCurrentMenu = menu;
00256 }
00257 
00258 static void
00259 unmapMenu(GLUTmenu * menu)
00260 {
00261   if (menu->cascade) {
00262     unmapMenu(menu->cascade);
00263     menu->cascade = NULL;
00264   }
00265   menu->anchor = NULL;
00266   menu->highlighted = NULL;
00267   XUnmapWindow(__glutDisplay, menu->win);
00268 }
00269 
00270 void
00271 __glutFinishMenu(Window win, int x, int y)
00272 {
00273   Window dummy;
00274   int rc;
00275 
00276   unmapMenu(__glutMappedMenu);
00277   XUngrabPointer(__glutDisplay, CurrentTime);
00278 
00279   /* This XFlush is needed to to make sure the pointer is
00280      really ungrabbed when the application's menu callback is
00281      called. Otherwise, a deadlock might happen because the
00282      application may try to read from an terminal window, but
00283      yet the ungrab hasn't really happened since it hasn't been
00284      flushed out. */
00285   XFlush(__glutDisplay);
00286 
00287   if (__glutMenuStatusFunc) {
00288     if (win != __glutMenuWindow->win) {
00289       /* The button release may have occurred in a window other
00290          than the window requesting the pop-up menu (for
00291          example, one of the submenu windows).  In this case, we
00292          need to translate the coordinates into the coordinate
00293          system of the window associated with the window. */
00294       rc = XTranslateCoordinates(__glutDisplay, win, __glutMenuWindow->win,
00295         x, y, &x, &y, &dummy);
00296       assert(rc != False);  /* Will always be on same screen. */
00297     }
00298     __glutSetWindow(__glutMenuWindow);
00299     __glutSetMenu(__glutMappedMenu);
00300 
00301     /* Setting __glutMappedMenu to NULL permits operations that
00302        change menus or destroy the menu window again. */
00303     __glutMappedMenu = NULL;
00304 
00305     __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
00306   }
00307   /* Setting __glutMappedMenu to NULL permits operations that
00308      change menus or destroy the menu window again. */
00309   __glutMappedMenu = NULL;
00310 
00311   /* If an item is selected and it is not a submenu trigger,
00312      generate menu callback. */
00313   if (__glutItemSelected && !__glutItemSelected->isTrigger) {
00314     __glutSetWindow(__glutMenuWindow);
00315     /* When menu callback is triggered, current menu should be
00316        set to the callback menu. */
00317     __glutSetMenu(__glutItemSelected->menu);
00318     __glutItemSelected->menu->select(
00319       __glutItemSelected->value);
00320   }
00321   __glutMenuWindow = NULL;
00322 }
00323 
00324 #define MENU_BORDER 1
00325 #define MENU_GAP 2
00326 #define MENU_ARROW_GAP 6
00327 #define MENU_ARROW_WIDTH 8
00328 
00329 static void
00330 mapMenu(GLUTmenu * menu, int x, int y)
00331 {
00332   XWindowChanges changes;
00333   unsigned int mask;
00334   int subMenuExtension, num;
00335 
00336   /* If there are submenus, we need to provide extra space for
00337      the submenu pull arrow.  */
00338   if (menu->submenus > 0) {
00339     subMenuExtension = MENU_ARROW_GAP + MENU_ARROW_WIDTH;
00340   } else {
00341     subMenuExtension = 0;
00342   }
00343 
00344   changes.stack_mode = Above;
00345   mask = CWStackMode | CWX | CWY;
00346   /* If the menu isn't managed (ie, validated so all the
00347      InputOnly subwindows are the right size), do so.  */
00348   if (!menu->managed) {
00349     GLUTmenuItem *item;
00350 
00351     item = menu->list;
00352     num = menu->num;
00353     while (item) {
00354       XWindowChanges itemupdate;
00355 
00356       itemupdate.y = (num - 1) * fontHeight + MENU_GAP;
00357       itemupdate.width = menu->pixwidth;
00358       itemupdate.width += subMenuExtension;
00359       XConfigureWindow(__glutDisplay, item->win,
00360         CWWidth | CWY, &itemupdate);
00361       item = item->next;
00362       num--;
00363     }
00364     menu->pixheight = MENU_GAP +
00365       fontHeight * menu->num + MENU_GAP;
00366     changes.height = menu->pixheight;
00367     changes.width = MENU_GAP +
00368       menu->pixwidth + subMenuExtension + MENU_GAP;
00369     mask |= CWWidth | CWHeight;
00370     menu->managed = True;
00371   }
00372   /* make sure menu appears fully on screen */
00373   if (y + menu->pixheight >= __glutScreenHeight) {
00374     changes.y = __glutScreenHeight - menu->pixheight;
00375   } else {
00376     changes.y = y;
00377   }
00378   if (x + menu->pixwidth + subMenuExtension >=
00379     __glutScreenWidth) {
00380     changes.x = __glutScreenWidth -
00381       menu->pixwidth + subMenuExtension;
00382   } else {
00383     changes.x = x;
00384   }
00385 
00386   /* Rember where the menu is placed so submenus can be
00387      properly placed relative to it. */
00388   menu->x = changes.x;
00389   menu->y = changes.y;
00390 
00391   XConfigureWindow(__glutDisplay, menu->win, mask, &changes);
00392   XInstallColormap(__glutDisplay, menuColormap);
00393   /* XXX The XRaiseWindow below should not be necessary because
00394      the XConfigureWindow requests an Above stack mode (same as
00395      XRaiseWindow), but some Sun users complained this was still
00396      necessary.  Probably some window manager or X server bug on
00397      these machines?? */
00398   XRaiseWindow(__glutDisplay, menu->win);
00399   XMapWindow(__glutDisplay, menu->win);
00400 }
00401 
00402 void
00403 __glutStartMenu(GLUTmenu * menu, GLUTwindow * window,
00404   int x, int y, int x_win, int y_win)
00405 {
00406   int grab;
00407 
00408   assert(__glutMappedMenu == NULL);
00409   grab = XGrabPointer(__glutDisplay, __glutRoot, True,
00410     ButtonPressMask | ButtonReleaseMask,
00411     GrabModeAsync, GrabModeAsync,
00412     __glutRoot, menuCursor, CurrentTime);
00413   if (grab != GrabSuccess) {
00414     /* Somebody else has pointer grabbed, ignore menu
00415        activation. */
00416     return;
00417   }
00418   __glutMappedMenu = menu;
00419   __glutMenuWindow = window;
00420   __glutItemSelected = NULL;
00421   if (__glutMenuStatusFunc) {
00422     __glutSetMenu(menu);
00423     __glutSetWindow(window);
00424     __glutMenuStatusFunc(GLUT_MENU_IN_USE, x_win, y_win);
00425   }
00426   mapMenu(menu, x, y);
00427 }
00428 
00429 static void
00430 paintSubMenuArrow(Window win, int x, int y)
00431 {
00432   XPoint p[5];
00433 
00434   p[0].x = p[4].x = x;
00435   p[0].y = p[4].y = y - menuFont->ascent + 1;
00436   p[1].x = p[0].x + MENU_ARROW_WIDTH - 1;
00437   p[1].y = p[0].y + (menuFont->ascent / 2) - 1;
00438   p[2].x = p[1].x;
00439   p[2].y = p[1].y + 1;
00440   p[3].x = p[0].x;
00441   p[3].y = p[0].y + menuFont->ascent - 2;
00442   XFillPolygon(__glutDisplay, win,
00443     whiteGC, p, 4, Convex, CoordModeOrigin);
00444   XDrawLines(__glutDisplay, win, blackGC, p, 5, CoordModeOrigin);
00445 }
00446 
00447 static void
00448 paintMenuItem(GLUTmenuItem * item, int num)
00449 {
00450   Window win = item->menu->win;
00451   GC gc;
00452   int y;
00453   int subMenuExtension;
00454 
00455   if (item->menu->submenus > 0) {
00456     subMenuExtension = MENU_ARROW_GAP + MENU_ARROW_WIDTH;
00457   } else {
00458     subMenuExtension = 0;
00459   }
00460   if (item->menu->highlighted == item) {
00461     gc = whiteGC;
00462   } else {
00463     gc = grayGC;
00464   }
00465   y = MENU_GAP + fontHeight * num - menuFont->descent;
00466   XFillRectangle(__glutDisplay, win, gc,
00467     MENU_GAP, y - fontHeight + menuFont->descent,
00468     item->menu->pixwidth + subMenuExtension, fontHeight);
00469   XDrawString(__glutDisplay, win, blackGC,
00470     MENU_GAP, y, item->label, item->len);
00471   if (item->isTrigger) {
00472     paintSubMenuArrow(win,
00473       item->menu->pixwidth + MENU_ARROW_GAP + 1, y);
00474   }
00475 }
00476 
00477 void
00478 __glutPaintMenu(GLUTmenu * menu)
00479 {
00480   GLUTmenuItem *item;
00481   int i = menu->num;
00482   int y = MENU_GAP + fontHeight * i - menuFont->descent;
00483 
00484   item = menu->list;
00485   while (item) {
00486     if (item->menu->highlighted == item) {
00487       paintMenuItem(item, i);
00488     } else {
00489       /* quick render of the menu item; assume background
00490          already cleared to gray */
00491       XDrawString(__glutDisplay, menu->win, blackGC,
00492         2, y, item->label, item->len);
00493       if (item->isTrigger) {
00494         paintSubMenuArrow(menu->win,
00495           menu->pixwidth + MENU_ARROW_GAP + 1, y);
00496       }
00497     }
00498     i--;
00499     y -= fontHeight;
00500     item = item->next;
00501   }
00502 }
00503 
00504 GLUTmenuItem *
00505 __glutGetMenuItem(GLUTmenu * menu, Window win, int *which)
00506 {
00507   GLUTmenuItem *item;
00508   int i;
00509 
00510   i = menu->num;
00511   item = menu->list;
00512   while (item) {
00513     if (item->win == win) {
00514       *which = i;
00515       return item;
00516     }
00517     if (item->isTrigger) {
00518       GLUTmenuItem *subitem;
00519 
00520       subitem = __glutGetMenuItem(menuList[item->value],
00521         win, which);
00522       if (subitem) {
00523         return subitem;
00524       }
00525     }
00526     i--;
00527     item = item->next;
00528   }
00529   return NULL;
00530 }
00531 
00532 static int
00533 getMenuItemIndex(GLUTmenuItem * item)
00534 {
00535   int count = 0;
00536 
00537   while (item) {
00538     count++;
00539     item = item->next;
00540   }
00541   return count;
00542 }
00543 
00544 GLUTmenu *
00545 __glutGetMenu(Window win)
00546 {
00547   GLUTmenu *menu;
00548 
00549   menu = __glutMappedMenu;
00550   while (menu) {
00551     if (win == menu->win) {
00552       return menu;
00553     }
00554     menu = menu->cascade;
00555   }
00556   return NULL;
00557 }
00558 
00559 GLUTmenu *
00560 __glutGetMenuByNum(int menunum)
00561 {
00562   if (menunum < 1 || menunum > menuListSize) {
00563     return NULL;
00564   }
00565   return menuList[menunum - 1];
00566 }
00567 
00568 static int
00569 getUnusedMenuSlot(void)
00570 {
00571   int i;
00572 
00573   /* Look for allocated, unused slot. */
00574   for (i = 0; i < menuListSize; i++) {
00575     if (!menuList[i]) {
00576       return i;
00577     }
00578   }
00579   /* Allocate a new slot. */
00580   menuListSize++;
00581   if (menuList) {
00582     menuList = (GLUTmenu **)
00583       realloc(menuList, menuListSize * sizeof(GLUTmenu *));
00584   } else {
00585     /* XXX Some realloc's do not correctly perform a malloc
00586        when asked to perform a realloc on a NULL pointer,
00587        though the ANSI C library spec requires this. */
00588     menuList = (GLUTmenu **) malloc(sizeof(GLUTmenu *));
00589   }
00590   if (!menuList)
00591     __glutFatalError("out of memory.");
00592   menuList[menuListSize - 1] = NULL;
00593   return menuListSize - 1;
00594 }
00595 
00596 static void
00597 menuModificationError(void)
00598 {
00599   /* XXX Remove the warning after GLUT 3.0. */
00600   __glutWarning("The following is a new check for GLUT 3.0; update your code.");
00601   __glutFatalError("menu manipulation not allowed while menus in use");
00602 }
00603 
00604 int
00605 glutCreateMenu(GLUTselectCB selectFunc)
00606 {
00607   XSetWindowAttributes wa;
00608   GLUTmenu *menu;
00609   int menuid;
00610 
00611   if (__glutMappedMenu)
00612     menuModificationError();
00613   if (!__glutDisplay)
00614     __glutOpenXConnection(NULL);
00615   menuid = getUnusedMenuSlot();
00616   menu = (GLUTmenu *) malloc(sizeof(GLUTmenu));
00617   if (!menu)
00618     __glutFatalError("out of memory.");
00619   menu->id = menuid;
00620   menu->num = 0;
00621   menu->submenus = 0;
00622   menu->managed = False;
00623   menu->pixwidth = 0;
00624   menu->select = selectFunc;
00625   menu->list = NULL;
00626   menu->cascade = NULL;
00627   menu->highlighted = NULL;
00628   menu->anchor = NULL;
00629   menuSetup();
00630   wa.override_redirect = True;
00631   wa.background_pixel = menuGray;
00632   wa.border_pixel = menuBlack;
00633   wa.colormap = menuColormap;
00634   wa.event_mask = StructureNotifyMask | ExposureMask |
00635     ButtonPressMask | ButtonReleaseMask |
00636     EnterWindowMask | LeaveWindowMask;
00637   /* Save unders really only enabled if useSaveUnders is set to
00638      CWSaveUnder, ie. using Mesa 3D.  See earlier comments. */
00639   wa.save_under = True;
00640   menu->win = XCreateWindow(__glutDisplay, __glutRoot,
00641   /* real position determined when mapped */
00642     0, 0,
00643   /* real size will be determined when menu is manged */
00644     1, 1,
00645     MENU_BORDER, menuDepth, InputOutput, menuVisual,
00646     CWOverrideRedirect | CWBackPixel | CWBorderPixel |
00647     CWEventMask | CWColormap | useSaveUnders,
00648     &wa);
00649   menuGraphicsContextSetup(menu->win);
00650   menuList[menuid] = menu;
00651   __glutSetMenu(menu);
00652   return menuid + 1;
00653 }
00654 
00655 void
00656 glutDestroyMenu(int menunum)
00657 {
00658   GLUTmenu *menu = __glutGetMenuByNum(menunum);
00659   GLUTmenuItem *item, *next;
00660 
00661   if (__glutMappedMenu)
00662     menuModificationError();
00663   assert(menu->id == menunum - 1);
00664   XDestroySubwindows(__glutDisplay, menu->win);
00665   XDestroyWindow(__glutDisplay, menu->win);
00666   menuList[menunum - 1] = NULL;
00667   /* free all menu entries */
00668   item = menu->list;
00669   while (item) {
00670     assert(item->menu == menu);
00671     next = item->next;
00672     free(item->label);
00673     free(item);
00674     item = next;
00675   }
00676   if (__glutCurrentMenu == menu) {
00677     __glutCurrentMenu = NULL;
00678   }
00679   free(menu);
00680 }
00681 
00682 int
00683 glutGetMenu(void)
00684 {
00685   if (__glutCurrentMenu) {
00686     return __glutCurrentMenu->id + 1;
00687   } else {
00688     return 0;
00689   }
00690 }
00691 
00692 void
00693 glutSetMenu(int menuid)
00694 {
00695   GLUTmenu *menu;
00696 
00697   if (menuid < 1 || menuid > menuListSize) {
00698     __glutWarning("glutSetMenu attempted on bogus menu.");
00699     return;
00700   }
00701   menu = menuList[menuid - 1];
00702   if (!menu) {
00703     __glutWarning("glutSetMenu attempted on bogus menu.");
00704     return;
00705   }
00706   __glutSetMenu(menu);
00707 }
00708 
00709 static void
00710 setMenuItem(GLUTmenuItem * item, char *label,
00711   int value, Bool isTrigger)
00712 {
00713   GLUTmenu *menu;
00714 
00715   menu = item->menu;
00716   item->label = strdup(label);
00717   if (!item->label)
00718     __glutFatalError("out of memory.");
00719   item->isTrigger = isTrigger;
00720   item->len = (int) strlen(label);
00721   item->value = value;
00722   item->pixwidth = XTextWidth(menuFont, label, item->len) + 4;
00723   if (item->pixwidth > menu->pixwidth) {
00724     menu->pixwidth = item->pixwidth;
00725   }
00726   menu->managed = False;
00727 }
00728 
00729 void
00730 glutAddMenuEntry(char *label, int value)
00731 {
00732   XSetWindowAttributes wa;
00733   GLUTmenuItem *entry;
00734 
00735   if (__glutMappedMenu)
00736     menuModificationError();
00737   entry = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
00738   if (!entry)
00739     __glutFatalError("out of memory.");
00740   entry->menu = __glutCurrentMenu;
00741   setMenuItem(entry, label, value, False);
00742   wa.event_mask = EnterWindowMask | LeaveWindowMask;
00743   entry->win = XCreateWindow(__glutDisplay,
00744     __glutCurrentMenu->win, MENU_GAP,
00745     __glutCurrentMenu->num * fontHeight + MENU_GAP,  /* x & y */
00746     entry->pixwidth, fontHeight,  /* width & height */
00747     0, CopyFromParent, InputOnly, CopyFromParent,
00748     CWEventMask, &wa);
00749   XMapWindow(__glutDisplay, entry->win);
00750   __glutCurrentMenu->num++;
00751   entry->next = __glutCurrentMenu->list;
00752   __glutCurrentMenu->list = entry;
00753 }
00754 
00755 void
00756 glutAddSubMenu(char *label, int menu)
00757 {
00758   XSetWindowAttributes wa;
00759   GLUTmenuItem *submenu;
00760 
00761   if (__glutMappedMenu)
00762     menuModificationError();
00763   submenu = (GLUTmenuItem *) malloc(sizeof(GLUTmenuItem));
00764   if (!submenu)
00765     __glutFatalError("out of memory.");
00766   __glutCurrentMenu->submenus++;
00767   submenu->menu = __glutCurrentMenu;
00768   setMenuItem(submenu, label, /* base 0 */ menu - 1, True);
00769   wa.event_mask = EnterWindowMask | LeaveWindowMask;
00770   submenu->win = XCreateWindow(__glutDisplay,
00771     __glutCurrentMenu->win, MENU_GAP,
00772     __glutCurrentMenu->num * fontHeight + MENU_GAP,  /* x & y */
00773     submenu->pixwidth, fontHeight,  /* width & height */
00774     0, CopyFromParent, InputOnly, CopyFromParent,
00775     CWEventMask, &wa);
00776   XMapWindow(__glutDisplay, submenu->win);
00777   __glutCurrentMenu->num++;
00778   submenu->next = __glutCurrentMenu->list;
00779   __glutCurrentMenu->list = submenu;
00780 }
00781 
00782 void
00783 glutChangeToMenuEntry(int num, char *label, int value)
00784 {
00785   GLUTmenuItem *item;
00786   int i;
00787 
00788   if (__glutMappedMenu)
00789     menuModificationError();
00790   i = __glutCurrentMenu->num;
00791   item = __glutCurrentMenu->list;
00792   while (item) {
00793     if (i == num) {
00794       if (item->isTrigger) {
00795         /* If changing a submenu trigger to a menu entry, we
00796            need to account for submenus.  */
00797         item->menu->submenus--;
00798       }
00799       free(item->label);
00800       setMenuItem(item, label, value, False);
00801       return;
00802     }
00803     i--;
00804     item = item->next;
00805   }
00806   __glutWarning("Current menu has no %d item.", num);
00807 }
00808 
00809 void
00810 glutChangeToSubMenu(int num, char *label, int menu)
00811 {
00812   GLUTmenuItem *item;
00813   int i;
00814 
00815   if (__glutMappedMenu)
00816     menuModificationError();
00817   i = __glutCurrentMenu->num;
00818   item = __glutCurrentMenu->list;
00819   while (item) {
00820     if (i == num) {
00821       if (!item->isTrigger) {
00822         /* If changing a menu entry to as submenu trigger, we
00823            need to account for submenus.  */
00824         item->menu->submenus++;
00825       }
00826       free(item->label);
00827       setMenuItem(item, label, /* base 0 */ menu - 1, True);
00828       return;
00829     }
00830     i--;
00831     item = item->next;
00832   }
00833   __glutWarning("Current menu has no %d item.", num);
00834 }
00835 
00836 void
00837 glutRemoveMenuItem(int num)
00838 {
00839   GLUTmenuItem *item, **prev, *remaining;
00840   int pixwidth, i;
00841 
00842   if (__glutMappedMenu)
00843     menuModificationError();
00844   i = __glutCurrentMenu->num;
00845   prev = &__glutCurrentMenu->list;
00846   item = __glutCurrentMenu->list;
00847   /* If menu item is removed, the menu's pixwidth may need to
00848      be recomputed. */
00849   pixwidth = 0;
00850   while (item) {
00851     if (i == num) {
00852       /* If this menu item's pixwidth is as wide as the menu's
00853          pixwidth, removing this menu item will necessitate
00854          shrinking the menu's pixwidth. */
00855       if (item->pixwidth >= __glutCurrentMenu->pixwidth) {
00856         /* Continue recalculating menu pixwidth, first skipping
00857            the removed item. */
00858         remaining = item->next;
00859         while (remaining) {
00860           if (remaining->pixwidth > pixwidth) {
00861             pixwidth = remaining->pixwidth;
00862           }
00863           remaining = remaining->next;
00864         }
00865       }
00866       __glutCurrentMenu->num--;
00867       __glutCurrentMenu->managed = False;
00868       __glutCurrentMenu->pixwidth = pixwidth;
00869 
00870       /* Patch up menu's item list. */
00871       *prev = item->next;
00872 
00873       free(item->label);
00874       free(item);
00875       return;
00876     }
00877     if (item->pixwidth > pixwidth) {
00878       pixwidth = item->pixwidth;
00879     }
00880     i--;
00881     prev = &item->next;
00882     item = item->next;
00883   }
00884   __glutWarning("Current menu has no %d item.", num);
00885 }
00886 
00887 void
00888 glutAttachMenu(int button)
00889 {
00890   if (__glutMappedMenu)
00891     menuModificationError();
00892   if (__glutCurrentWindow->menu[button] < 1) {
00893     __glutCurrentWindow->buttonUses++;
00894   }
00895   __glutChangeWindowEventMask(
00896     ButtonPressMask | ButtonReleaseMask, True);
00897   __glutCurrentWindow->menu[button] = __glutCurrentMenu->id + 1;
00898 }
00899 
00900 void
00901 glutDetachMenu(int button)
00902 {
00903   if (__glutMappedMenu)
00904     menuModificationError();
00905   if (__glutCurrentWindow->menu[button] > 0) {
00906     __glutCurrentWindow->buttonUses--;
00907     __glutChangeWindowEventMask(ButtonPressMask | ButtonReleaseMask,
00908       __glutCurrentWindow->buttonUses > 0);
00909     __glutCurrentWindow->menu[button] = 0;
00910   }
00911 }
00912 
00913 void
00914 __glutMenuItemEnterOrLeave(GLUTmenuItem * item,
00915   int num, int type)
00916 {
00917   int alreadyUp = 0;
00918 
00919   if (type == EnterNotify) {
00920     GLUTmenuItem *prevItem = item->menu->highlighted;
00921 
00922     if (prevItem && prevItem != item) {
00923       /* If there's an already higlighted item in this menu
00924          that is different from this one (we could be
00925          re-entering an item with an already cascaded
00926          submenu!), unhighlight the previous item. */
00927       item->menu->highlighted = NULL;
00928       paintMenuItem(prevItem, getMenuItemIndex(prevItem));
00929     }
00930     item->menu->highlighted = item;
00931     __glutItemSelected = item;
00932     if (item->menu->cascade) {
00933       if (!item->isTrigger) {
00934         /* Entered a menu item that is not a submenu trigger,
00935            so pop down the current submenu cascade of this
00936            menu.  */
00937         unmapMenu(item->menu->cascade);
00938         item->menu->cascade = NULL;
00939       } else {
00940         GLUTmenu *submenu = menuList[item->value];
00941 
00942         if (submenu->anchor == item) {
00943           /* We entered the submenu trigger for the submenu
00944              that is already up, so don't take down the
00945              submenu.  */
00946           alreadyUp = 1;
00947         } else {
00948           /* Submenu already popped up for some other submenu
00949              item of this menu; need to pop down that other
00950              submenu cascade.  */
00951           unmapMenu(item->menu->cascade);
00952           item->menu->cascade = NULL;
00953         }
00954       }
00955     }
00956     if (!alreadyUp) {
00957       /* Make sure the menu item gets painted with
00958          highlighting. */
00959       paintMenuItem(item, num);
00960     } else {
00961       /* If already up, should already be highlighted.  */
00962     }
00963   } else {
00964     /* LeaveNotify: Handle leaving a menu item...  */
00965     if (item->menu->cascade &&
00966       item->menu->cascade->anchor == item) {
00967       /* If there is a submenu casacaded from this item, do not
00968          change the highlighting on this item upon leaving. */
00969     } else {
00970       /* Unhighlight this menu item.  */
00971       item->menu->highlighted = NULL;
00972       paintMenuItem(item, num);
00973     }
00974     __glutItemSelected = NULL;
00975   }
00976   if (item->isTrigger) {
00977     if (type == EnterNotify && !alreadyUp) {
00978       GLUTmenu *submenu = menuList[item->value];
00979 
00980       mapMenu(submenu,
00981         item->menu->x + item->menu->pixwidth +
00982         MENU_ARROW_GAP + MENU_ARROW_WIDTH +
00983         MENU_GAP + MENU_BORDER,
00984         item->menu->y + fontHeight * (num - 1) + MENU_GAP);
00985       item->menu->cascade = submenu;
00986       submenu->anchor = item;
00987     }
00988   }
00989 }
 

Powered by Plone

This site conforms to the following standards: