home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 mARCH / PCWK3A99.iso / Linux / DDD331 / DDD-3_1_.000 / DDD-3_1_ / ddd-3.1.1 / ddd / MakeMenu.C < prev    next >
C/C++ Source or Header  |  1998-11-17  |  34KB  |  1,357 lines

  1. // $Id: MakeMenu.C,v 1.84 1998/11/17 13:34:35 zeller Exp $
  2. // Create custom Motif Menus
  3.  
  4. // Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
  5. // Written by Andreas Zeller <zeller@ips.cs.tu-bs.de>.
  6. // 
  7. // This file is part of DDD.
  8. // 
  9. // DDD is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. // 
  14. // DDD is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. // 
  19. // You should have received a copy of the GNU General Public
  20. // License along with DDD -- see the file COPYING.
  21. // If not, write to the Free Software Foundation, Inc.,
  22. // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  23. // 
  24. // DDD is the data display debugger.
  25. // For details, see the DDD World-Wide-Web page, 
  26. // `http://www.cs.tu-bs.de/softech/ddd/',
  27. // or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
  28.  
  29. char MakeMenu_rcsid[] = 
  30.     "$Id: MakeMenu.C,v 1.84 1998/11/17 13:34:35 zeller Exp $";
  31.  
  32. #include "MakeMenu.h"
  33.  
  34. #include "assert.h"
  35. #include "strclass.h"
  36. #include "MString.h"
  37. #include "TimeOut.h"
  38. #include "misc.h"
  39. #include "string-fun.h"
  40. #include "charsets.h"
  41. #include "wm.h"
  42.  
  43. #include <stdlib.h>
  44. #include <Xm/Xm.h>
  45. #include <Xm/RowColumn.h>
  46. #include <Xm/CascadeB.h>
  47. #include <Xm/PushB.h>
  48. #include <Xm/ToggleB.h>
  49. #include <Xm/ArrowB.h>
  50. #include <Xm/CascadeB.h>
  51. #include <Xm/Separator.h>
  52. #include <Xm/Scale.h>
  53. #include <Xm/TextF.h>
  54. #include <Xm/Label.h>
  55. #include <Xm/List.h>
  56. #include <Xm/MenuShell.h>
  57. #include <X11/Xutil.h>
  58.  
  59. #include "LessTifH.h"
  60. #include "bool.h"
  61. #include "verify.h"
  62. #include "findParent.h"
  63. #include "frame.h"
  64. #include "ComboBox.h"
  65. #include "SpinBox.h"
  66.  
  67. #ifndef LOG_FLATTENING
  68. #define LOG_FLATTENING 0
  69. #endif
  70.  
  71. #ifndef LOG_PUSH_MENUS
  72. #define LOG_PUSH_MENUS 0
  73. #endif
  74.  
  75.  
  76. // Pushmenu callbacks
  77. static void ArmPushMenuCB(Widget, XtPointer, XtPointer);
  78. static void RedrawPushMenuCB(Widget, XtPointer, XtPointer);
  79. static void PopupPushMenuAct(Widget w, XEvent* e, String *, Cardinal *);
  80. static void DecoratePushMenuAct(Widget w, XEvent* e, String *, Cardinal *);
  81.  
  82. static XtActionsRec actions [] = {
  83.     {"popup-push-menu",            PopupPushMenuAct },
  84.     {"decorate-push-menu",         DecoratePushMenuAct },
  85. };
  86.  
  87. static char pushMenuTranslations[] = 
  88.     "<Expose>:          decorate-push-menu()\n"
  89. ;
  90.  
  91. static char lesstif_pushMenuTranslations[] = 
  92.     "None<Btn3Down>:    popup-push-menu()\n"
  93. ;
  94.  
  95. struct PushMenuInfo {
  96.     Widget widget;        // The PushButton
  97.     Widget subMenu;        // Submenu of this PushButton
  98.     bool flat;            // Whether the PushButton is flattened
  99.     XtIntervalId timer;        // Timer while waiting
  100.  
  101.     PushMenuInfo(Widget w, Widget s, bool f)
  102.     : widget(w), subMenu(s), flat(f), timer(0)
  103.     {}
  104. };
  105.  
  106. //-----------------------------------------------------------------------
  107. // Auto-raise stuff
  108. //-----------------------------------------------------------------------
  109.  
  110. // Whether menus should be auto-raised
  111. #define XtNautoRaiseMenu  "autoRaiseMenu"
  112. #define XtCAutoRaiseMenu  "AutoRaiseMenu"
  113.  
  114. struct MMresource_values {
  115.     Boolean auto_raise_menu;
  116. };
  117.  
  118. static XtResource MMsubresources[] = {
  119.     {
  120.     XtNautoRaiseMenu,
  121.     XtCAutoRaiseMenu,
  122.     XmRBoolean,
  123.     sizeof(Boolean),
  124.     XtOffsetOf(MMresource_values, auto_raise_menu),
  125.     XmRImmediate,
  126.     XtPointer(False)
  127.     }
  128. };
  129.  
  130.  
  131. // Make sure menu stays on top.  This prevents conflicts with
  132. // auto-raise windows which would otherwise hide menu panels.
  133. static void AutoRaiseEH(Widget w, XtPointer, XEvent *event, Boolean *)
  134. {
  135.  
  136.     if (event->type != VisibilityNotify)
  137.     return;
  138.  
  139.     switch (event->xvisibility.state)
  140.     {
  141.     case VisibilityFullyObscured:
  142.     case VisibilityPartiallyObscured:
  143.     XRaiseWindow(XtDisplay(w), frame(w));
  144.     break;
  145.     }
  146. }
  147.  
  148. static void auto_raise(Widget shell)
  149. {
  150.     assert(XmIsMenuShell(shell));
  151.  
  152.     // Get text
  153.     MMresource_values values;
  154.     XtGetApplicationResources(shell, &values, 
  155.                   MMsubresources, XtNumber(MMsubresources), 
  156.                   NULL, 0);
  157.  
  158.     if (values.auto_raise_menu)
  159.     {
  160.     XtAddEventHandler(shell, VisibilityChangeMask, False,
  161.               AutoRaiseEH, XtPointer(0));
  162.     }
  163. }
  164.  
  165.  
  166. //-----------------------------------------------------------------------
  167. // Flat buttons
  168. //-----------------------------------------------------------------------
  169.  
  170. // The currently unflattened button
  171. static Widget active_button = 0;
  172.  
  173. static void flatten_button(Widget w, bool switch_colors = true)
  174. {
  175.     Pixel background;
  176.     Pixmap highlight_pixmap, label_pixmap;
  177.     Pixmap bottom_shadow_pixmap;
  178.     XtVaGetValues(w,
  179.           XmNbackground,         &background,
  180.           XmNlabelPixmap,        &label_pixmap,
  181.           XmNhighlightPixmap,    &highlight_pixmap,
  182.           XmNbottomShadowPixmap, &bottom_shadow_pixmap,
  183.           NULL);
  184.  
  185.     if (bottom_shadow_pixmap == XmUNSPECIFIED_PIXMAP)
  186.     {
  187. #if LOG_FLATTENING
  188.     clog << "Flattening " << XtName(w) << "\n";
  189. #endif
  190.  
  191.     Arg args[10];
  192.     Cardinal arg = 0;
  193.  
  194.     Pixmap empty = XmGetPixmap(XtScreen(w), "background", 
  195.                    background, background);
  196.  
  197.     XtSetArg(args[arg], XmNbottomShadowPixmap, empty); arg++;
  198.     XtSetArg(args[arg], XmNtopShadowPixmap,    empty); arg++;
  199.  
  200.     if (switch_colors)
  201.     {
  202.         XtSetArg(args[arg], XmNlabelPixmap, highlight_pixmap); arg++;
  203.         XtSetArg(args[arg], XmNhighlightPixmap, label_pixmap); arg++;
  204.     }
  205.  
  206.     XtSetValues(w, args, arg);
  207.     }
  208. }
  209.  
  210. static void unflatten_button(Widget w, bool switch_colors = true)
  211. {
  212.     Pixel background;
  213.     Pixmap highlight_pixmap, label_pixmap;
  214.     Pixmap bottom_shadow_pixmap;
  215.     XtVaGetValues(w,
  216.           XmNbackground,         &background,
  217.           XmNlabelPixmap,        &label_pixmap,
  218.           XmNhighlightPixmap,    &highlight_pixmap,
  219.           XmNbottomShadowPixmap, &bottom_shadow_pixmap,
  220.           NULL);
  221.  
  222.     if (bottom_shadow_pixmap != XmUNSPECIFIED_PIXMAP)
  223.     {
  224. #if LOG_FLATTENING
  225.     clog << "Unflattening " << XtName(w) << "\n";
  226. #endif
  227.  
  228.     Arg args[10];
  229.     Cardinal arg = 0;
  230.  
  231.     XtSetArg(args[arg], XmNbottomShadowPixmap, 
  232.          XmUNSPECIFIED_PIXMAP); arg++;
  233.     XtSetArg(args[arg], XmNtopShadowPixmap,
  234.          XmUNSPECIFIED_PIXMAP); arg++;
  235.  
  236.     if (switch_colors)
  237.     {
  238.         XtSetArg(args[arg], XmNlabelPixmap, highlight_pixmap); arg++;
  239.         XtSetArg(args[arg], XmNhighlightPixmap, label_pixmap); arg++;
  240.     }
  241.  
  242.     XtSetValues(w, args, arg);
  243.     }
  244. }
  245.  
  246.  
  247. static void FlattenEH(Widget w,
  248.               XtPointer /* client_data */,
  249.               XEvent *event, 
  250.               Boolean * /* continue_to_dispatch */)
  251. {
  252.     if (event->xcrossing.state & (Button1Mask | Button2Mask | Button3Mask | 
  253.                   Button4Mask | Button5Mask))
  254.     return;            // Button is still pressed
  255.  
  256.     switch (event->type)
  257.     {
  258.     case EnterNotify:
  259.     {
  260. #if LOG_FLATTENING
  261.     clog << "Entering " << XtName(w) << "\n";
  262. #endif
  263.  
  264.     unflatten_button(w);
  265.     active_button = w;
  266.     break;
  267.     }
  268.  
  269.     case LeaveNotify:
  270.     {
  271. #if LOG_FLATTENING
  272.     clog << "Leaving " << XtName(w) << "\n";
  273. #endif
  274.  
  275.     flatten_button(w);
  276.     active_button = 0;
  277.     break;
  278.     }
  279.     }
  280. }
  281.  
  282. // Handle Arm() and Disarm() actions
  283. static void FlattenCB(Widget w, XtPointer client_data, XtPointer)
  284. {
  285.     if (w == active_button)
  286.     {
  287.     // We have already entered it -- don't interfere
  288.     return;
  289.     }
  290.  
  291.     bool set = bool(client_data);
  292.     if (set)
  293.     {
  294.     // clog << "Arming " << XtName(w) << "\n";
  295.  
  296.     flatten_button(w, false);
  297.     }
  298.     else
  299.     {
  300.     // clog << "Disarming " << XtName(w) << "\n";
  301.  
  302.     unflatten_button(w, false);
  303.     }
  304. }
  305.  
  306. static void ReflattenButtonCB(Widget /* shell */, XtPointer client_data, 
  307.                   XtPointer = 0)
  308. {
  309.     Widget w = (Widget)client_data;
  310.     EventMask event_mask = EnterWindowMask | LeaveWindowMask;
  311.     XtAddEventHandler(w, event_mask, False, FlattenEH, XtPointer(0));
  312.     XtAddCallback(w, XmNarmCallback,    FlattenCB, XtPointer(False));
  313.     XtAddCallback(w, XmNdisarmCallback, FlattenCB, XtPointer(True));
  314.     flatten_button(w);
  315. }
  316.  
  317.  
  318. //-----------------------------------------------------------------------
  319. // Add items
  320. //-----------------------------------------------------------------------
  321.  
  322. // Add items to shell.  If IGNORE_SEPS is set, all separators are ignored.
  323. void MMaddItems(Widget shell, MMDesc items[], bool ignore_seps)
  324. {
  325.     static bool actions_added = false;
  326.         
  327.     if (!actions_added)
  328.     {
  329.     XtAppAddActions(XtWidgetToApplicationContext(shell), 
  330.             actions, XtNumber(actions));
  331.     actions_added = true;
  332.     }
  333.  
  334.     Arg args[10];
  335.     int arg;
  336.  
  337.     // Create lots of buttons...
  338.     for (MMDesc *item = items; item != 0 && item->name != 0; item++)
  339.     {
  340.     char *name              = (char *)item->name;
  341.     MMType flags            = item->type;
  342.     MMType type             = flags & MMTypeMask;
  343.     Widget& widget          = item->widget;
  344.     Widget *widgetptr       = item->widgetptr;
  345.     MMDesc *subitems        = item->items;
  346.     Widget& label           = item->label;
  347.  
  348.     if (flags & MMIgnore)
  349.         continue;        // Don't create
  350.  
  351.     string subMenuName = string(name) + "Menu";
  352.     string panelName   = string(name) + "Panel";
  353.     static string textName  = "text";
  354.     static string labelName = "label";
  355.     Widget subMenu = 0;
  356.     Widget panel   = 0;
  357.     bool flat = false;
  358.     label = 0;
  359.     widget = 0;
  360.  
  361.     switch(type) 
  362.     {
  363.     case MMFlatPush:
  364.     {
  365.         flat = true;
  366.         // FALL THROUGH
  367.     }
  368.  
  369.     case MMPush:
  370.     {
  371.         // Create a PushButton
  372.         arg = 0;
  373.         if (flat)
  374.         {
  375.         Pixel background;
  376.         XtVaGetValues(shell, XmNbackground, &background, NULL);
  377.         Pixmap empty = XmGetPixmap(XtScreen(shell), "background", 
  378.                        background, background);
  379.  
  380.         XtSetArg(args[arg], XmNbottomShadowPixmap, empty); arg++;
  381.         XtSetArg(args[arg], XmNtopShadowPixmap,    empty); arg++;
  382.         XtSetArg(args[arg], XmNhighlightThickness, 0);     arg++;
  383.         XtSetArg(args[arg], XmNshadowThickness,    2);     arg++;
  384.         }
  385.         else
  386.         {
  387.         XtSetArg(args[arg], 
  388.              XmNhighlightPixmap, XmUNSPECIFIED_PIXMAP); arg++;
  389.         }
  390.  
  391.         PushMenuInfo *info = 0;
  392.         if (lesstif_version <= 84)
  393.         {
  394.         // LessTif 0.84 and earlier wants the PushButton as
  395.         // parent of the menu
  396.         widget = verify(XmCreatePushButton(shell, name, args, arg));
  397.  
  398.         if (subitems != 0)
  399.         {
  400.             subMenu = MMcreatePushMenu(widget, subMenuName, subitems);
  401.             info = new PushMenuInfo(widget, subMenu, flat);
  402.             XtVaSetValues(widget, XmNuserData, XtPointer(info), NULL);
  403.         }
  404.         }
  405.         else
  406.         {
  407.         // Motif wants the shell as parent of the menu
  408.         if (subitems != 0)
  409.         {
  410.             subMenu = MMcreatePushMenu(shell, subMenuName, subitems);
  411.             info = new PushMenuInfo(0, subMenu, flat);
  412.             XtSetArg(args[arg], XmNuserData, XtPointer(info)); arg++;
  413.         }
  414.  
  415.         widget = verify(XmCreatePushButton(shell, name, args, arg));
  416.  
  417.         if (info != 0)
  418.             info->widget = widget;
  419.         }
  420.         break;
  421.     }
  422.  
  423.     case MMToggle:
  424.     {
  425.         // Create a ToggleButton
  426.         assert(subitems == 0);
  427.  
  428.         arg = 0;
  429.         widget = verify(XmCreateToggleButton(shell, name, args, arg));
  430.         break;
  431.     }
  432.  
  433.     case MMLabel:
  434.     {
  435.         // Create a Label
  436.         assert(subitems == 0);
  437.  
  438.         arg = 0;
  439.         widget = verify(XmCreateLabel(shell, name, args, arg));
  440.         break;
  441.     }
  442.  
  443.     case MMArrow:
  444.     {
  445.         // Create an arrow
  446.         assert(subitems == 0);
  447.  
  448.         arg = 0;
  449.         widget = verify(XmCreateArrowButton(shell, name, args, arg));
  450.         break;
  451.     }
  452.  
  453.     case MMMenu:
  454.     {
  455.         // Create a CascadeButton and a new PulldownMenu
  456.         assert(subitems != 0);
  457.  
  458.         subMenu = MMcreatePulldownMenu(shell, subMenuName, subitems);
  459.  
  460.         arg = 0;
  461.         XtSetArg(args[arg], XmNsubMenuId, subMenu); arg++;
  462.         widget = verify(XmCreateCascadeButton(shell, name, args, arg));
  463.  
  464.             if (lesstif_version <= 79)
  465.         {
  466.         // LessTif 0.79 and earlier has a very tight packing
  467.         // of menu items; place a few spaces around the labels
  468.         // to increase item distance.
  469.         XmString old_label;
  470.         XtVaGetValues(widget, XmNlabelString, &old_label, NULL);
  471.         MString new_label(old_label, true);
  472.         XmStringFree(old_label);
  473.  
  474.         if (!new_label.isNull())
  475.         {
  476.             new_label = MString("  ") + new_label + MString("  ");
  477.             XtVaSetValues(widget, 
  478.                   XmNlabelString, new_label.xmstring(), 
  479.                   NULL);
  480.         }
  481.  
  482.         // Same applies to accelerator texts.
  483.         XmString old_acc;
  484.         XtVaGetValues(widget, XmNacceleratorText, &old_acc, NULL);
  485.         MString new_acc(old_acc, true);
  486.         XmStringFree(old_acc);
  487.  
  488.         if (!new_acc.isNull())
  489.         {
  490.             new_acc = MString("  ") + new_acc;
  491.             XtVaSetValues(widget, 
  492.                   XmNacceleratorText, new_acc.xmstring(), 
  493.                   NULL);
  494.         }
  495.         }
  496.         break;
  497.     }
  498.  
  499.     case MMRadioMenu:
  500.     {
  501.         // Create a CascadeButton and a new PulldownMenu
  502.         assert(subitems != 0);
  503.  
  504.         subMenu = MMcreateRadioPulldownMenu(shell, subMenuName, subitems);
  505.  
  506.         arg = 0;
  507.         XtSetArg(args[arg], XmNsubMenuId, subMenu); arg++;
  508.         widget = verify(XmCreateCascadeButton(shell, name, args, arg));
  509.         break;
  510.     }
  511.  
  512.     case MMOptionMenu:
  513.     {
  514.         // Create an option menu
  515.         assert(subitems != 0);
  516.  
  517.         subMenu = MMcreatePulldownMenu(shell, subMenuName, subitems);
  518.  
  519.         arg = 0;
  520.         XtSetArg(args[arg], XmNsubMenuId, subMenu); arg++;
  521.         widget = verify(XmCreateOptionMenu(shell, name, args, arg));
  522.         break;
  523.     }
  524.  
  525.     case MMPanel:
  526.     case MMRadioPanel:
  527.     case MMButtonPanel:
  528.     {
  529.         // Create a label with an associated panel
  530.         assert(subitems != 0);
  531.  
  532.         bool have_label = 
  533.         (name[0] != '\0' && (flags & MMUnmanagedLabel) == 0);
  534.  
  535.         arg = 0;
  536.         XtSetArg(args[arg], XmNorientation, XmHORIZONTAL); arg++;
  537.         XtSetArg(args[arg], XmNborderWidth,     0); arg++;
  538.         XtSetArg(args[arg], XmNentryBorder,     0); arg++;
  539.         XtSetArg(args[arg], XmNspacing,         0); arg++;
  540.         XtSetArg(args[arg], XmNmarginWidth,     0); arg++;
  541.         XtSetArg(args[arg], XmNmarginHeight,    0); arg++;
  542.         XtSetArg(args[arg], XmNshadowThickness, 0); arg++;
  543.  
  544.         widget = verify(XmCreateRowColumn(shell, panelName, args, arg));
  545.  
  546.         arg = 0;
  547.         label = verify(XmCreateLabel(widget, name, args, arg));
  548.         if (have_label)
  549.         XtManageChild(label);
  550.  
  551.         Widget (*create_panel)(Widget, String, MMDesc[], 
  552.                    ArgList, Cardinal) = 0;
  553.  
  554.         switch (type)
  555.         {
  556.         case MMPanel:
  557.         create_panel = MMcreatePanel;
  558.         break;
  559.  
  560.         case MMRadioPanel:
  561.         create_panel = MMcreateRadioPanel;
  562.         break;
  563.  
  564.         case MMButtonPanel:
  565.         create_panel = MMcreateButtonPanel;
  566.         break;
  567.  
  568.         default:
  569.         assert(0);
  570.         abort();
  571.         }
  572.  
  573.         arg = 0;
  574.         XtSetArg(args[arg], XmNorientation, 
  575.              (flags & MMVertical) ? XmVERTICAL : XmHORIZONTAL); arg++;
  576.  
  577.         if (!have_label)
  578.         {
  579.         XtSetArg(args[arg], XmNborderWidth,     0); arg++;
  580.         XtSetArg(args[arg], XmNentryBorder,     0); arg++;
  581.         XtSetArg(args[arg], XmNspacing,         0); arg++;
  582.         XtSetArg(args[arg], XmNmarginWidth,     0); arg++;
  583.         XtSetArg(args[arg], XmNmarginHeight,    0); arg++;
  584.         XtSetArg(args[arg], XmNshadowThickness, 0); arg++;
  585.         }
  586.  
  587.         subMenu = create_panel(widget, subMenuName, subitems, args, arg);
  588.  
  589.         XtManageChild(subMenu);
  590.         break;
  591.     }
  592.  
  593.     case MMScale:
  594.     {
  595.         // Create a scale
  596.         assert(subitems == 0);
  597.  
  598.         arg = 0;
  599.         widget = verify(XmCreateScale(shell, name, args, arg));
  600.         break;
  601.     }
  602.  
  603.     case MMSpinBox:
  604.     case MMComboBox:
  605.     case MMTextField:
  606.     case MMEnterField:
  607.     {
  608.         // Create a label with an associated text field
  609.         assert(subitems == 0);
  610.  
  611.         arg = 0;
  612.         XtSetArg(args[arg], XmNorientation,     XmHORIZONTAL); arg++;
  613.         XtSetArg(args[arg], XmNborderWidth,     0); arg++;
  614.         XtSetArg(args[arg], XmNentryBorder,     0); arg++;
  615.         XtSetArg(args[arg], XmNspacing,         0); arg++;
  616.         XtSetArg(args[arg], XmNmarginWidth,     0); arg++;
  617.         XtSetArg(args[arg], XmNmarginHeight,    0); arg++;
  618.         XtSetArg(args[arg], XmNshadowThickness, 0); arg++;
  619.  
  620.         panel = verify(XmCreateRowColumn(shell, name, args, arg));
  621.  
  622.         arg = 0;
  623.         label = verify(XmCreateLabel(panel, labelName, args, arg));
  624.         if (name[0] != '\0' && (flags & MMUnmanagedLabel) == 0)
  625.         XtManageChild(label);
  626.  
  627.         switch (type)
  628.         {
  629.         case MMSpinBox:
  630.         arg = 0;
  631.         widget = CreateSpinBox(panel, textName, args, arg);
  632.         break;
  633.  
  634.         case MMComboBox:
  635.         arg = 0;
  636.         widget = CreateComboBox(panel, textName, args, arg);
  637.         break;
  638.  
  639.         case MMTextField:
  640.         case MMEnterField:
  641.         arg = 0;
  642.         widget = verify(XmCreateTextField(panel, textName, args, arg));
  643.         XtManageChild(widget);
  644.         break;
  645.         }
  646.         break;
  647.     }
  648.  
  649.     case MMSeparator:
  650.     {
  651.         // Create a separator
  652.         assert(subitems == 0);
  653.  
  654.         if (ignore_seps)
  655.         continue;
  656.         arg = 0;
  657.         widget = verify(XmCreateSeparator(shell, name, args, arg));
  658.         break;
  659.     }
  660.  
  661.     default:
  662.         // Invalid type
  663.         assert(0);
  664.         abort();
  665.     }
  666.  
  667.     if (flags & MMHelp)
  668.     {
  669.         arg = 0;
  670.         XtSetArg(args[arg], XmNmenuHelpWidget, item->widget); arg++;
  671.         XtSetValues(shell, args, arg);
  672.     }
  673.  
  674.     if (panel == 0)
  675.         panel = widget;
  676.  
  677.     if (flags & MMInsensitive)
  678.         set_sensitive(panel, False);
  679.  
  680.     if (!(flags & MMUnmanaged))
  681.         XtManageChild(panel);
  682.  
  683.     if (widgetptr != 0)
  684.         *widgetptr = widget;
  685.     }
  686. }
  687.  
  688.  
  689. //-----------------------------------------------------------------------
  690. // Custom menu creation
  691. //-----------------------------------------------------------------------
  692.  
  693. // Create pulldown menu from items
  694. Widget MMcreatePulldownMenu(Widget parent, String name, MMDesc items[],
  695.                 ArgList args, Cardinal arg)
  696. {
  697.     Widget menu = verify(XmCreatePulldownMenu(parent, name, args, arg));
  698.     MMaddItems(menu, items);
  699.     auto_raise(XtParent(menu));
  700.  
  701.     return menu;
  702. }
  703.  
  704. // Create radio pulldown menu from items
  705. Widget MMcreateRadioPulldownMenu(Widget parent, String name, MMDesc items[],
  706.                  ArgList _args, Cardinal _arg)
  707. {
  708.     ArgList args = new Arg[_arg + 10];
  709.     Cardinal arg = 0;
  710.  
  711.     XtSetArg(args[arg], XmNisHomogeneous, True); arg++;
  712.     XtSetArg(args[arg], XmNentryClass, xmToggleButtonWidgetClass); arg++;
  713.     XtSetArg(args[arg], XmNradioBehavior, True); arg++;
  714.  
  715.     for (Cardinal i = 0; i < _arg; i++)
  716.     args[arg++] = _args[i];
  717.  
  718.     Widget w = MMcreatePulldownMenu(parent, name, items, args, arg);
  719.  
  720.     delete[] args;
  721.     return w;
  722. }
  723.  
  724. // Create popup menu from items
  725. Widget MMcreatePopupMenu(Widget parent, String name, MMDesc items[],
  726.              ArgList args, Cardinal arg)
  727. {
  728.     Widget menu = verify(XmCreatePopupMenu(parent, name, args, arg));
  729.     MMaddItems(menu, items);
  730.  
  731.     // Don't auto-raise popup menus.
  732.     // 1. There are conflicts with nested popups.
  733.     // 2. During a popup, the pointer is grabbed such that we won't
  734.     //    have an auto-raised window anyway.
  735.     // auto_raise(XtParent(menu));
  736.  
  737.     return menu;
  738. }
  739.  
  740.  
  741. // Create menu bar from items
  742. Widget MMcreateMenuBar(Widget parent, String name, MMDesc items[],
  743.                ArgList args, Cardinal arg)
  744. {
  745.     Widget bar = verify(XmCreateMenuBar(parent, name, args, arg));
  746.     MMaddItems(bar, items);
  747.     XtManageChild(bar);
  748.  
  749.     return bar;
  750. }
  751.  
  752. // Create work area from items
  753. Widget MMcreateWorkArea(Widget parent, String name, MMDesc items[],
  754.             ArgList args, Cardinal arg)
  755. {
  756.     Widget bar = verify(XmCreateWorkArea(parent, name, args, arg));
  757.     MMaddItems(bar, items, true);
  758.     XtManageChild(bar);
  759.  
  760.     return bar;
  761. }
  762.  
  763. // Create panel from items
  764. Widget MMcreatePanel(Widget parent, String name, MMDesc items[],
  765.              ArgList args, Cardinal arg)
  766. {
  767.     Widget panel = verify(XmCreateWorkArea(parent, name, args, arg));
  768.     MMaddItems(panel, items);
  769.     XtManageChild(panel);
  770.  
  771.     return panel;
  772. }
  773.  
  774. void MMadjustPanel(MMDesc items[], Dimension space)
  775. {
  776.     // Align panel labels
  777.     Dimension max_label_width = 0;
  778.     MMDesc *item;
  779.     for (item = items; item != 0 && item->name != 0; item++)
  780.     {
  781.     if (item->label == 0)
  782.         continue;
  783.  
  784.     XtWidgetGeometry size;
  785.     size.request_mode = CWWidth;
  786.     XtQueryGeometry(item->label, NULL, &size);
  787.     max_label_width = max(max_label_width, size.width);
  788.     }
  789.  
  790.     // Leave some extra space
  791.     max_label_width += space;
  792.  
  793.     for (item = items; item != 0 && item->name != 0; item++)
  794.     {
  795.     if (item->label == 0)
  796.         continue;
  797.  
  798.     XtVaSetValues(item->label,
  799.               XmNrecomputeSize, False,
  800.               XmNwidth, max_label_width,
  801.               XtPointer(0));
  802.     }
  803. }
  804.  
  805. // Create radio panel from items
  806. Widget MMcreateRadioPanel(Widget parent, String name, MMDesc items[],
  807.               ArgList _args, Cardinal _arg)
  808. {
  809.     ArgList args = new Arg[_arg + 10];
  810.     Cardinal arg = 0;
  811.  
  812.     XtSetArg(args[arg], XmNisHomogeneous, True);                      arg++;
  813.     XtSetArg(args[arg], XmNentryClass,    xmToggleButtonWidgetClass); arg++;
  814.     XtSetArg(args[arg], XmNradioBehavior, True);                      arg++;
  815.  
  816.     for (Cardinal i = 0; i < _arg; i++)
  817.     args[arg++] = _args[i];
  818.  
  819.     Widget panel = verify(XmCreateRowColumn(parent, name, args, arg));
  820.     MMaddItems(panel, items);
  821.     XtManageChild(panel);
  822.  
  823.     delete[] args;
  824.     return panel;
  825. }
  826.  
  827. // Create button panel from items
  828. Widget MMcreateButtonPanel(Widget parent, String name, MMDesc items[],
  829.                ArgList args, Cardinal arg)
  830. {
  831.     Widget panel = verify(XmCreateRowColumn(parent, name, args, arg));
  832.     MMaddItems(panel, items);
  833.     XtManageChild(panel);
  834.  
  835.     return panel;
  836. }
  837.  
  838. // Perform proc on items
  839. void MMonItems(MMDesc items[], MMItemProc proc, XtPointer closure, int depth)
  840. {
  841.     if (depth == 0)
  842.     return;
  843.  
  844.     for (MMDesc *item = items; item != 0 && item->name != 0; item++)
  845.     {
  846.     if (item->type & MMIgnore)
  847.         continue;
  848.  
  849.     proc(item, closure);
  850.  
  851.     if (item->items)
  852.         MMonItems(item->items, proc, closure, depth - 1);
  853.     }
  854. }
  855.  
  856.  
  857. //-----------------------------------------------------------------------
  858. // Callbacks
  859. //-----------------------------------------------------------------------
  860.  
  861. // Add callbacks to items
  862. static void addCallback(MMDesc *item, XtPointer default_closure)
  863. {
  864.     MMType flags            = item->type;
  865.     MMType type             = flags & MMTypeMask;
  866.     Widget widget           = item->widget;
  867.     XtCallbackRec callback  = item->callback;
  868.     
  869.     if (callback.closure == 0)
  870.     callback.closure = default_closure;
  871.  
  872.     bool flat = false;
  873.  
  874.     switch(type) 
  875.     {
  876.     case MMFlatPush:
  877.     {
  878.     flat = true;
  879.     // FALL THROUGH
  880.     }
  881.  
  882.     case MMPush:
  883.     {
  884.     void *userData = 0;
  885.     XtVaGetValues(widget, XmNuserData, &userData, XtPointer(0));
  886.  
  887.     if (userData != 0)
  888.     {
  889.         PushMenuInfo *info = (PushMenuInfo *)userData;
  890.  
  891.         // A 'push menu' is a menu associated with a push button.
  892.         // It pops up after pressing the button a certain time.
  893.         XtAddCallback(widget, XmNarmCallback,    ArmPushMenuCB, info);
  894.         XtAddCallback(widget, XmNarmCallback,    RedrawPushMenuCB, 0);
  895.         XtAddCallback(widget, XmNdisarmCallback, RedrawPushMenuCB, 0);
  896.  
  897.         static XtTranslations translations =
  898.             XtParseTranslationTable(pushMenuTranslations);
  899.         XtAugmentTranslations(widget, translations);
  900.  
  901.         if (lesstif_version <= 81)
  902.         {
  903.         // In LessTif 0.81 and earlier, one must use button 3
  904.         // to pop up push menus
  905.         static XtTranslations lesstif_translations =
  906.             XtParseTranslationTable(lesstif_pushMenuTranslations);
  907.         XtAugmentTranslations(widget, lesstif_translations);
  908.         }
  909.     }
  910.  
  911.     if (flat)
  912.     {
  913.         ReflattenButtonCB(widget, XtPointer(widget));
  914.     }
  915.  
  916.     // FALL THROUGH
  917.     }
  918.  
  919.     case MMArrow:
  920.     {
  921.     if (callback.callback != 0)
  922.         XtAddCallback(widget, 
  923.               XmNactivateCallback,
  924.               callback.callback, 
  925.               callback.closure);
  926.     else
  927.         set_sensitive(widget, False);
  928.     break;
  929.     }
  930.  
  931.     case MMToggle:
  932.     case MMScale:
  933.     {
  934.     if (callback.callback != 0)
  935.         XtAddCallback(widget,
  936.               XmNvalueChangedCallback,
  937.               callback.callback, 
  938.               callback.closure);
  939.     else
  940.         set_sensitive(widget, False);
  941.     break;
  942.     }
  943.  
  944.     case MMComboBox:
  945.     {
  946.     if (callback.callback != 0)
  947.     {
  948.         Widget list = ComboBoxList(widget);
  949.  
  950.         XtAddCallback(list, XmNbrowseSelectionCallback,
  951.               callback.callback, callback.closure);
  952.         XtAddCallback(list, XmNsingleSelectionCallback,
  953.               callback.callback, callback.closure);
  954.         XtAddCallback(list, XmNmultipleSelectionCallback,
  955.               callback.callback, callback.closure);
  956.         XtAddCallback(list, XmNextendedSelectionCallback,
  957.               callback.callback, callback.closure);
  958.     }
  959.  
  960.     // FALL THROUGH
  961.     }
  962.  
  963.     case MMSpinBox:
  964.     case MMTextField:
  965.     {
  966.     if (callback.callback != 0)
  967.         XtAddCallback(widget,
  968.               XmNvalueChangedCallback,
  969.               callback.callback, 
  970.               callback.closure);
  971.  
  972.     if (type == MMTextField)
  973.         break;
  974.     // FALL THROUGH
  975.     }
  976.  
  977.     case MMEnterField:
  978.     {
  979.     if (callback.callback != 0)
  980.         XtAddCallback(widget,
  981.               XmNactivateCallback,
  982.               callback.callback, 
  983.               callback.closure);
  984.     break;
  985.     }
  986.  
  987.     case MMMenu:
  988.     case MMRadioMenu:
  989.     case MMOptionMenu:
  990.     {
  991.     Widget subMenu = 0;
  992.     XtVaGetValues(widget, XmNsubMenuId, &subMenu, NULL);
  993.  
  994.     if (subMenu != 0 && callback.callback != 0)
  995.     {
  996.         XtAddCallback(subMenu,
  997.               XmNmapCallback,
  998.               callback.callback, 
  999.               callback.closure);
  1000.         XtAddCallback(subMenu,
  1001.               XmNunmapCallback,
  1002.               callback.callback, 
  1003.               callback.closure);
  1004. #if XmVersion >= 1002
  1005.         XtAddCallback(subMenu,
  1006.               XmNtearOffMenuActivateCallback,
  1007.               callback.callback, 
  1008.               callback.closure);
  1009.         XtAddCallback(subMenu,
  1010.               XmNtearOffMenuDeactivateCallback,
  1011.               callback.callback, 
  1012.               callback.closure);
  1013. #endif
  1014.     }
  1015.     break;
  1016.     }
  1017.  
  1018.     case MMLabel:
  1019.     case MMSeparator:
  1020.     case MMPanel:
  1021.     case MMRadioPanel:
  1022.     case MMButtonPanel:
  1023.     assert(callback.callback == 0);
  1024.     break;
  1025.  
  1026.     default:
  1027.     // invalid type
  1028.     assert(0);
  1029.     abort();
  1030.     }
  1031. }
  1032.  
  1033. void MMaddCallbacks(MMDesc items[], XtPointer default_closure, int depth)
  1034. {
  1035.     MMonItems(items, addCallback, default_closure, depth);
  1036. }
  1037.  
  1038.  
  1039. // Add help callback
  1040. static void addHelpCallback(MMDesc *item, XtPointer closure)
  1041. {
  1042.     Widget widget       = item->widget;
  1043.     XtCallbackProc proc = XtCallbackProc(closure);
  1044.  
  1045.     XtAddCallback(widget, XmNhelpCallback, proc, XtPointer(0));
  1046. }
  1047.  
  1048. void MMaddHelpCallback(MMDesc items[], XtCallbackProc proc, int depth)
  1049. {
  1050.     MMonItems(items, addHelpCallback, XtPointer(proc), depth);
  1051. }
  1052.  
  1053.  
  1054.  
  1055. //-----------------------------------------------------------------------
  1056. // PushMenus
  1057. //-----------------------------------------------------------------------
  1058.  
  1059. // Create pushmenu from items
  1060. Widget MMcreatePushMenu(Widget parent, String name, MMDesc items[],
  1061.             ArgList _args, Cardinal _arg)
  1062. {
  1063.     ArgList args = new Arg[_arg + 10];
  1064.     Cardinal arg = 0;
  1065.  
  1066.     // By default, PushButton menus are activated using Button 1.
  1067.     if (XmVersion < 1002 || lesstif_version <= 84)
  1068.     {
  1069.     // Setting the menuPost resource is required by Motif 1.1 and
  1070.     // LessTif 0.84 and earlier.  However, OSF/Motif 2.0 (and
  1071.     // OSF/Motif 1.2, according to Roy Dragseth
  1072.     // <royd@math.uit.no>) choke on this line - buttons become
  1073.     // entirely insensitive.
  1074.     XtSetArg(args[arg], XmNmenuPost, "<Btn1Down>"); arg++;
  1075.     }
  1076.  
  1077. #if XmVersion >= 1002
  1078.     // Tear-off push menus don't work well - in LessTif, they cause
  1079.     // frequent X errors, and in Motif, they disable the old menus
  1080.     // once torn off.  So, we explicitly disable them.
  1081.     XtSetArg(args[arg], XmNtearOffModel, XmTEAR_OFF_DISABLED); arg++;
  1082. #endif
  1083.  
  1084.     for (Cardinal i = 0; i < _arg; i++)
  1085.     args[arg++] = _args[i];
  1086.     
  1087.     Widget menu = verify(XmCreatePopupMenu(parent, name, args, arg));
  1088.     MMaddItems(menu, items);
  1089.  
  1090.     // Don't auto-raise popup menus.
  1091.     // 1. There are conflicts with nested popups.
  1092.     // 2. During a popup, the pointer is grabbed such that we won't
  1093.     //    have an auto-raised window anyway.
  1094.     // auto_raise(XtParent(menu));
  1095.  
  1096.     // LessTif places a passive grab on the parent, such that the
  1097.     // pointer is grabbed as soon as the menuPost event occurs.  This
  1098.     // grab breaks PushMenus, so we cancel it.  Motif places a passive
  1099.     // grab on button 3, such that the pointer is grabbed as soon as
  1100.     // button 3 is pressed.  In Motif 1.1, it even remains grabbed!
  1101.     // This breaks any X session, so we cancel it.
  1102.     XtUngrabButton(parent, AnyButton, AnyModifier);
  1103.  
  1104.     delete[] args;
  1105.     return menu;
  1106. }
  1107.  
  1108.  
  1109.  
  1110.  
  1111. static XEvent last_push_menu_event; // Just save it
  1112.  
  1113. // Remove time out again
  1114. static void CancelPopupPushMenuCB(Widget w, XtPointer client_data, 
  1115.                   XtPointer call_data)
  1116. {
  1117.     XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *)call_data;
  1118.     (void) cbs;            // Use it
  1119.  
  1120.     PushMenuInfo *info = (PushMenuInfo *)client_data;
  1121.  
  1122.     if (info->timer != 0)
  1123.     {
  1124. #if LOG_PUSH_MENUS
  1125.     clog << "canceling (reason " << cbs->reason << ")\n";
  1126. #endif
  1127.  
  1128.     XtRemoveTimeOut(info->timer);
  1129.     info->timer = 0;
  1130.     }
  1131.  
  1132.     XtRemoveCallback(w, XmNdisarmCallback,
  1133.              CancelPopupPushMenuCB, XtPointer(info));
  1134.     XtRemoveCallback(w, XmNactivateCallback,
  1135.              CancelPopupPushMenuCB, XtPointer(info));
  1136.  
  1137.     XtUnmanageChild(info->subMenu);
  1138.     Widget shell = XtParent(info->subMenu);
  1139.     XtPopdown(shell);
  1140. }
  1141.  
  1142. // Popup menu right now
  1143. static void PopupPushMenuCB(XtPointer client_data, XtIntervalId *id)
  1144. {
  1145.     (void) id;            // Use it
  1146.  
  1147.     PushMenuInfo *info = (PushMenuInfo *)client_data;
  1148.     Widget w = info->widget;
  1149.  
  1150.     assert(info->timer == *id);
  1151.     info->timer = 0;
  1152.  
  1153. #if LOG_PUSH_MENUS
  1154.     clog << "popping up\n";
  1155. #endif
  1156.  
  1157.     XtRemoveCallback(w, XmNdisarmCallback,
  1158.              CancelPopupPushMenuCB, XtPointer(info));
  1159.     XtRemoveCallback(w, XmNactivateCallback,
  1160.              CancelPopupPushMenuCB, XtPointer(info));
  1161.  
  1162.     // Popup the menu
  1163.     XtCallActionProc(w, "popup-push-menu", &last_push_menu_event, 0, 0);
  1164.  
  1165.     // Unactivate pushbutton
  1166.     XtCallActionProc(w, "Disarm",          &last_push_menu_event, 0, 0);
  1167.  
  1168. #if XmVersion < 1002
  1169.     if (XtIsRealized(w))
  1170.     {
  1171.     // In Motif 1.1, the PushButton does not redisplay after being disarmed
  1172.     XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True);
  1173.     }
  1174. #endif
  1175. }
  1176.  
  1177. static void ReflattenButtonEH(Widget shell, XtPointer client_data, 
  1178.                   XEvent *event, Boolean *)
  1179. {
  1180.     switch (event->type)
  1181.     {
  1182.     case UnmapNotify:
  1183.     ReflattenButtonCB(shell, client_data);
  1184.     break;
  1185.  
  1186.     default:
  1187.     break;
  1188.     }
  1189. }
  1190.  
  1191.  
  1192. static void PopupPushMenuAct(Widget w, XEvent *event, String *, Cardinal *)
  1193. {
  1194.     if (!XmIsPushButton(w))
  1195.     return;
  1196.  
  1197.     void *userData = 0;
  1198.     XtVaGetValues(w, XmNuserData, &userData, XtPointer(0));
  1199.     if (userData == 0)
  1200.     return;
  1201.  
  1202.     PushMenuInfo *info = (PushMenuInfo *)userData;
  1203.  
  1204.     Widget shell = XtParent(info->subMenu);
  1205.  
  1206.     // Attempt to place menu below button
  1207.     Position button_x, button_y;
  1208.     XtTranslateCoords(w, 0, 0, &button_x, &button_y);
  1209.  
  1210.     Dimension height = 0;
  1211.     XtVaGetValues(w, XmNheight, &height, NULL);
  1212.  
  1213.     Position x = button_x;
  1214.     Position y = button_y + height;
  1215.  
  1216.     event->xbutton.x_root = x;
  1217.     event->xbutton.y_root = y;
  1218.     XmMenuPosition(info->subMenu, &event->xbutton);
  1219.  
  1220.     if (info->flat)
  1221.     {
  1222.     // Don't have the PushMenu interfere with flattening.  Disable
  1223.     // flattening until the menu is popped down again.
  1224.     EventMask event_mask = EnterWindowMask | LeaveWindowMask;
  1225.     XtRemoveEventHandler(w, event_mask, False, FlattenEH, XtPointer(0));
  1226.     XtRemoveCallback(w, XmNarmCallback,    FlattenCB, XtPointer(False));
  1227.     XtRemoveCallback(w, XmNdisarmCallback, FlattenCB, XtPointer(True));
  1228. #if 0
  1229.     XtAddCallback(shell, XtNpopdownCallback, ReflattenButtonCB, 
  1230.               XtPointer(w));
  1231. #else
  1232.     // XtNpopDownCallback isn't called by LessTif and unknown in
  1233.     // Motif 1.1.  *Sigh*.
  1234.     XtAddEventHandler(shell, StructureNotifyMask, False, 
  1235.               ReflattenButtonEH, XtPointer(w));
  1236. #endif
  1237.     }
  1238.  
  1239.     XtManageChild(info->subMenu);
  1240.     XtPopup(shell, XtGrabNone);
  1241. }
  1242.  
  1243. static void DecoratePushMenuAct(Widget w, XEvent */* event */, 
  1244.                 String *, Cardinal *)
  1245. {
  1246.     if (!XmIsPushButton(w) || !XtIsRealized(w))
  1247.     return;
  1248.  
  1249.     // clog << "Redraw " << XtName(w) << "\n";
  1250.  
  1251.     // Draw a little triangle in upper right corner
  1252.     XPoint points[4] = {
  1253.     {-6, 1}, {5, 0}, {-3, 3}, {-2, -3}
  1254.     };
  1255.  
  1256.     Dimension width;
  1257.     Dimension highlightThickness;
  1258.     Dimension shadowThickness;
  1259.     Pixel foreground;
  1260.     Pixel background;
  1261.     Colormap colormap;
  1262.     XtVaGetValues(w,
  1263.           XmNwidth, &width,
  1264.           XmNhighlightThickness, &highlightThickness, 
  1265.           XmNshadowThickness, &shadowThickness,
  1266.           XmNforeground, &foreground,
  1267.           XmNbackground, &background,
  1268.           XmNcolormap, &colormap,
  1269.           NULL);
  1270.  
  1271.     points[0].x += width - highlightThickness - shadowThickness;
  1272.     points[0].y += highlightThickness + shadowThickness;
  1273.  
  1274.     Pixel color;
  1275.     XmGetColors(XtScreen(w), colormap, background, 0, 0, &color, 0);
  1276.  
  1277.     XGCValues xgc;
  1278.     xgc.foreground = color;
  1279.     xgc.background = background;
  1280.     GC gc = XtGetGC(w, GCForeground | GCBackground, &xgc);
  1281.  
  1282.     XFillPolygon(XtDisplay(w), XtWindow(w), gc, points, XtNumber(points), 
  1283.          Convex, CoordModePrevious);
  1284. }
  1285.  
  1286.  
  1287.  
  1288. // Popup menu after some delay
  1289. struct subresource_values {
  1290.     int push_menu_popup_time;    // Delay before popping up menu
  1291. };
  1292.  
  1293. static XtResource subresources[] = {
  1294.     {
  1295.     XtNpushMenuPopupTime,
  1296.     XtCPushMenuPopupTime,
  1297.     XmRInt,
  1298.     sizeof(int),
  1299.     XtOffsetOf(subresource_values, push_menu_popup_time), 
  1300.     XmRImmediate,
  1301.     XtPointer(500)
  1302.     }
  1303. };
  1304.  
  1305. static void ArmPushMenuCB(Widget w, XtPointer client_data, XtPointer call_data)
  1306. {
  1307.     PushMenuInfo *info = (PushMenuInfo *)client_data;
  1308.  
  1309.     subresource_values values;
  1310.     XtGetApplicationResources(w, &values, 
  1311.                   subresources, XtNumber(subresources), 
  1312.                   NULL, 0);
  1313.  
  1314.     XmPushButtonCallbackStruct *cbs = (XmPushButtonCallbackStruct *)call_data;
  1315.     if (cbs && cbs->event)
  1316.     last_push_menu_event = *cbs->event;
  1317.  
  1318.     if (info->timer != 0)
  1319.     XtRemoveTimeOut(info->timer);
  1320.  
  1321.     info->timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w), 
  1322.                   values.push_menu_popup_time, 
  1323.                   PopupPushMenuCB, XtPointer(info));
  1324.  
  1325. #if LOG_PUSH_MENUS
  1326.     clog << "Waiting for " << XtName(w) << " menu " 
  1327.      << "(timer " << info->timer << ")...";
  1328. #endif
  1329.  
  1330.     // If we're disarmed or activated within the delay, don't popup.
  1331.     XtAddCallback(w, XmNdisarmCallback,   
  1332.           CancelPopupPushMenuCB, XtPointer(info));
  1333.     XtAddCallback(w, XmNactivateCallback, 
  1334.           CancelPopupPushMenuCB, XtPointer(info));
  1335. }
  1336.  
  1337. static void RedrawPushMenuCB(Widget w, XtPointer, XtPointer)
  1338. {
  1339.     XtCallActionProc(w, "decorate-push-menu", 0, 0, 0);
  1340. }
  1341.  
  1342.  
  1343. void set_sensitive(Widget w, bool state)
  1344. {
  1345.     if (w != 0)
  1346.     {
  1347.     XtSetSensitive(w, state);
  1348.  
  1349.     if (!state && w == active_button)
  1350.     {
  1351.         // We won't get the LeaveWindow event, since W is now
  1352.         // insensitive.  Flatten button explicitly.
  1353.         flatten_button(w);
  1354.     }
  1355.     }
  1356. }
  1357.