home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 110 / EnigmaAmiga110CD.iso / indispensabili / utility / apdf / xpdf-0.80 / ltk / ltkmenu.cc < prev    next >
C/C++ Source or Header  |  1998-11-27  |  11KB  |  408 lines

  1. //========================================================================
  2. //
  3. // LTKMenu.cc
  4. //
  5. // Menus and menu items.
  6. //
  7. // Copyright 1997 Derek B. Noonburg.
  8. //
  9. //========================================================================
  10.  
  11. #ifdef __GNUC__
  12. #pragma implementation
  13. #endif
  14.  
  15. #include <stdlib.h>
  16. #include <stdarg.h>
  17. #include <stddef.h>
  18. #include <X11/Xlib.h>
  19. #include <X11/Xutil.h>
  20. #include <X11/cursorfont.h>
  21. #include "gmem.h"
  22. #include "LTKConfig.h"
  23. #include "LTKApp.h"
  24. #include "LTKWindow.h"
  25. #include "LTKBorder.h"
  26. #include "LTKMenu.h"
  27.  
  28. #ifdef XlibSpecificationRelease
  29. #if XlibSpecificationRelease < 5
  30. typedef char *XPointer;
  31. #endif
  32. #else
  33. typedef char *XPointer;
  34. #endif
  35.  
  36. //------------------------------------------------------------------------
  37.  
  38. #define horizBorder  2
  39. #define horizSpace  12
  40. #define arrowSize   10
  41.  
  42. //------------------------------------------------------------------------
  43. // menu design
  44. // -----------
  45. //
  46. // X coordinates:
  47. //
  48. //    /-- main border
  49. //    | /-- selection border
  50. //    | |               /-- shortcut
  51. //    | |               |    /-- submenu arrow
  52. //    | |               |    |   /-- selection border
  53. //    | |               |    |   | /-- main border
  54. //    | |               |    |   | |
  55. //    v v               v    v   v v  
  56. //   | |                          | |
  57. //   | +--------------------------+ |
  58. //   | | +----------------------+ | |
  59. //   | | | ...text...        |> | | |
  60. //   | | +----------------------+ | |
  61. //   | +--------------------------+ |
  62. //   | | +----------------------+ | |
  63. //   | | | ...text...  shortcut | | |
  64. //   | | +----------------------+ | |
  65. //   | +--------------------------+ |
  66. //   | |                          | |
  67. //   A B C D        E  F      G H I J
  68. //   
  69. //   A = 0
  70. //   B = A + ltkBorderWidth
  71. //   C = B + ltkBorderWidth
  72. //   D = C + horizBorder
  73. //   E = D + max{text widths}
  74. //   F = E + horizSpace
  75. //   G = F + max{arrowSize, shortcut widths}
  76. //   H = G + horizBorder
  77. //   I = H + ltkBorderWidth
  78. //   J = I + ltkBorderWidth
  79. //
  80. //   total width = J
  81. //               = 4*ltkBorderWidth + 2*horizBorder + horizSpace
  82. //                 + max{text widths}
  83. //                 + max{arrowSize, shortcut widths}
  84. //
  85. //------------------------------------------------------------------------
  86.  
  87. static Bool isExposeEvent(Display *display, XEvent *e, XPointer w);
  88.  
  89. //------------------------------------------------------------------------
  90. // LTKMenu
  91. //------------------------------------------------------------------------
  92.  
  93. LTKMenu::LTKMenu(char *title1, int numItems1, ...) {
  94.   va_list args;
  95.   int i;
  96.  
  97.   title = title1;
  98.   numItems = numItems1;
  99.   items = (LTKMenuItem **)gmalloc(numItems * sizeof(LTKMenuItem *));
  100.   va_start(args, numItems1);
  101.   for (i = 0; i < numItems; ++i)
  102.     items[i] = va_arg(args, LTKMenuItem *);
  103.   xwin = None;
  104. }
  105.  
  106. LTKMenu::~LTKMenu() {
  107.   int i;
  108.  
  109.   if (xwin != None)
  110.     unpost();
  111.   for (i = 0; i < numItems; ++i)
  112.     delete items[i];
  113.   gfree(items);
  114. }
  115.  
  116. void LTKMenu::post(LTKWindow *win1, int x1, int y1, LTKMenu *parent1) {
  117.   XFontStruct *fontStruct;
  118.   XCharStruct extents;
  119.   XSetWindowAttributes attr;
  120.   XEvent event;
  121.   int w, h, shortcutW, direction, ascent, descent;
  122.   int i;
  123.   GBool haveSubmenus;
  124.  
  125.   // parent menu
  126.   parent = parent1;
  127.  
  128.   // parent window
  129.   win = win1;
  130.   display = win->getDisplay();
  131.   fgGC = win->getFgGC();
  132.   bgGC = win->getBgGC();
  133.   brightGC = win->getBrightGC();
  134.   darkGC = win->getDarkGC();
  135.   fontStruct = win->getXFontStruct();
  136.  
  137.   // compute width and height
  138.   width = 0;
  139.   height = 0;
  140.   itemHeight = fontStruct->ascent + fontStruct->descent;
  141.   if (itemHeight < arrowSize)
  142.     itemHeight = arrowSize;
  143.   if (title) {
  144.     XTextExtents(fontStruct, title, strlen(title),
  145.          &direction, &ascent, &descent, &extents);
  146.     if (extents.width > width)
  147.       width = extents.width;
  148.     height += itemHeight + ltkBorderWidth + 2 + 2 * ltkBorderWidth;
  149.   }
  150.   shortcutW = 0;
  151.   haveSubmenus = gFalse;
  152.   for (i = 0; i < numItems; ++i) {
  153.     if (items[i]->text) {
  154.       XTextExtents(fontStruct, items[i]->text, strlen(items[i]->text),
  155.            &direction, &ascent, &descent, &extents);
  156.       if (extents.width > width)
  157.     width = extents.width;
  158.       height += itemHeight + ltkBorderWidth;
  159.     } else {
  160.       height += 2 + ltkBorderWidth;
  161.     }
  162.     if (items[i]->submenu) {
  163.       haveSubmenus = gTrue;
  164.     } else if (items[i]->shortcut) {
  165.       XTextExtents(fontStruct, items[i]->shortcut, strlen(items[i]->shortcut),
  166.            &direction, &ascent, &descent, &extents);
  167.       if (extents.width > shortcutW)
  168.     shortcutW = extents.width;
  169.     }
  170.   }
  171.   width += 2*ltkBorderWidth + horizBorder + horizSpace;
  172.   if (!haveSubmenus || shortcutW > arrowSize)
  173.     width += shortcutW;
  174.   else 
  175.     width += arrowSize;
  176.   width += 2*ltkBorderWidth + horizBorder;
  177.   height += 3*ltkBorderWidth;
  178.  
  179.   // compute position
  180.   w = win->getApp()->getDisplayWidth();
  181.   h = win->getApp()->getDisplayHeight();
  182.   x = x1;
  183.   if (x + width > w)
  184.     x = w - width;
  185.   if (x < 0)
  186.     x = 0;
  187.   y = y1;
  188.   if (y + height > h)
  189.     y = h - height;
  190.   if (y < 0)
  191.     y = 0;
  192.  
  193.   // create X window
  194.   attr.background_pixel = win->getBgColor();
  195.   attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;
  196.   attr.override_redirect = True;
  197.   xwin = XCreateWindow(display,
  198.                RootWindow(display, win->getScreenNum()),
  199.                x, y, width, height, 0, CopyFromParent,
  200.                InputOutput, CopyFromParent,
  201.                CWBackPixel | CWEventMask | CWOverrideRedirect,
  202.                &attr);
  203.  
  204.   // map it
  205.   XMapWindow(display, xwin);
  206.   XPeekIfEvent(display, &event, &isExposeEvent, (XPointer)this);
  207.  
  208.   // grab the pointer
  209.   XGrabPointer(display, xwin, False,
  210.            ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
  211.              PointerMotionHintMask,
  212.            GrabModeAsync, GrabModeAsync, None,
  213.            XCreateFontCursor(display, XC_sb_left_arrow), CurrentTime);
  214.  
  215.   // register with app
  216.   win->getApp()->setMenu(this);
  217.  
  218.   // no item selected yet
  219.   currentItem = -1;
  220.   currentSubmenu = NULL;
  221. }
  222.  
  223. static Bool isExposeEvent(Display *display, XEvent *e, XPointer w) {
  224.   return e->type == Expose &&
  225.          e->xexpose.window == ((LTKMenu *)w)->getXWindow();
  226. }
  227.  
  228. void LTKMenu::repost() {
  229.   // grab the pointer
  230.   XGrabPointer(display, xwin, False,
  231.            ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
  232.              PointerMotionHintMask,
  233.            GrabModeAsync, GrabModeAsync, None,
  234.            XCreateFontCursor(display, XC_sb_left_arrow), CurrentTime);
  235.  
  236.   // register with app
  237.   win->getApp()->setMenu(this);
  238. }
  239.  
  240. void LTKMenu::unpost() {
  241.   XEvent event;
  242.  
  243.   // unpost any submenus
  244.   if (currentSubmenu) {
  245.     currentSubmenu->unpost();
  246.     currentSubmenu = NULL;
  247.   }
  248.  
  249.   // let go of the pointer
  250.   XUngrabPointer(display, CurrentTime);
  251.  
  252.   // kill the window
  253.   XUnmapWindow(display, xwin);
  254.   XDestroyWindow(display, xwin);
  255.  
  256.   // flush any leftover events
  257.   XSync(display, False);
  258.   while (XCheckWindowEvent(display, xwin, 0xffffffff, &event)) ;
  259.  
  260.   // mark the menu as unposted
  261.   xwin = None;
  262.   win->getApp()->setMenu(NULL);
  263. }
  264.  
  265. void LTKMenu::done() {
  266.   if (parent)
  267.     parent->done();
  268.   else
  269.     unpost();
  270. }
  271.  
  272. void LTKMenu::redraw() {
  273.   XCharStruct extents;
  274.   int direction, ascent, descent;
  275.   int textBase, y1, x2, y2;
  276.   int i;
  277.  
  278.   // draw menu border
  279.   ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
  280.         0, 0, width, height, ltkBorderRaised);
  281.  
  282.   // draw title
  283.   textBase = win->getXFontStruct()->ascent;
  284.   y1 = 2 * ltkBorderWidth;
  285.   if (title) {
  286.     XTextExtents(win->getXFontStruct(), title, strlen(title),
  287.          &direction, &ascent, &descent, &extents);
  288.     x2 = (width - extents.width) / 2;
  289.     XDrawString(display, xwin, fgGC,
  290.         x2, y1 + textBase, title, strlen(title));
  291.     y1 += itemHeight + ltkBorderWidth;
  292.     ltkDrawSplitBorder(display, xwin, brightGC, darkGC, bgGC,
  293.                0, y1, width, 0, ltkBorderSunken);
  294.     y1 += 2 + 2 * ltkBorderWidth;
  295.   }
  296.  
  297.   // draw items
  298.   for (i = 0; i < numItems; ++i) {
  299.     if (items[i]->text) {
  300.       XDrawString(display, xwin, fgGC,
  301.           2*ltkBorderWidth + horizBorder, y1 + textBase,
  302.           items[i]->text, strlen(items[i]->text));
  303.       if (items[i]->submenu) {
  304.     x2 = width - 2*ltkBorderWidth - horizBorder - arrowSize;
  305.     y2 = y1 + (itemHeight - arrowSize) / 2;
  306.     ltkDrawTriBorder(display, xwin, brightGC, darkGC, bgGC,
  307.              x2, y2, arrowSize, arrowSize,
  308.              ltkTriRight, ltkBorderRaised);
  309.       } else if (items[i]->shortcut) {
  310.     XTextExtents(win->getXFontStruct(),
  311.              items[i]->shortcut, strlen(items[i]->shortcut),
  312.              &direction, &ascent, &descent, &extents);
  313.     x2 = width - 2*ltkBorderWidth - horizBorder - extents.width;
  314.     XDrawString(display, xwin, fgGC, x2, y1 + textBase,
  315.             items[i]->shortcut, strlen(items[i]->shortcut));
  316.       }
  317.       y1 += itemHeight + ltkBorderWidth;
  318.     } else {
  319.       ltkDrawDivider(display, xwin, brightGC, darkGC, bgGC,
  320.              2*ltkBorderWidth + horizBorder - 1, y1,
  321.              width - 4*ltkBorderWidth - 2*horizBorder + 2, 0,
  322.              ltkBorderRaised);
  323.       y1 += 2 + ltkBorderWidth;
  324.     }
  325.   }
  326. }
  327.  
  328. void LTKMenu::buttonPress(int mx, int my, int button, GBool dblClick) {
  329.   done();
  330.   if (currentItem >= 0 && items[currentItem]->cbk)
  331.     (*items[currentItem]->cbk)(items[currentItem]);
  332. }
  333.  
  334. void LTKMenu::buttonRelease(int mx, int my, int button, GBool click) {
  335.   if (!click) {
  336.     done();
  337.     if (currentItem >= 0 && items[currentItem]->cbk)
  338.       (*items[currentItem]->cbk)(items[currentItem]);
  339.   }
  340. }
  341.  
  342. void LTKMenu::mouseMove(int mx, int my, int btn) {
  343.   int y1, i, j;
  344.  
  345.   y1 = ltkBorderWidth;
  346.   if (title)
  347.     y1 += itemHeight + ltkBorderWidth + 2 + 2 * ltkBorderWidth;
  348.   j = -1;
  349.   if (mx >= 0 && mx < width && my >= y1) {
  350.     for (i = 0; i < numItems; ++i) {
  351.       if (items[i]->text) {
  352.     if (my < y1 + itemHeight + ltkBorderWidth) {
  353.       j = i;
  354.       break;
  355.     }
  356.     y1 += itemHeight + ltkBorderWidth;
  357.       } else {
  358.     if (my < y1 + 2 + ltkBorderWidth)
  359.       break;
  360.     y1 += 2 + ltkBorderWidth;
  361.       }
  362.     }
  363.   }
  364.   if (j != currentItem && (j >= 0 || !currentSubmenu)) {
  365.     if (currentItem >= 0) {
  366.       if (currentSubmenu) {
  367.     currentSubmenu->unpost();
  368.     currentSubmenu = NULL;
  369.     repost();
  370.       }
  371.       ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
  372.             ltkBorderWidth, currentY,
  373.             width - 2 * ltkBorderWidth,
  374.             itemHeight + 2*ltkBorderWidth,
  375.             ltkBorderNone);
  376.     }
  377.     currentItem = j;
  378.     currentY = y1;
  379.     if (currentItem >= 0) {
  380.       ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
  381.             ltkBorderWidth, currentY,
  382.             width - 2 * ltkBorderWidth,
  383.             itemHeight + 2*ltkBorderWidth,
  384.             ltkBorderRaised);
  385.       if (items[currentItem]->submenu) {
  386.     currentSubmenu = items[currentItem]->submenu;
  387.     currentSubmenu->post(win, x + width - 2*ltkBorderWidth,
  388.                  y + currentY, this);
  389.       }
  390.     }
  391.   }
  392.   if (j < 0 && parent)
  393.     parent->mouseMove(x + mx - parent->x, y + my - parent->y, btn);
  394. }
  395.  
  396. //------------------------------------------------------------------------
  397. // LTKMenuItem
  398. //------------------------------------------------------------------------
  399.  
  400. LTKMenuItem::LTKMenuItem(char *text1, char *shortcut1, int itemNum1,
  401.              LTKMenuCbk cbk1, LTKMenu *submenu1) {
  402.   text = text1;
  403.   shortcut = shortcut1;
  404.   itemNum = itemNum1;
  405.   cbk = cbk1;
  406.   submenu = submenu1;
  407. }
  408.