home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / stdwin / Ports / x11 / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-30  |  11.6 KB  |  621 lines  |  [TEXT/????]

  1. /* X11 STDWIN -- Menu handling */
  2.  
  3. #include "x11.h"
  4. #include "llevent.h" /* For _w_dirty */
  5.  
  6. static bool local; /* When zero, menus are created global */
  7.  
  8. static int nmenus;
  9. static MENU **menulist; /* Only global menus */
  10.  
  11. static MENU *sysmenu; /* System menu, included in each window's mbar */
  12.  
  13. static
  14. mbardirty(win)
  15.     WINDOW *win;
  16. {
  17.     _w_dirty= win->mbar.dirty= TRUE;
  18. }
  19.  
  20. void
  21. wmenusetdeflocal(flag)
  22.     bool flag;
  23. {
  24.     local= flag;
  25. }
  26.  
  27. void
  28. wmenuattach(win, mp)
  29.     WINDOW *win;
  30.     MENU *mp;
  31. {
  32.     int i;
  33.     
  34.     for (i= 0; i < win->nmenus; ++i) {
  35.         if (win->menulist[i] == mp)
  36.             return; /* Already attached */
  37.     }
  38.     L_APPEND(win->nmenus, win->menulist, MENU *, mp);
  39.     mbardirty(win);
  40. }
  41.  
  42. void
  43. wmenudetach(win, mp)
  44.     WINDOW *win;
  45.     MENU *mp;
  46. {
  47.     int i;
  48.     
  49.     for (i= 0; i < win->nmenus; ++i) {
  50.         if (win->menulist[i] == mp) {
  51.             L_REMOVE(win->nmenus, win->menulist, MENU *, i);
  52.             mbardirty(win);
  53.             break;
  54.         }
  55.     }
  56. }
  57.  
  58. _waddmenus(win)
  59.     WINDOW *win;
  60. {
  61.     int i;
  62.     
  63.     /* Create system menu if first time here */
  64.     if (sysmenu == NULL) {
  65.         bool savelocal= local;
  66.         wmenusetdeflocal(TRUE);
  67.         sysmenu= wmenucreate(0, "X");
  68.         if (sysmenu != NULL)
  69.             wmenuadditem(sysmenu, "Close", -1);
  70.         wmenusetdeflocal(savelocal);
  71.     }
  72.     
  73.     /* Initialize the nonzero variables used by the menu module */
  74.     win->curmenu= win->curitem= -1;
  75.     
  76.     /* Add system menu and all global menus to the menu list */
  77.     if (sysmenu != NULL) {
  78.         L_APPEND(win->nmenus, win->menulist, MENU *, sysmenu);
  79.     }
  80.     for (i= 0; i < nmenus; ++i) {
  81.         L_APPEND(win->nmenus, win->menulist, MENU *, menulist[i]);
  82.     }
  83.     if (nmenus > 0)
  84.         mbardirty(win);
  85. }
  86.  
  87. _w_delmenus(win)
  88.     WINDOW *win;
  89. {
  90.     /* Delete the menu list before closing a window */
  91.     L_DEALLOC(win->nmenus, win->menulist);
  92. }
  93.  
  94. MENU *
  95. wmenucreate(id, title)
  96.     int id;
  97.     char *title;
  98. {
  99.     MENU *mp= ALLOC(MENU);
  100.     
  101.     if (mp == NULL)
  102.         return NULL;
  103.     mp->id= id;
  104.     mp->title= strdup(title);
  105.     mp->nitems= 0;
  106.     mp->itemlist= NULL;
  107.     if (!local) {
  108.         L_APPEND(nmenus, menulist, MENU *, mp);
  109.         _waddtoall(mp);
  110.     }
  111.     return mp;
  112. }
  113.  
  114. void
  115. wmenudelete(mp)
  116.     MENU *mp;
  117. {
  118.     int i;
  119.     
  120.     for (i= 0; i < nmenus; ++i) {
  121.         if (menulist[i] == mp) {
  122.             L_REMOVE(nmenus, menulist, MENU *, i);
  123.             break;
  124.         }
  125.     }
  126.     _wdelfromall(mp);
  127.     
  128.     for (i= 0; i < mp->nitems; ++i)
  129.         FREE(mp->itemlist[i].text);
  130.     FREE(mp->itemlist);
  131.     FREE(mp);
  132. }
  133.  
  134. int
  135. wmenuadditem(mp, text, shortcut)
  136.     MENU *mp;
  137.     char *text;
  138.     int shortcut;
  139. {
  140.     struct item it;
  141.     
  142.     it.text= strdup(text);
  143.     if (shortcut < 0)
  144.         it.sctext= NULL;
  145.     else {
  146.         char buf[50];
  147.         sprintf(buf, "M-%c", shortcut);
  148.         it.sctext= strdup(buf);
  149.     }
  150.     it.shortcut= shortcut;
  151.     it.enabled=  it.text != NULL && it.text[0] != EOS;
  152.     it.checked= FALSE;
  153.     L_APPEND(mp->nitems, mp->itemlist, struct item, it);
  154.     return mp->nitems - 1;
  155. }
  156.  
  157. void
  158. wmenusetitem(mp, i, text)
  159.     MENU *mp;
  160.     int i;
  161.     char *text;
  162. {
  163.     if (i < 0 || i >= mp->nitems)
  164.         return;
  165.     FREE(mp->itemlist[i].text);
  166.     mp->itemlist[i].text= strdup(text);
  167. }
  168.  
  169. void
  170. wmenuenable(mp, i, flag)
  171.     MENU *mp;
  172.     int i;
  173.     bool flag;
  174. {
  175.     if (i < 0 || i >= mp->nitems)
  176.         return;
  177.     mp->itemlist[i].enabled= flag;
  178. }
  179.  
  180. void
  181. wmenucheck(mp, i, flag)
  182.     MENU *mp;
  183.     int i;
  184.     bool flag;
  185. {
  186.     if (i < 0 || i >= mp->nitems)
  187.         return;
  188.     mp->itemlist[i].checked= flag;
  189. }
  190.  
  191. /* --- system-dependent code starts here --- */
  192.  
  193. /*
  194. XXX Stuff recomputed more than I like:
  195.     - length of menu titles and menu item texts
  196.     - menu item text widths
  197. */
  198.  
  199. #define NOITEM (-1)
  200. #define NOMENU (-1)
  201.  
  202. /* Precomputed values (assume _wdrawmbar is always called first) */
  203. static int baseline;
  204. static int lineheight;
  205. static int halftitledist;
  206. #define titledist (2*halftitledist)
  207. static int shortcutdist;
  208.  
  209. static
  210. _wmenusetup()
  211. {
  212.     shortcutdist= XTextWidth(_wmf, "    ", 4);
  213.     halftitledist= XTextWidth(_wmf, "0", 1);
  214.     baseline= _wmf->ascent;
  215.     lineheight= _wmf->ascent + _wmf->descent;
  216. }
  217.  
  218. /* Draw the menu bar.
  219.    Called via the normal exposure event mechanism. */
  220.  
  221. _wdrawmbar(win)
  222.     WINDOW *win;
  223. {
  224.     int i;
  225.     int x;
  226.     int y;
  227.  
  228.     if (win->mbar.wid == None)
  229.         return;
  230.     _wmenusetup();
  231.     x= titledist;
  232.     y= baseline - 1 + (win->mbar.height - lineheight) / 2;
  233.     
  234.     XClearWindow(_wd, win->mbar.wid);
  235.     for (i= 0; i < win->nmenus; ++i) {
  236.         char *title= win->menulist[i]->title;
  237.         int len= strlen(title);
  238.         XDrawString(_wd, win->mbar.wid, win->gc, x, y, title, len);
  239.         x += XTextWidth(_wmf, title, len) + titledist;
  240.     }
  241. }
  242.  
  243. static
  244. inverttitle(win, it)
  245.     WINDOW *win;
  246.     int it;
  247. {
  248.     int x= leftedge(win, it);
  249.     MENU *mp= win->menulist[it];
  250.     char *title= mp->title;
  251.     int len= strlen(title);
  252.     int width= XTextWidth(_wmf, title, len);
  253.     
  254.     _winvert(win->mbar.wid, win->gc,
  255.         x-halftitledist, 0, width+titledist, win->mbar.height);
  256. }
  257.  
  258. static int
  259. leftedge(win, it)
  260.     WINDOW *win;
  261.     int it;
  262. {
  263.     int i;
  264.     int x= titledist;
  265.     
  266.     for (i= 0; i < it; ++i) {
  267.         char *title= win->menulist[i]->title;
  268.         int len= strlen(title);
  269.         int width= XTextWidth(_wmf, title, len);
  270.         x += width + titledist;
  271.     }
  272.     
  273.     return x;
  274. }
  275.  
  276. /* Draw the current menu */
  277.  
  278. static
  279. drawmenu(win)
  280.     WINDOW *win;
  281. {
  282.     int i;
  283.     int x= halftitledist;
  284.     int y= baseline;
  285.     MENU *mp= win->menulist[win->curmenu];
  286.     
  287.     for (i= 0; i < mp->nitems; ++i) {
  288.         char *text= mp->itemlist[i].text;
  289.         int len= strlen(text);
  290.         char *marker= NULL;
  291.         if (!mp->itemlist[i].enabled) {
  292.             if (len > 0)
  293.                 marker= "-";
  294.         }
  295.         else if (mp->itemlist[i].checked)
  296.             marker= "*";
  297.         if (marker != NULL) {
  298.             int width= XTextWidth(_wmf, marker, 1);
  299.             XDrawString(_wd, win->mwin.wid, win->gc,
  300.                 (x-width)/2, y, marker, 1);
  301.         }
  302.         XDrawString(_wd, win->mwin.wid, win->gc, x, y, text, len);
  303.         text= mp->itemlist[i].sctext;
  304.         if (text != NULL) {
  305.             int width;
  306.             len= strlen(text);
  307.             width= XTextWidth(_wmf, text, len);
  308.             XDrawString(_wd, win->mwin.wid, win->gc,
  309.                 win->mwin.width - width - halftitledist, y,
  310.                 text, len);
  311.         }
  312.         y += lineheight;
  313.     }
  314. }
  315.  
  316. /* Create the window for the menu.
  317.    It is a direct child of the outer window, but aligned with the
  318.    top of the inner window */
  319.  
  320. static
  321. makemenu(win)
  322.     WINDOW *win;
  323. {
  324.     int i;
  325.     int maxwidth= 0;
  326.     MENU *mp= win->menulist[win->curmenu];
  327.     Window child_dummy;
  328.     
  329.     /* Compute the maximum item width.
  330.        Item width is the sum of:
  331.        - 1/2 title width (left margin, also used for tick marks)
  332.        - text width of item text
  333.        - if there is a shortcut:
  334.            - shortcutdist (some space between text and shortcut)
  335.            - text width of shortcut text
  336.        - 1/2 title width (right margin)
  337.     */
  338.  
  339.     for (i= 0; i < mp->nitems; ++i) {
  340.         char *text= mp->itemlist[i].text;
  341.         int len= strlen(text);
  342.         int width= XTextWidth(_wmf, text, len);
  343.         text= mp->itemlist[i].sctext;
  344.         if (text != NULL) {
  345.             len= strlen(text);
  346.             width += XTextWidth(_wmf, text, len) + shortcutdist;
  347.         }
  348.         if (width > maxwidth)
  349.             maxwidth= width;
  350.     }
  351.     
  352.     win->mwin.width= maxwidth + titledist;
  353.     win->mwin.height= mp->nitems * lineheight + 1;
  354.     CLIPMAX(win->mwin.width, WidthOfScreen(_ws));
  355.     CLIPMAX(win->mwin.height, HeightOfScreen(_ws));
  356.     
  357.     if (!XTranslateCoordinates(_wd, win->wo.wid,
  358.         RootWindowOfScreen(_ws),
  359.         leftedge(win, win->curmenu) -halftitledist + win->mbar.x,
  360.         win->wi.y,
  361.         &win->mwin.x,
  362.         &win->mwin.y,
  363.         &child_dummy)) {
  364.         
  365.         _wwarning("makemenu: XTranslateCoordinates failed");
  366.         win->mwin.x = win->mwin.y = 0;
  367.     }
  368.     
  369.     CLIPMAX(win->mwin.x, WidthOfScreen(_ws) - win->mwin.width);
  370.     CLIPMAX(win->mwin.y, HeightOfScreen(_ws) - win->mwin.height);
  371.     CLIPMIN(win->mwin.x, 0);
  372.     CLIPMIN(win->mwin.y, 0);
  373.     
  374.     win->mwin.dirty= TRUE;
  375.     win->mwin.border= IBORDER;
  376.     
  377.     (void) _wcreate1(&win->mwin, RootWindowOfScreen(_ws), XC_arrow, FALSE,
  378.         win->fgo, win->bgo, 1);
  379.     _wsaveunder(&win->mwin, True);
  380.     XMapWindow(_wd, win->mwin.wid);
  381.     i = XGrabPointer(_wd,        /* display */
  382.         win->mwin.wid,        /* grab_window */
  383.         False,            /* owner_events */
  384.         ButtonMotionMask | ButtonReleaseMask,
  385.                     /* event_masks */
  386.         GrabModeAsync,        /* pointer_mode */
  387.         GrabModeAsync,        /* keyboard_mode */
  388.         None,            /* confine_to */
  389.         None,            /* cursor */
  390.         _w_lasttime        /* timestamp */
  391.         );
  392.     if (i != GrabSuccess) {
  393.         _wwarning("makemenu: XGrabPointer failed (err %d)", i);
  394.         /* Didn't get the grab -- forget about it */
  395.     }
  396. }
  397.  
  398. /* Handle mouse state change in menu bar */
  399.  
  400. _whitmbar(bsp, ep)
  401.     struct button_state *bsp;
  402.     EVENT *ep;
  403. {
  404.     WINDOW *win= bsp->win;
  405.  
  406.     if (win->curmenu >= 0) {
  407.         /* We already created a menu.
  408.            This is probably an event that was queued before
  409.            the menu window was created. */
  410.         _wdebug(1, "_whitmbar: mouse in mwin");
  411.     }
  412.     
  413.     if (!bsp->down)
  414.         hidemenu(win);
  415.     else if (bsp->y >= 0 && bsp->y <= win->mbar.height &&
  416.             bsp->x >= 0 && bsp->x <= win->mbar.width)
  417.         showmenu(win, whichmenu(win, bsp->x));
  418. }
  419.  
  420. /* Handle mouse state change in menu.
  421.    (This is called with a fake button state from _whitmbar,
  422.    since the menu bar has grabbed the pointer) */
  423.  
  424. /*static*/ /* Now called from event.c */
  425. _whitmwin(bsp, ep)
  426.     struct button_state *bsp;
  427.     EVENT *ep;
  428. {
  429.     WINDOW *win= bsp->win;
  430.     MENU *mp= win->menulist[win->curmenu];
  431.     int it;
  432.     
  433.     if (bsp->x >= 0 && bsp->x <= win->mwin.width)
  434.         it= whichitem(mp, bsp->y);
  435.     else
  436.         it= NOITEM;
  437.     _wdebug(5, "_whitmwin: hit item %d", it);
  438.     hiliteitem(win, it);
  439.     
  440.     if (!bsp->down) {
  441.         hidemenu(win);
  442.         XFlush(_wd); /* Show it right now */
  443.         if (it >= 0) {
  444.             if (mp->id == 0) {
  445.                 ep->type= WE_CLOSE;
  446.                 ep->window= win;
  447.             }
  448.             else {
  449.                 ep->type= WE_MENU;
  450.                 ep->u.m.id= mp->id;
  451.                 ep->u.m.item= it;
  452.                 ep->window= win;
  453.             }
  454.         }
  455.     }
  456. }
  457.  
  458. /* Show and hide menus */
  459.  
  460. static
  461. showmenu(win, newmenu)
  462.     WINDOW *win;
  463.     int newmenu;
  464. {
  465.     if (newmenu != win->curmenu) {
  466.         hidemenu(win);
  467.         if (newmenu >= 0) {
  468.             win->curmenu= newmenu;
  469.             win->curitem= NOITEM;
  470.             inverttitle(win, win->curmenu);
  471.             makemenu(win);
  472.             drawmenu(win);
  473.         }
  474.     }
  475. }
  476.  
  477. static
  478. hidemenu(win)
  479.     WINDOW *win;
  480. {
  481.     if (win->curmenu >= 0) {
  482.         hiliteitem(win, NOITEM);
  483.         XDestroyWindow(_wd, win->mwin.wid);
  484.         win->mwin.wid= 0;
  485.         inverttitle(win, win->curmenu);
  486.         win->curmenu= NOMENU;
  487.     }
  488. }
  489.  
  490. /* Compute which menu was hit */
  491.  
  492. static int
  493. whichmenu(win, xhit)
  494.     WINDOW *win;
  495.     int xhit;
  496. {
  497.     int i;
  498.     int x= halftitledist;
  499.     
  500.     if (xhit < x)
  501.         return NOMENU;
  502.     
  503.     for (i= 0; i < win->nmenus; ++i) {
  504.         char *title= win->menulist[i]->title;
  505.         int len= strlen(title);
  506.         x += XTextWidth(_wmf, title, len) + titledist;
  507.         if (xhit < x)
  508.             return i;
  509.     }
  510.     
  511.     return NOMENU;
  512. }
  513.  
  514. /* (Un)hilite the given menu item */
  515.  
  516. static
  517. hiliteitem(win, it)
  518.     WINDOW *win;
  519.     int it;
  520. {
  521.     if (it != win->curitem) {
  522.         if (win->curitem >= 0)
  523.             invertitem(win, win->curitem);
  524.         if (it >= 0)
  525.             invertitem(win, it);
  526.         win->curitem= it;
  527.     }
  528. }
  529.  
  530. static
  531. invertitem(win, it)
  532.     WINDOW *win;
  533.     int it;
  534. {
  535.     int top, size;
  536.     
  537.     size= lineheight;
  538.     top= it*size;
  539.     _winvert(win->mwin.wid, win->gc, 0, top, win->mwin.width, size);
  540. }
  541.  
  542. /* Compute which item was hit */
  543.  
  544. static int
  545. whichitem(mp, yhit)
  546.     MENU *mp;
  547.     int yhit;
  548. {
  549.     int it= yhit < 0 ? NOITEM : yhit / lineheight;
  550.     
  551.     _wdebug(4, "whichitem: yhit=%d, it=%d", yhit, it);
  552.     if (it >= 0 && it < mp->nitems && mp->itemlist[it].enabled)
  553.         return it;
  554.     else
  555.         return NOITEM;
  556. }
  557.  
  558. /* Generate a menu selection event from a meta-key press.
  559.    *ep has the window already filled in. */
  560.  
  561. bool
  562. _w_menukey(c, ep)
  563.     int c;
  564.     EVENT *ep;
  565. {
  566.     WINDOW *win= ep->window;
  567.     int i;
  568.     int altc;
  569.     bool althit= FALSE;
  570.     
  571.     c &= 0xff;
  572.     
  573.     if (islower(c))
  574.         altc= toupper(c);
  575.     else if (isupper(c))
  576.         altc= tolower(c);
  577.     else
  578.         altc= 0;
  579.     
  580.     for (i= 0; i < win->nmenus; ++i) {
  581.         MENU *mp= win->menulist[i];
  582.         int j;
  583.         for (j= 0; j < mp->nitems; ++j) {
  584.             if (mp->itemlist[j].shortcut == c) {
  585.                 ep->type= WE_MENU;
  586.                 ep->u.m.id= mp->id;
  587.                 ep->u.m.item= j;
  588.                 return TRUE;
  589.             }
  590.             else if (altc != 0 && !althit &&
  591.                 mp->itemlist[j].shortcut == altc) {
  592.                 ep->u.m.id= mp->id;
  593.                 ep->u.m.item= j;
  594.                 althit= TRUE;
  595.             }
  596.         }
  597.     }
  598.     if (althit)
  599.         ep->type= WE_MENU;
  600.     else {
  601.         /* Return a WE_KEY event with the meta bit set */
  602.         ep->type = WE_KEY;
  603.         ep->u.key.code = c;
  604.         ep->u.key.mask = WM_META;
  605.     }
  606.     return TRUE;
  607. }
  608.  
  609. /* Delete all menus -- called by wdone().
  610.    (In fact local menus aren't deleted since they aren't in the menu list). */
  611.  
  612. _wkillmenus()
  613. {
  614.     while (nmenus > 0)
  615.         wmenudelete(menulist[nmenus-1]);
  616.     if (sysmenu != NULL) {
  617.         wmenudelete(sysmenu);
  618.         sysmenu= NULL;
  619.     }
  620. }
  621.