00001 
00002 
00003 
00004 
00005 
00006 
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>  
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 
00041 
00042 
00043 
00044 
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;       
00052 
00053 
00054   for (;;) {
00055     
00056     if (XAllocColor(dpy, cmap, color))
00057       return;
00058 
00059     
00060     
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     
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     
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     
00090 
00091 
00092 
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           
00117 
00118 
00119 
00120           
00121 
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           
00149 
00150           useSaveUnders = 0;
00151           XFree(overlayVisuals);
00152           return;
00153         }
00154       }
00155       XFree(overlayVisuals);
00156     }
00157   }
00158   
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   
00170 
00171 
00172 
00173 
00174 
00175 
00176 
00177 
00178 
00179 
00180 
00181 
00182 
00183 
00184 
00185 
00186 
00187 
00188 
00189 
00190 
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     
00206     return;
00207   }
00208   menuFont = XLoadQueryFont(__glutDisplay,
00209     "-*-helvetica-bold-o-normal--14-*-*-*-p-*-iso8859-1");
00210   if (!menuFont) {
00211     
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 
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   
00280 
00281 
00282 
00283 
00284 
00285   XFlush(__glutDisplay);
00286 
00287   if (__glutMenuStatusFunc) {
00288     if (win != __glutMenuWindow->win) {
00289       
00290 
00291 
00292 
00293 
00294       rc = XTranslateCoordinates(__glutDisplay, win, __glutMenuWindow->win,
00295         x, y, &x, &y, &dummy);
00296       assert(rc != False);  
00297     }
00298     __glutSetWindow(__glutMenuWindow);
00299     __glutSetMenu(__glutMappedMenu);
00300 
00301     
00302 
00303     __glutMappedMenu = NULL;
00304 
00305     __glutMenuStatusFunc(GLUT_MENU_NOT_IN_USE, x, y);
00306   }
00307   
00308 
00309   __glutMappedMenu = NULL;
00310 
00311   
00312 
00313   if (__glutItemSelected && !__glutItemSelected->isTrigger) {
00314     __glutSetWindow(__glutMenuWindow);
00315     
00316 
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   
00337 
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   
00347 
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   
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   
00387 
00388   menu->x = changes.x;
00389   menu->y = changes.y;
00390 
00391   XConfigureWindow(__glutDisplay, menu->win, mask, &changes);
00392   XInstallColormap(__glutDisplay, menuColormap);
00393   
00394 
00395 
00396 
00397 
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     
00415 
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       
00490 
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   
00574   for (i = 0; i < menuListSize; i++) {
00575     if (!menuList[i]) {
00576       return i;
00577     }
00578   }
00579   
00580   menuListSize++;
00581   if (menuList) {
00582     menuList = (GLUTmenu **)
00583       realloc(menuList, menuListSize * sizeof(GLUTmenu *));
00584   } else {
00585     
00586 
00587 
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   
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   
00638 
00639   wa.save_under = True;
00640   menu->win = XCreateWindow(__glutDisplay, __glutRoot,
00641   
00642     0, 0,
00643   
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   
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,  
00746     entry->pixwidth, fontHeight,  
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,  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,  
00773     submenu->pixwidth, fontHeight,  
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         
00796 
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         
00823 
00824         item->menu->submenus++;
00825       }
00826       free(item->label);
00827       setMenuItem(item, label,  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   
00848 
00849   pixwidth = 0;
00850   while (item) {
00851     if (i == num) {
00852       
00853 
00854 
00855       if (item->pixwidth >= __glutCurrentMenu->pixwidth) {
00856         
00857 
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       
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       
00924 
00925 
00926 
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         
00935 
00936 
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           
00944 
00945 
00946           alreadyUp = 1;
00947         } else {
00948           
00949 
00950 
00951           unmapMenu(item->menu->cascade);
00952           item->menu->cascade = NULL;
00953         }
00954       }
00955     }
00956     if (!alreadyUp) {
00957       
00958 
00959       paintMenuItem(item, num);
00960     } else {
00961       
00962     }
00963   } else {
00964     
00965     if (item->menu->cascade &&
00966       item->menu->cascade->anchor == item) {
00967       
00968 
00969     } else {
00970       
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 }