home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume9 / xscreen / part01 / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-19  |  25.0 KB  |  1,009 lines

  1. /*
  2.  *    $Source: /u1/X/xterm/RCS/menu.c,v $
  3.  *    $Header: menu.c,v 10.101 86/12/01 17:52:43 swick Rel $
  4.  */
  5.  
  6. #ifdef MODEMENU
  7. #include "X/Xlib.h"
  8. #include "menu.h"
  9.  
  10. #ifndef lint
  11. static char sccs_id[] = "@(#)menu.c\tX10/6.6B\t12/26/86";
  12. #endif    lint
  13.  
  14. #define    FALSE            0
  15. #define    TRUE            1
  16. #define    InvertPlane        1
  17. #define    SetStateFlags(item)    item->itemFlags = (item->itemFlags &\
  18.                  ~(itemStateMask | itemChanged)) |\
  19.                  ((item->itemFlags & itemSetMask) >>\
  20.                  itemSetMaskShift)
  21.  
  22.  
  23. static short Check_MarkBits[] = {
  24.    0x0100, 0x0180, 0x00c0, 0x0060,
  25.    0x0031, 0x001b, 0x000e, 0x0004
  26. };
  27. static short Check_GrayBits[] = {
  28.    0x0100, 0x0080, 0x0040, 0x0020,
  29.    0x0011, 0x000a, 0x0004, 0x0000
  30. };
  31. static short Default_CursorBits[] = {
  32.    0x0000, 0x0002, 0x0006, 0x000e,
  33.    0x001e, 0x003e, 0x007e, 0x00fe,
  34.    0x01fe, 0x003e, 0x0036, 0x0062,
  35.    0x0060, 0x00c0, 0x00c0, 0x0000
  36. };
  37. static short Default_GrayBits[] = {
  38.    0xaaaa, 0x5555, 0xaaaa, 0x5555,
  39.    0xaaaa, 0x5555, 0xaaaa, 0x5555,
  40.    0xaaaa, 0x5555, 0xaaaa, 0x5555,
  41.    0xaaaa, 0x5555, 0xaaaa, 0x5555,
  42. };
  43. static short Default_MaskBits[] = {
  44.    0x0003, 0x0007, 0x000f, 0x001f,
  45.    0x003f, 0x007f, 0x00ff, 0x01ff,
  46.    0x03ff, 0x07ff, 0x007f, 0x00f7,
  47.    0x00f3, 0x01e1, 0x01e0, 0x01c0
  48. };
  49. static char def_menu_font[] = "vtsingle";
  50.  
  51. Pixmap Gray_Tile;
  52. Menu Menu_Default;
  53. Cursor Menu_DefaultCursor;
  54. char *Menu_DefaultFont;
  55. FontInfo *Menu_DefaultFontInfo;
  56.  
  57. /*
  58.  * AddMenuItem() adds a menu item to an existing menu, at the end of the
  59.  * list, which are number sequentially from zero.  The menuitem index is
  60.  * return, or -1 if failed.
  61.  */
  62.  
  63. AddMenuItem(menu, text)
  64. register Menu *menu;
  65. register char *text;
  66. {
  67.     register MenuItem *menuitem, **next;
  68.     register int i;
  69.     extern char *malloc();
  70.  
  71.     if(!menu || !text || (menuitem = (MenuItem *)malloc(sizeof(MenuItem)))
  72.      == (MenuItem *)0)
  73.         return(-1);
  74.     bzero((char *)menuitem, sizeof(MenuItem));
  75.     menuitem->itemText = text;
  76.     menuitem->itemTextLength = strlen(text);
  77.     for(i = 0, next = &menu->menuItems ; *next ; i++)
  78.         next = &(*next)->nextItem;
  79.     *next = menuitem;
  80.     menu->menuFlags |= menuChanged;
  81.     return(i);
  82. }
  83.  
  84. /*
  85.  * DisposeItem() releases the memory allocated for the given indexed
  86.  * menuitem.  Nonzero is returned if an item was actual disposed of.
  87.  */
  88. DisposeItem(menu, i)
  89. register Menu *menu;
  90. register int i;
  91. {
  92.     register MenuItem **next, **last, *menuitem;
  93.  
  94.     if(!menu || i < 0)
  95.         return(0);
  96.     next = &menu->menuItems;
  97.     do {
  98.         if(!*next)
  99.             return(0);
  100.         last = next;
  101.         next = &(*next)->nextItem;
  102.     } while(i-- > 0);
  103.     menuitem = *last;
  104.     *last = *next;
  105.     free(menuitem);
  106.     return(1);
  107. }
  108.  
  109. /*
  110.  * DisposeMenu() releases the memory allocated for the given menu.
  111.  */
  112. DisposeMenu(menu)
  113. register Menu *menu;
  114. {
  115.     static Unmap_Menu();
  116.  
  117.     if(!menu)
  118.         return;
  119.     if(menu->menuFlags & menuMapped)
  120.         Unmap_Menu(menu);
  121.     while(DisposeItem(menu, 0));
  122.     if(menu->menuWindow)
  123.         XDestroyWindow(menu->menuWindow);
  124.     if(menu->menuSaved)
  125.         XFreePixmap(menu->menuSaved);
  126.     free(menu);
  127. }
  128.  
  129. InitMenu(name)
  130. register char *name;
  131. {
  132.     register char *cp;
  133.     register Bitmap bit;
  134.  
  135.     /*
  136.      * If the gray tile hasn't been set up, do it now.
  137.      */
  138.     if(!Gray_Tile) {
  139.         if(!(bit = XStoreBitmap(grayWidth, grayHeight,
  140.          Default_GrayBits)))
  141.             return;
  142.         Gray_Tile = XMakePixmap(bit, WhitePixel, BlackPixel);
  143.         XFreeBitmap(bit);
  144.     }
  145.     Menu_Default.menuFlags = menuChanged;
  146.     if((cp = XGetDefault(name, "MenuFreeze")) && strcmp(cp, "on") == 0)
  147.         Menu_Default.menuFlags |= menuFreeze;
  148.     if((cp = XGetDefault(name, "MenuSave")) && strcmp(cp, "on") == 0)
  149.         Menu_Default.menuFlags |= menuSaveMenu;
  150.     Menu_Default.menuInitialItem = -1;
  151.     Menu_Default.menuBorderWidth = (cp = XGetDefault(name, "MenuBorder")) ?
  152.      atoi(cp) : 2;
  153.     Menu_Default.menuItemPad = (cp = XGetDefault(name, "MenuPad")) ?
  154.      atoi(cp) : 3;
  155.     Menu_DefaultFont = (cp = XGetDefault(name, "MenuFont")) ? cp :
  156.      def_menu_font;
  157. };
  158.  
  159. /*
  160.  * ItemFlags returns the state of item "n" of the menu.
  161.  */
  162. ItemFlags(menu, n)
  163. register Menu *menu;
  164. register int n;
  165. {
  166.     register MenuItem *item;
  167.  
  168.     if(!menu || !menu->menuItems || n < 0)
  169.         return(-1);
  170.     for(item = menu->menuItems ; n > 0 ; n--)
  171.         if(!(item = item->nextItem))
  172.             return(0);
  173.     return((item->itemFlags & itemSetMask) >> itemSetMaskShift);
  174. }
  175.  
  176. /*
  177.  * ItemText changes the text of item "n" of the menu.
  178.  */
  179. ItemText(menu, n, text)
  180. register Menu *menu;
  181. register int n;
  182. char *text;
  183. {
  184.     register MenuItem *item;
  185.  
  186.     if(!menu || !menu->menuItems || n < 0 || !text)
  187.         return(0);
  188.     for(item = menu->menuItems ; n > 0 ; n--)
  189.         if(!(item = item->nextItem))
  190.             return(0);
  191.     item->itemText = text;
  192.     menu->menuFlags |= menuChanged;
  193.     return(1);
  194. }
  195.  
  196. /*
  197.  * NewMenu() returns a pointer to an initialized new Menu structure, or NULL
  198.  * if failed.
  199.  *
  200.  * The Menu structure _menuDefault contains the default menu settings.
  201.  */
  202. Menu *NewMenu(name, reverse)
  203. char *name;
  204. int reverse;
  205. {
  206.     register Menu *menu;
  207.     register int fg, bg;
  208.     extern char *malloc();
  209.  
  210.     /*
  211.      * If the GrayTile hasn't been defined, InitMenu() was never
  212.      * run, so exit.
  213.      */
  214.     if(!Gray_Tile)
  215.         return((Menu *)0);
  216.     /*
  217.      * Allocate the memory for the menu structure.
  218.      */
  219.     if((menu = (Menu *)malloc(sizeof(Menu))) == (Menu *)0)
  220.         return((Menu *)0);
  221.     /*
  222.      * Initialize to default values.
  223.      */
  224.     *menu = Menu_Default;
  225.     /*
  226.      * If the menu font hasn't yet been gotten, go get it.
  227.      */
  228.     if(!menu->menuFontInfo) {
  229.         if(!Menu_DefaultFontInfo && !(Menu_DefaultFontInfo =
  230.          XOpenFont(Menu_DefaultFont)))
  231.             return((Menu *)0);
  232.         menu->menuFontInfo = Menu_DefaultFontInfo;
  233.     }
  234.     /*
  235.      * If the menu cursor hasn't been given, make a default one.
  236.      */
  237.     if(!menu->menuCursor) {
  238.         if(!Menu_DefaultCursor) {
  239.             if(reverse) {
  240.                 fg = WhitePixel;
  241.                 bg = BlackPixel;
  242.             } else {
  243.                 fg = BlackPixel;
  244.                 bg = WhitePixel;
  245.             }
  246.             if(!(Menu_DefaultCursor =
  247.              XCreateCursor(defaultCursorWidth, defaultCursorHeight,
  248.               Default_CursorBits, Default_MaskBits, defaultCursorX,
  249.               defaultCursorY, fg, bg, GXcopy)))
  250.                 return((Menu *)0);
  251.         }
  252.         menu->menuCursor = Menu_DefaultCursor;
  253.     }
  254.     /*
  255.      * Initialze the default background and border pixmaps and foreground
  256.      * and background colors (black and white).
  257.      */
  258.     if(reverse) {
  259.         menu->menuBgTile = BlackPixmap;
  260.         menu->menuFgColor = WhitePixel;
  261.         menu->menuBgColor = BlackPixel;
  262.     } else {
  263.         menu->menuBgTile = WhitePixmap;
  264.         menu->menuFgColor = BlackPixel;
  265.         menu->menuBgColor = WhitePixel;
  266.     }
  267.     /*
  268.      * Set the menu title.  If name is NULL or is an empty string, no
  269.      * title will be displayed.
  270.      */
  271.     if(name && *name) {
  272.         menu->menuTitleLength = strlen(menu->menuTitle = name);
  273.         menu->menuTitleWidth = XStringWidth(name, menu->menuFontInfo,
  274.          0, 0);
  275.         menu->menuItemTop = menu->menuFontInfo->height + 2 *
  276.          menu->menuItemPad + 1;
  277.     } else
  278.         menu->menuTitleLength = menu->menuTitleWidth =
  279.          menu->menuItemTop = 0;
  280.     return(menu);
  281. }
  282.  
  283. /*
  284.  * SetItemCheck sets the check state of item "n" of the menu to "state".
  285.  */
  286. SetItemCheck(menu, n, state)
  287. register Menu *menu;
  288. register int n;
  289. int state;
  290. {
  291.     register MenuItem *item;
  292.  
  293.     if(!menu || !menu->menuItems || n < 0)
  294.         return(0);
  295.     for(item = menu->menuItems ; n > 0 ; n--)
  296.         if(!(item = item->nextItem))
  297.             return(0);
  298.     if(state)
  299.         item->itemFlags |= itemSetChecked;
  300.     else
  301.         item->itemFlags &= ~itemSetChecked;
  302.     if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
  303.      (item->itemFlags & itemStateMask)) {
  304.         item->itemFlags |= itemChanged;
  305.         menu->menuFlags |= menuItemChanged;
  306.     } else
  307.         item->itemFlags &= ~itemChanged;
  308.     return(1);
  309. }
  310.  
  311. /*
  312.  * SetItemDisable sets the disable state of item "n" of the menu to "state".
  313.  */
  314. SetItemDisable(menu, n, state)
  315. register Menu *menu;
  316. register int n;
  317. int state;
  318. {
  319.     register MenuItem *item;
  320.  
  321.     if(!menu || !menu->menuItems || n < 0)
  322.         return(0);
  323.     for(item = menu->menuItems ; n > 0 ; n--)
  324.         if(!(item = item->nextItem))
  325.             return(0);
  326.     if(state)
  327.         item->itemFlags |= itemSetDisabled;
  328.     else
  329.         item->itemFlags &= ~itemSetDisabled;
  330.     if(((item->itemFlags & itemSetMask) >> itemSetMaskShift) !=
  331.      (item->itemFlags & itemStateMask)) {
  332.         item->itemFlags |= itemChanged;
  333.         menu->menuFlags |= menuItemChanged;
  334.     } else
  335.         item->itemFlags &= ~itemChanged;
  336.     return(1);
  337. }
  338.  
  339. /*
  340.  * TrackMenu does most of the work of displaying the menu and tracking the
  341.  * mouse.
  342.  */
  343. TrackMenu(menu, event)
  344. register Menu *menu;
  345. register XButtonPressedEvent *event;
  346. {
  347.     register MenuItem *item;
  348.     register int i, button;
  349.     register MenuItem *hilited_item = (MenuItem *)0;
  350.     register int drawn;
  351.     XButtonReleasedEvent ev;
  352.     register int changed;
  353.     int y, n, hilited_y, hilited_n, in_window;
  354.     static MenuItem *Mouse_InItem(), *Y_InItem();
  355.     static Unmap_Menu();
  356.  
  357.     /*
  358.      * Check that things are reasonable.
  359.      */
  360.     if(!menu || !event || !menu->menuItems || event->type != ButtonPressed)
  361.         return(-1);
  362.     /*
  363.      * Set the changed flag and clear the menu changed flags.
  364.      */
  365.     changed = menu->menuFlags & (menuChanged | menuItemChanged);
  366.     /*
  367.      * If the entire menu has changed, throw away any saved pixmap and
  368.      * then call RecalcMenu().
  369.      */
  370.     if(changed & menuChanged) {
  371.         if(menu->menuSaved)
  372.             XFreePixmap(menu->menuSaved);
  373.         menu->menuSaved = (Pixmap)0;
  374.         if(!Recalc_Menu(menu))
  375.             return(-1);
  376.         changed &= ~menuItemChanged;
  377.     }
  378.     /*
  379.      * Now if the window was never created, go ahead and make it.  Otherwise
  380.      * if the menu has changed, resize the window.
  381.      */
  382.     if(!menu->menuWindow) {
  383.         if((menu->menuWindow = XCreateWindow(RootWindow, 0, 0,
  384.          menu->menuWidth, menu->menuHeight, menu->menuBorderWidth,
  385.          Gray_Tile, menu->menuBgTile)) == (Window)0)
  386.             return(-1);
  387.         XDefineCursor(menu->menuWindow, menu->menuCursor);
  388.         XSelectInput(menu->menuWindow, ExposeWindow | EnterWindow |
  389.          LeaveWindow | MouseMoved | ButtonReleased);
  390.     } else if(changed & menuChanged)
  391.          XChangeWindow(menu->menuWindow, menu->menuWidth,
  392.          menu->menuHeight);
  393.     /*
  394.      * Figure out where the menu is supposed to go, from the initial button
  395.      * press, and move the window there.  Then map the menu.
  396.      */
  397.     if(!Move_Menu(menu, event) || !Map_Menu(menu))
  398.         return(-1);
  399.     /*
  400.      * Try to grab the mouse, over a period of 10 seconds.
  401.      */
  402.     for(i = 10 ; ; ) {
  403.         if(XGrabMouse(menu->menuWindow, menu->menuCursor,
  404.          ButtonReleased | EnterWindow | LeaveWindow | MouseMoved))
  405.             break;
  406.         if(--i <= 0) {
  407.             Unmap_Menu(menu);
  408.             return(-1);
  409.         }
  410.         sleep(1);
  411.     }
  412.     /*
  413.      * Save away the button that was pressed and use it to match a
  414.      * corresponding ButtonReleased event.
  415.      */
  416.     button = event->detail & 03;
  417.     /*
  418.      * Now process events for the menu window.
  419.      */
  420.     drawn = 0;
  421.     for( ; ; ) {
  422.         XNextEvent(&ev);
  423.         if(ev.type != ButtonReleased && ev.window != menu->menuWindow) {
  424.             if(menu->menuEventHandler)
  425.                 (*menu->menuEventHandler)(&ev);
  426.             continue;
  427.         }
  428.         switch(ev.type) {
  429.          case ExposeWindow:
  430.             /*
  431.              * If we have a saved pixmap, display it.  Otherwise
  432.              * redraw the menu and save it away.
  433.              */
  434.             if(menu->menuSaved) {
  435.                 XPixmapPut(menu->menuWindow, 0, 0, 0, 0,
  436.                  menu->menuWidth, menu->menuHeight,
  437.                  menu->menuSaved, GXcopy, AllPlanes);
  438.                 /*
  439.                  * If the menuItemChanged flag is still set,
  440.                  * then we need to redraw certain menu items.
  441.                  * ("i" is the vertical position of the top
  442.                  * of the current item.)
  443.                  */
  444.                 if(changed & menuItemChanged) {
  445.                     i = menu->menuItemTop;
  446.                     for(item = menu->menuItems ; item ;
  447.                      item = item->nextItem) {
  448.                         if(item->itemFlags &
  449.                          itemChanged)
  450.                             Modify_Item(menu, item,
  451.                              i);
  452.                         i += item->itemHeight;
  453.                     }
  454.                 }
  455.             } else
  456.                 Draw_Menu(menu);
  457.             /*
  458.              * If the menu has changed in any way and we want to
  459.              * save the menu, throw away any existing save menu
  460.              * image and make a new one.
  461.              */
  462.             XFlush();
  463.             if(changed && (menu->menuFlags & menuSaveMenu)) {
  464.                 if(menu->menuSaved)
  465.                     XFreePixmap(menu->menuSaved);
  466.                 menu->menuSaved = XPixmapSave(menu->menuWindow,
  467.                  0, 0, menu->menuWidth, menu->menuHeight);
  468.             }
  469.             /*
  470.              * See which item the cursor may currently be in.  If
  471.              * it is in a non-disabled item, hilite it.
  472.              */
  473.             if(hilited_item = Mouse_InItem(menu, &hilited_y,
  474.              &hilited_n, &in_window))
  475.                 XPixFill(menu->menuWindow, 0, hilited_y,
  476.                  menu->menuWidth, hilited_item->itemHeight,
  477.                  BlackPixmap, (Bitmap)0, GXinvert, InvertPlane);
  478.             drawn++;
  479.             break;
  480.          case EnterWindow:
  481.             in_window = TRUE;
  482.             /* drop through */
  483.          case MouseMoved:
  484.             if(!drawn || !in_window)
  485.                 break;
  486.             /*
  487.              * See which item the cursor may currently be in.  If
  488.              * the item has changed, unhilite the old one and
  489.              * then hilited the new one.
  490.              */
  491.             y = ((XEnterWindowEvent *)&ev)->y;
  492.             if((item = Y_InItem(menu, &y, &n)) != hilited_item) {
  493.                 if(hilited_item)
  494.                     XPixFill(menu->menuWindow, 0,
  495.                      hilited_y, menu->menuWidth,
  496.                      hilited_item->itemHeight, BlackPixmap,
  497.                      (Bitmap)0, GXinvert, InvertPlane);
  498.                 if(hilited_item = item) {
  499.                     XPixFill(menu->menuWindow, 0,
  500.                      hilited_y = y, menu->menuWidth,
  501.                      item->itemHeight, BlackPixmap,
  502.                      (Bitmap)0, GXinvert, InvertPlane);
  503.                     hilited_n = n;
  504.                 }
  505.             }
  506.             break;
  507.          case LeaveWindow:
  508.             if(!drawn)
  509.                 break;
  510.             /*
  511.              * Unhilite any window that is currently hilited.
  512.              */
  513.             if(hilited_item) {
  514.                 XPixFill(menu->menuWindow, 0, hilited_y,
  515.                  menu->menuWidth, hilited_item->itemHeight,
  516.                  BlackPixmap, (Bitmap)0, GXinvert, InvertPlane);
  517.                 hilited_item = (MenuItem *)0;
  518.             }
  519.             in_window = FALSE;
  520.             break;
  521.          case ButtonReleased:
  522.             /*
  523.              * If the correct button was released, ungrab the mouse
  524.              * and return the index number of any selected menu
  525.              * item.
  526.              */
  527.             if((ev.detail & 0x3) == button) {
  528.                 if(in_window) {
  529.                     y = ((XButtonReleasedEvent *)&ev)->y;
  530.                     if((item = Y_InItem(menu, &y, &n)) !=
  531.                      hilited_item) {
  532.                         if(hilited_item)
  533.                         XPixFill(menu->menuWindow, 0,
  534.                          hilited_y, menu->menuWidth,
  535.                          hilited_item->itemHeight,
  536.                          BlackPixmap, (Bitmap)0,
  537.                          GXinvert, InvertPlane);
  538.                         if(hilited_item = item) {
  539.                         XPixFill(menu->menuWindow, 0,
  540.                          hilited_y = y, menu->menuWidth,
  541.                          hilited_item->itemHeight,
  542.                          BlackPixmap, (Bitmap)0,
  543.                          GXinvert, InvertPlane);
  544.                         hilited_n = n;
  545.                         }
  546.                     }
  547.                 }
  548.                 XUngrabMouse();
  549.                 menu->menuFlags &= ~(menuChanged |
  550.                  menuItemChanged);
  551.                 Unmap_Menu(menu);
  552.                 XFlush();
  553.                 if(hilited_item)
  554.                     return(menu->menuInitialItem =
  555.                      hilited_n);
  556.                 return(-1);
  557.             }
  558.             break;
  559.         }
  560.     }
  561. }
  562.  
  563. /*
  564.  * Recalculate all of the various menu and item variables.
  565.  */
  566. static Recalc_Menu(menu)
  567. register Menu *menu;
  568. {
  569.     register MenuItem *item;
  570.     register int max, i, height, fontheight;
  571.  
  572.     /*
  573.      * We must have already gotten the menu font.
  574.      */
  575.     if(!menu->menuFontInfo)
  576.         return(0);
  577.     /*
  578.      * Initialize the various max width variables.
  579.      */
  580.     fontheight = menu->menuFontInfo->height;
  581.     height = menu->menuItemTop;
  582.     menu->menuMaxTextWidth = menu->menuTitleWidth;
  583.     /*
  584.      * The item height is the maximum of the font height and the
  585.      * checkbox height.
  586.      */
  587.     max = fontheight;
  588.     if(checkMarkHeight > max)
  589.         max = checkMarkHeight;
  590.     /*
  591.      * Go through the menu item list.
  592.      */
  593.     for(item = menu->menuItems ; item ; item = item->nextItem) {
  594.         /*
  595.          * If the item text is a single dash, we assume this is
  596.          * a line separator and treat it special.
  597.          */
  598.         if(strcmp(item->itemText, "-") == 0)
  599.             height += (item->itemHeight = lineSeparatorHeight);
  600.         else {
  601.             height += (item->itemHeight = max);
  602.             /*
  603.              * Check the text width with the max value stored in
  604.              * menu.
  605.              */
  606.             if((item->itemTextWidth = XStringWidth(item->itemText,
  607.              menu->menuFontInfo, 0, 0)) > menu->menuMaxTextWidth)
  608.                 menu->menuMaxTextWidth = item->itemTextWidth;
  609.         }
  610.         /*
  611.          * If the itemChanged flag is set, set the state bits.
  612.          */
  613.         if(item->itemFlags & itemChanged) {
  614.             item->itemFlags = (item->itemFlags & ~itemStateMask) |
  615.              ((item->itemFlags & itemSetMask) >> itemSetMaskShift);
  616.             item->itemFlags &= ~itemChanged;
  617.         }
  618.     }
  619.     /*
  620.      * Set the menu height and then set the menu width.
  621.      */
  622.     menu->menuHeight = height;
  623.     menu->menuWidth = 3 * menu->menuItemPad + menu->menuMaxTextWidth +
  624.      checkMarkWidth;
  625.     return(1);
  626. }
  627.  
  628. /*
  629.  * Figure out where to popup the menu, relative to the where the button was
  630.  * pressed.
  631.  */
  632. static Move_Menu(menu, ev)
  633. register Menu *menu;
  634. XButtonPressedEvent *ev;
  635. {
  636.     register MenuItem *item;
  637.     register int n, x, y;
  638.     int ev_x, ev_y;
  639.     int total_width;
  640.     Window subw;
  641.  
  642.     /*
  643.      * Get the coordinates of the mouse when the button was pressed.
  644.      */
  645.     XInterpretLocator(RootWindow, &ev_x, &ev_y, &subw, ev->location);
  646.     /*
  647.      * Try to popup the menu so that the cursor is centered within the
  648.      * width of the menu, but compensate if that would run it outside
  649.      * the display area.
  650.      */
  651.     total_width = menu->menuWidth + 2 * menu->menuBorderWidth;
  652.     if((x = ev_x - total_width / 2) < 0)
  653.         x = 0;
  654.     else if(x + total_width > DisplayWidth())
  655.         x = DisplayWidth() - total_width;
  656. #ifdef DROPMENUS
  657.     y = 0;
  658. #else DROPMENUS
  659.     /*
  660.      * If we have an inital item, try to popup the menu centered
  661.      * vertically within this item.
  662.      */
  663.     if(menu->menuInitialItem >= 0) {
  664.         /*
  665.          * Look through the item list. "y" is the vertical position
  666.          * of the top of the current item and "n" is the item number.
  667.          */
  668.         y = menu->menuItemTop + menu->menuBorderWidth;
  669.         for(n = 0, item = menu->menuItems ; ; n++) {
  670.             /*
  671.              * On finding the intial item, center within this item.
  672.              */
  673.             if(n == menu->menuInitialItem) {
  674.                 y += item->itemHeight / 2;
  675.                 break;
  676.             }
  677.             y += item->itemHeight;
  678.             /*
  679.              * If we run out of items, turn off the initial item
  680.              * and treat this as if no initial item.
  681.              */
  682.             if(!(item = item->nextItem)) {
  683.                 menu->menuInitialItem = -1;
  684.                 goto noInitial;
  685.             }
  686.         }
  687.     /*
  688.      * If no initial item, try to popup the menu centered in the item
  689.      * nearest the center of the menu.
  690.      */
  691.     } else {
  692. noInitial:
  693.         /*
  694.          * Look through the item list. "y" is the vertical position
  695.          * of the top of the current item and "n" is the vertical
  696.          * position of the center of the menu.
  697.          */
  698.         y = menu->menuItemTop + menu->menuBorderWidth;
  699.         for(n = menu->menuHeight / 2, item = menu->menuItems ; item ;
  700.          item = item->nextItem)
  701.             /*
  702.              * If the center of the menu is in this item, we
  703.              * center within this item.
  704.              */
  705.             if((y += item->itemHeight) > n) {
  706.                 y -= item->itemHeight / 2;
  707.                 break;
  708.             }
  709.     }
  710. #endif DROPMENU
  711.     /*
  712.      * If the menu extends above outside of the display, warp
  713.      * the mouse vertically so the menu will all show up.
  714.      */
  715.     if((y = ev_y - y) < 0) {
  716.         XWarpMouse(RootWindow, ev_x, ev_y - y);
  717.         y = 0;
  718.     } else if((n = y + menu->menuHeight + 2 * menu->menuBorderWidth
  719.                - DisplayHeight()) > 0) {
  720.         XWarpMouse(RootWindow, ev_x, ev_y - n);
  721.         y -= n;
  722.     }
  723.     XMoveWindow(menu->menuWindow, x, y);
  724.     /*
  725.      * If we are in freeze mode, save what will be the coordinates of
  726.      * the save image.
  727.      */
  728.     if(menu->menuFlags & menuFreeze) {
  729.         menu->menuSavedImageX = x;
  730.         menu->menuSavedImageY = y;
  731.     }
  732.     return(1);
  733. }
  734.  
  735. /*
  736.  * Map the menu window.
  737.  */
  738. static Map_Menu(menu)
  739. register Menu *menu;
  740. {
  741.     register int i;
  742.  
  743.     /*
  744.      * If we are in freeze mode, save the pixmap underneath where the menu
  745.      * will be (including the border).
  746.      */
  747.     if(menu->menuFlags & menuFreeze) {
  748.         XGrabServer();
  749.         i = 2 * menu->menuBorderWidth;
  750.         if((menu->menuSavedImage = XPixmapSave(RootWindow,
  751.          menu->menuSavedImageX, menu->menuSavedImageY, menu->menuWidth
  752.          + i, menu->menuHeight + i)) == (Pixmap)0)
  753.             return(0);
  754.     }
  755.     /*
  756.      * Actually map the window.
  757.      */
  758.     XMapWindow(menu->menuWindow);
  759.     menu->menuFlags |= menuMapped;
  760.     return(1);
  761. }
  762.  
  763. /*
  764.  * Draw the entire menu in the blank window.
  765.  */
  766. static Draw_Menu(menu)
  767. register Menu *menu;
  768. {
  769.     register MenuItem *item;
  770.     register int top = menu->menuItemTop;
  771.     register int x = menu->menuItemPad;
  772.     register int y, dim;
  773.  
  774.     /*
  775.      * If we have a menu title, draw it first, centered and hilited.
  776.      */
  777.     if(menu->menuTitleLength) {
  778.         XPixSet(menu->menuWindow, 0, 0, menu->menuWidth,
  779.          top - 1, menu->menuFgColor);
  780.         XText(menu->menuWindow, (menu->menuWidth -
  781.          menu->menuTitleWidth) / 2, menu->menuItemPad, menu->menuTitle,
  782.          menu->menuTitleLength, menu->menuFontInfo->id,
  783.          menu->menuBgColor, menu->menuFgColor);
  784.     }
  785.     /*
  786.      * For each item in the list, first draw any check mark and then
  787.      * draw the rest of it.
  788.      */
  789.     for(item = menu->menuItems ; item ; item = item->nextItem) {
  790.         SetStateFlags(item);
  791.         dim = (item->itemFlags & itemDisabled);
  792.         /*
  793.          * Draw the check mark, possibly dimmed, wherever is necessary.
  794.          */
  795.         if(item->itemFlags & itemChecked) {
  796.             XBitmapBitsPut(menu->menuWindow, x, y = top +
  797.              (item->itemHeight - checkMarkHeight) / 2,
  798.              checkMarkWidth, checkMarkHeight, dim ? Check_GrayBits :
  799.              Check_MarkBits, menu->menuFgColor, menu->menuBgColor,
  800.              (Bitmap)0, GXcopy, AllPlanes);
  801.         }
  802.         /*
  803.          * Draw the item, possibly dimmed.
  804.          */
  805.         Draw_Item(menu, item, top, dim);
  806.         top += item->itemHeight;
  807.     }
  808. }
  809.  
  810. /*
  811.  * Modify the item at vertical position y.  This routine is table driven and
  812.  * the state and set bits are each 2 bits long, contiguous, the least
  813.  * significant bits in the flag word and with the state bits in bits 0 & 1.
  814.  */
  815.  
  816. #define    drawCheck    0x10
  817. #define    removeCheck    0x08
  818. #define    dimCheck    0x04
  819. #define    drawItem    0x02
  820. #define    dimItem        0x01
  821.  
  822. static char Modify_Table[] = {
  823.     0x00, 0x02, 0x08, 0x0a, 0x01, 0x00, 0x09, 0x08,
  824.     0x10, 0x12, 0x00, 0x12, 0x15, 0x14, 0x05, 0x00
  825. };
  826.     
  827. static Modify_Item(menu, item, top)
  828. register Menu *menu;
  829. register MenuItem *item;
  830. int top;
  831. {
  832.     register int x = menu->menuItemPad;
  833.     register int y;
  834.     register int center = top + item->itemHeight / 2;
  835.     register int func = Modify_Table[item->itemFlags &
  836.      (itemStateMask | itemSetMask)];
  837.  
  838.     /*
  839.      * If we really won't be making a change, return.
  840.      */
  841.     if(func == 0)
  842.         return;
  843.     /*
  844.      * Draw the check mark if needed, possibly dimmed.
  845.      */
  846.     y = center - (checkMarkHeight / 2);
  847.     if(func & (drawCheck | dimCheck))
  848.         XBitmapBitsPut(menu->menuWindow, x, y, checkMarkWidth,
  849.          checkMarkHeight, (func & dimCheck) ? Check_GrayBits :
  850.          Check_MarkBits, menu->menuFgColor, menu->menuBgColor,
  851.          (Bitmap)0, GXcopy, AllPlanes);
  852.     /*
  853.      * Remove the check mark if needed.
  854.      */
  855.     if(func & removeCheck)
  856.         XTileSet(menu->menuWindow, x, y, checkMarkWidth,
  857.          checkMarkHeight, menu->menuBgTile);
  858.     /*
  859.      * Call Draw_Item if we need to draw or dim the item.
  860.      */
  861.     if((x = func & dimItem) || (func & drawItem))
  862.         Draw_Item(menu, item, top, x);
  863.     /*
  864.      * Update state flags.
  865.      */
  866.     SetStateFlags(item);
  867. }
  868.  
  869. /*
  870.  * Draw the item (less check mark) at vertical position y.
  871.  * Dim the item if "dim" is set.
  872.  */
  873. static Draw_Item(menu, item, y, dim)
  874. register Menu *menu;
  875. register MenuItem *item;
  876. register int y;
  877. int  dim;
  878. {
  879.     register int x = 2 * menu->menuItemPad + checkMarkWidth;
  880.     register int center = y + item->itemHeight / 2;
  881.  
  882.     /*
  883.      * If the item text is a single dash, draw a separating line.
  884.      */
  885.     if(strcmp(item->itemText, "-") == 0) {
  886.         XLine(menu->menuWindow, 0, center, menu->menuWidth, center,
  887.          1, 1, menu->menuFgColor, GXcopy, AllPlanes);
  888.         return;
  889.     }
  890.     /*
  891.      * Draw and/or dim the text, centered vertically.
  892.      */
  893.     y = center - (menu->menuFontInfo->height / 2);
  894.     if(dim) {
  895.         XTileSet(menu->menuWindow, x, y, item->itemTextWidth,
  896.          menu->menuFontInfo->height, Gray_Tile);
  897.         XTextPad(menu->menuWindow, x, y, item->itemText,
  898.          item->itemTextLength, menu->menuFontInfo->id, 0, 0,
  899.          menu->menuFgColor, menu->menuBgColor, menu->menuFgColor ?
  900.          GXand : GXor, AllPlanes);
  901.     } else
  902.         XText(menu->menuWindow, x, y, item->itemText,
  903.          item->itemTextLength, menu->menuFontInfo->id,
  904.          menu->menuFgColor, menu->menuBgColor);
  905. }
  906.  
  907. /*
  908.  * Determine which enabled menu item the mouse is currently in.  Return the
  909.  * top position of this item and its item number.  Set inwindow to whether
  910.  * we are or not.
  911.  */
  912. static MenuItem *Mouse_InItem(menu, top, n, inwindow)
  913. register Menu *menu;
  914. int *top, *n, *inwindow;
  915. {
  916.     int x, y;
  917.     Window subw;
  918.     static MenuItem *Y_InItem();
  919.  
  920.     /*
  921.      * Find out where the mouse is.  If its not in the menu window,
  922.      * return NULL.
  923.      */
  924.     XQueryMouse(RootWindow, &x, &y, &subw);
  925.     if(subw != menu->menuWindow) {
  926.         *inwindow = FALSE;
  927.         return((MenuItem *)0);
  928.     }
  929.     *inwindow = TRUE;
  930.     /*
  931.      * Now get the coordinates relative to the menu window.
  932.      */
  933.     XInterpretLocator(menu->menuWindow, &x, &y, &subw, (x << 16) | y);
  934.     /*
  935.      * Call Y_InItem().
  936.      */
  937.     *top = y;
  938.     return(Y_InItem(menu, top, n));
  939. }
  940.  
  941. /*
  942.  * Return which enabled item the locator is in.  Also return the
  943.  * top position of this item and its item number.  Initial y passed
  944.  * in top.
  945.  */
  946. static MenuItem *Y_InItem(menu, top, n)
  947. register Menu *menu;
  948. int *top, *n;
  949. {
  950.     register MenuItem *item;
  951.     register int t, i;
  952.     register int y = *top;
  953.     Window subw;
  954.  
  955.     /*
  956.      * Go through the item list.  "t" is the vertical position of the
  957.      * current item and "i" is its item number.
  958.      */
  959.     t = menu->menuItemTop;
  960.     /*
  961.      * If the mouse is before the first item, return.
  962.      */
  963.     if(y < t)
  964.         return((MenuItem *)0);
  965.     for(i = 0, item = menu->menuItems ; item ; i++, item = item->nextItem) {
  966.         /*
  967.          * If the y coordinate is within this menu item, then return.
  968.          * But don't return disable items.
  969.          */
  970.         if(t + item->itemHeight > y) {
  971.             if(item->itemFlags & itemDisabled)
  972.                 return((MenuItem *)0);
  973.             *top = t;
  974.             *n = i;
  975.             return(item);
  976.         }
  977.         t += item->itemHeight;
  978.     }
  979.     /*
  980.      * Should never get here.
  981.      */
  982.     return((MenuItem *)0);
  983. }
  984.  
  985. /*
  986.  * Unmap_Menu() unmaps a menu, if it is currently mapped.
  987.  */
  988. static Unmap_Menu(menu)
  989. register Menu *menu;
  990. {
  991.     register int i;
  992.  
  993.     if(!menu || !(menu->menuFlags & menuMapped))
  994.         return;
  995.     if(menu->menuFlags & menuFreeze) {
  996.         XUnmapTransparent(menu->menuWindow);
  997.         i = 2 * menu->menuBorderWidth;
  998.         XPixmapPut(RootWindow, 0, 0, menu->menuSavedImageX,
  999.          menu->menuSavedImageY, menu->menuWidth + i,
  1000.          menu->menuHeight + i, menu->menuSavedImage,
  1001.          GXcopy, AllPlanes);
  1002.         XFreePixmap(menu->menuSavedImage);
  1003.         XUngrabServer();
  1004.     } else
  1005.         XUnmapWindow(menu->menuWindow);
  1006.     menu->menuFlags &= ~menuMapped;
  1007. }
  1008. #endif MODEMENU
  1009.