1 /**************************************************************************\
2  *
3  *  This file is part of the Coin 3D visualization library.
4  *  Copyright (C) by Kongsberg Oil & Gas Technologies.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  ("GPL") version 2 as published by the Free Software Foundation.
9  *  See the file LICENSE.GPL at the root directory of this source
10  *  distribution for additional information about the GNU GPL.
11  *
12  *  For using Coin with software that can not be combined with the GNU
13  *  GPL, and for taking advantage of the additional benefits of our
14  *  support services, please contact Kongsberg Oil & Gas Technologies
15  *  about acquiring a Coin Professional Edition License.
16  *
17  *  See http://www.coin3d.org/ for more information.
18  *
19  *  Kongsberg Oil & Gas Technologies, Bygdoy Alle 5, 0257 Oslo, NORWAY.
20  *  http://www.sim.no/  sales@sim.no  coin-support@coin3d.org
21  *
22 \**************************************************************************/
23 
24 #include <assert.h>
25 #include <stdio.h>
26 
27 #include <Xm/RowColumn.h>
28 #include <Xm/SeparatoG.h>
29 #include <Xm/PushBG.h>
30 #include <Xm/ToggleBG.h>
31 #include <Xm/ToggleB.h>
32 #include <Xm/CascadeBG.h>
33 
34 #include <Inventor/SoLists.h>
35 #include <Inventor/errors/SoDebugError.h>
36 
37 #include <soxtdefs.h>
38 #include <Inventor/Xt/SoXt.h>
39 #include <Inventor/Xt/widgets/XtNativePopupMenu.h>
40 
41 #define SOXT_POPUPMENU_DEBUG 0
42 
43 // *************************************************************************
44 
45 struct MenuRecord {
46   int menuid;
47   int pos;
48   char * name;
49   char * title;
50   Widget menu;
51   MenuRecord * parent;
52 }; // struct MenuRecord
53 
54 struct ItemRecord {
55   int itemid;
56   int flags;
57   int pos;
58   char * name;
59   char * title;
60   Widget item;
61   MenuRecord * parent;
62 }; // struct ItemRecord
63 
64 #define ITEM_MARKED       0x0001
65 #define ITEM_SEPARATOR    0x0002
66 #define ITEM_ENABLED      0x0004
67 
68 // *************************************************************************
69 
70 /*!
71   \class XtNativePopupMenu Inventor/Qt/widgets/XtNativePopupMenu.h
72   \brief The XtNativePopupMenu class implements a common interface for popup
73   menu management for all the Coin GUI toolkit libraries.
74 */
75 
76 // *************************************************************************
77 
XtNativePopupMenu(void)78 XtNativePopupMenu::XtNativePopupMenu(
79   void)
80 {
81   this->menus = new SbPList;
82   this->items = new SbPList;
83   this->dirty = TRUE;
84   this->popup = (Widget) NULL;
85 } // XtNativePopupMenu()
86 
~XtNativePopupMenu(void)87 XtNativePopupMenu::~XtNativePopupMenu(// virtual
88   void)
89 {
90   const int numMenus = this->menus->getLength();
91 //  QPopupMenu * popup = NULL;
92   int i;
93   for (i = 0; i < numMenus; i++) {
94     MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
95 //    if (rec->menuid == 0) popup = rec->menu;
96     delete [] rec->name;
97     delete [] rec->title;
98 //    if (rec->parent == NULL) delete rec->menu; // menu not attached
99     delete rec;
100   }
101 
102   const int numItems = this->items->getLength();
103   for (i = 0; i < numItems; i++) {
104     ItemRecord * rec = (ItemRecord *) (*this->items)[i];
105     delete [] rec->name;
106     delete [] rec->title;
107     delete rec;
108   }
109 
110   // delete root popup menu
111 //  delete popup;
112 } // ~XtNativePopupMenu()
113 
114 // *************************************************************************
115 
116 /*!
117 */
118 
119 int
newMenu(const char * name,int menuid)120 XtNativePopupMenu::newMenu(
121   const char * name,
122   int menuid)
123 {
124   int id = menuid;
125   if (id == -1) {
126     id = 1;
127     while (this->getMenuRecord(id) != NULL) id++;
128   } else {
129     if (this->getMenuRecord(id) != NULL) {
130 #if SOXT_DEBUG
131       SoDebugError::postInfo("XtNativePopupMenu::NewMenu",
132         "requested menuid already taken");
133 #endif // SOXT_DEBUG
134       return -1;
135     }
136   }
137   // id contains ok ID
138   MenuRecord * rec = this->createMenuRecord(name);
139   rec->menuid = id;
140   this->menus->append((void *) rec);
141   return id;
142 } // newMenu()
143 
144 /*!
145 */
146 
147 int
getMenu(const char * name)148 XtNativePopupMenu::getMenu(
149   const char * name)
150 {
151   const int numMenus = this->menus->getLength();
152   int i;
153   for (i = 0; i < numMenus; i++)
154     if (strcmp(((MenuRecord *) (*this->menus)[i])->name, name) == 0)
155       return ((MenuRecord *) (*this->menus)[i])->menuid;
156   return -1;
157 } // getMenu()
158 
159 /*!
160 */
161 
162 void
setMenuTitle(int menuid,const char * title)163 XtNativePopupMenu::setMenuTitle(
164   int menuid,
165   const char * title)
166 {
167   MenuRecord * rec = this->getMenuRecord(menuid);
168   if (rec == NULL) {
169     SoDebugError::postWarning("XtNativePopupMenu::SetMenuTitle",
170       "no such menu (%d.title = \"%s\")", menuid, title);
171     return;
172   }
173   delete [] rec->title;
174   rec->title = strcpy(new char [strlen(title)+1], title);
175 //  if (rec->parent)
176 //    rec->parent->changeItem(rec->menuid, QString(rec->title));
177 } // setMenuTitle()
178 
179 /*!
180 */
181 
182 const char *
getMenuTitle(int menuid)183 XtNativePopupMenu::getMenuTitle(
184   int menuid)
185 {
186   MenuRecord * rec = this->getMenuRecord(menuid);
187   if (rec == NULL)
188     return NULL;
189   return rec->title;
190 } // getMenuTitle()
191 
192 // *************************************************************************
193 
194 /*!
195 */
196 
197 int
newMenuItem(const char * name,int itemid)198 XtNativePopupMenu::newMenuItem(
199   const char * name,
200   int itemid)
201 {
202   // FIXME: this function is the same in the other So-libraries --
203   // move to common abstraction layer SoGuiPopupMenu of
204   // possible. 20031012 mortene.
205 
206   int id = itemid;
207   if (id == -1) {
208     id = 1;
209     while (this->getItemRecord(id) != NULL) id++;
210   } else {
211     if (this->getItemRecord(itemid) != NULL) {
212 #if SOXT_DEBUG
213       SoDebugError::postInfo("XtNativePopupMenu::NewMenuItem",
214         "requested itemid already taken");
215 #endif // SOXT_DEBUG
216       return -1;
217     }
218   }
219   ItemRecord * rec = this->createItemRecord(name);
220   rec->itemid = id;
221   this->items->append(rec);
222   return id;
223 } // newMenuItem()
224 
225 /*!
226 */
227 
228 int
getMenuItem(const char * name)229 XtNativePopupMenu::getMenuItem(
230   const char * name)
231 {
232   const int numItems = this->items->getLength();
233   int i;
234   for (i = 0; i < numItems; i++)
235     if (strcmp(((ItemRecord *) (*this->items)[i])->name, name) == 0)
236       return ((ItemRecord *) (*this->items)[i])->itemid;
237   return -1;
238 } // getMenuItem()
239 
240 /*!
241 */
242 
243 void
setMenuItemTitle(int itemid,const char * title)244 XtNativePopupMenu::setMenuItemTitle(
245   int itemid,
246   const char * title)
247 {
248   ItemRecord * rec = this->getItemRecord(itemid);
249   if (rec == NULL)
250     return;
251   delete [] rec->title;
252   rec->title = strcpy(new char [strlen(title)+1], title);
253 //  if (rec->parent)
254 //    rec->parent->changeItem(rec->itemid, QString(rec->title));
255 } // setMenuItemTitle()
256 
257 /*!
258 */
259 
260 const char *
getMenuItemTitle(int itemid)261 XtNativePopupMenu::getMenuItemTitle(
262   int itemid)
263 {
264   ItemRecord * rec = this->getItemRecord(itemid);
265   if (rec == NULL) return NULL;
266   return rec->title;
267 } // getMenuItemTitle()
268 
269 /*!
270 */
271 
272 void
setMenuItemEnabled(int itemid,SbBool enabled)273 XtNativePopupMenu::setMenuItemEnabled(// virtual
274   int itemid,
275   SbBool enabled)
276 {
277   ItemRecord * rec = this->getItemRecord(itemid);
278   if (rec == NULL) {
279 #if SOXT_DEBUG
280     SoDebugError::postInfo("XtNativePopupMenu::SetMenuItemEnabled",
281       "no such menu item");
282 #endif // SOXT_DEBUG
283     return;
284   }
285   if (enabled)
286     rec->flags |= ITEM_ENABLED;
287   else
288     rec->flags &= ~ITEM_ENABLED;
289   if (rec->item != (Widget) NULL)
290     XtVaSetValues(rec->item, XmNsensitive, enabled ? True : False, NULL);
291 } // setMenuItemEnabled()
292 
293 /*!
294 */
295 
296 SbBool
getMenuItemEnabled(int itemid)297 XtNativePopupMenu::getMenuItemEnabled(
298   int itemid)
299 {
300   ItemRecord * rec = this->getItemRecord(itemid);
301   if (rec == NULL)
302     return FALSE;
303   return (rec->flags & ITEM_ENABLED) ? TRUE : FALSE;
304 } // getMenuItemEnabled()
305 
306 /*!
307 */
308 
309 void
_setMenuItemMarked(int itemid,SbBool marked)310 XtNativePopupMenu::_setMenuItemMarked(int itemid, SbBool marked)
311 {
312   ItemRecord * rec = this->getItemRecord(itemid);
313   if (rec == NULL)
314     return;
315   if (marked)
316     rec->flags |= ITEM_MARKED;
317   else
318     rec->flags &= ~ITEM_MARKED;
319 
320   if (rec->item != NULL)
321     XmToggleButtonSetState(rec->item, marked ? True : False, False);
322 }
323 
324 /*!
325 */
326 
327 SbBool
getMenuItemMarked(int itemid)328 XtNativePopupMenu::getMenuItemMarked(
329   int itemid)
330 {
331   ItemRecord * rec = this->getItemRecord(itemid);
332   if (rec == NULL)
333     return FALSE;
334   return (rec->flags & ITEM_MARKED) ? TRUE : FALSE;
335 } // getMenuItemMarked()
336 
337 // *************************************************************************
338 
339 /*!
340 */
341 
342 void
addMenu(int menuid,int submenuid,int pos)343 XtNativePopupMenu::addMenu(
344   int menuid,
345   int submenuid,
346   int pos)
347 {
348   MenuRecord * super = this->getMenuRecord(menuid);
349   MenuRecord * sub = this->getMenuRecord(submenuid);
350   if (super == NULL || sub == NULL) {
351 #if SOXT_DEBUG
352     SoDebugError::postInfo("XtNativePopupMenu::AddMenu",
353       "no such menu (super = 0x%08x, sub = 0x%08x)", super, sub);
354 #endif // SOXT_DEBUG
355     return;
356   }
357   if (pos == -1) {
358     int max = 0;
359     int i;
360     const int numItems = this->items->getLength();
361     for (i = 0; i < numItems; i++) {
362       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
363       if (rec->parent == super) {
364         if (rec->pos >= max)
365           max = rec->pos + 1;
366       }
367     }
368     const int numMenus = this->menus->getLength();
369     for (i = 0; i < numMenus; i++) {
370       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
371       if (rec->parent == super) {
372         if (rec->pos >= max)
373           max = rec->pos + 1;
374       }
375     }
376     sub->pos = max;
377     sub->parent = super;
378   } else {
379     int i;
380     const int numItems = this->items->getLength();
381     for (i = 0; i < numItems; i++) {
382       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
383       if (rec->parent == super) {
384         if (rec->pos >= pos)
385           rec->pos = rec->pos + 1;
386       }
387     }
388     const int numMenus = this->menus->getLength();
389     for (i = 0; i < numMenus; i++) {
390       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
391       if (rec->parent == super) {
392         if (rec->pos >= pos)
393           rec->pos = rec->pos + 1;
394       }
395     }
396     sub->pos = pos;
397     sub->parent = super;
398   }
399 } // addMenu()
400 
401 /*!
402 */
403 
404 void
addMenuItem(int menuid,int itemid,int pos)405 XtNativePopupMenu::addMenuItem(
406   int menuid,
407   int itemid,
408   int pos)
409 {
410   MenuRecord * menu = this->getMenuRecord(menuid);
411   ItemRecord * item = this->getItemRecord(itemid);
412   if (menu == NULL || item == NULL) {
413 #if SOXT_DEBUG
414     SoDebugError::postInfo("XtNativePopupMenu::AddMenuItem",
415       "no such item (menu = 0x%08x, item = 0x%08x)", menu, item);
416 #endif // SOXT_DEBUG
417     return;
418   }
419   if (pos == -1) {
420     int max = 0;
421     int i;
422     const int numItems = this->items->getLength();
423     for (i = 0; i < numItems; i++) {
424       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
425       if (rec->parent == menu) {
426         if (rec->pos >= max)
427           max = rec->pos + 1;
428       }
429     }
430     const int numMenus = this->menus->getLength();
431     for (i = 0; i < numMenus; i++) {
432       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
433       if (rec->parent == menu) {
434         if (rec->pos >= max)
435           max = rec->pos + 1;
436       }
437     }
438     item->pos = max;
439     item->parent = menu;
440   } else {
441     int i;
442     const int numItems = this->items->getLength();
443     for (i = 0; i < numItems; i++) {
444       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
445       if (rec->parent == menu) {
446         if (rec->pos >= pos)
447           rec->pos = rec->pos + 1;
448       }
449     }
450     const int numMenus = this->menus->getLength();
451     for (i = 0; i < numMenus; i++) {
452       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
453       if (rec->parent == menu) {
454         if (rec->pos >= pos)
455           rec->pos = rec->pos + 1;
456       }
457     }
458     item->pos = pos;
459     item->parent = menu;
460   }
461 } // addMenuItem()
462 
463 void
addSeparator(int menuid,int pos)464 XtNativePopupMenu::addSeparator(
465   int menuid,
466   int pos)
467 {
468   MenuRecord * menu = this->getMenuRecord(menuid);
469   if (menu == NULL) {
470     SoDebugError::postWarning("XtNativePopupMenu::AddSeparator",
471       "no such menu (%d)", menuid);
472     return;
473   }
474   ItemRecord * sep = this->createItemRecord("separator");
475   sep->flags |= ITEM_SEPARATOR;
476   if (pos == -1) {
477     int max = 0;
478     int i;
479     const int numItems = this->items->getLength();
480     for (i = 0; i < numItems; i++) {
481       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
482       if (rec->parent == menu) {
483         if (rec->pos >= max)
484           max = rec->pos + 1;
485       }
486     }
487     const int numMenus = this->menus->getLength();
488     for (i = 0; i < numMenus; i++) {
489       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
490       if (rec->parent == menu) {
491         if (rec->pos >= max)
492           max = rec->pos + 1;
493       }
494     }
495     sep->pos = max;
496     sep->parent = menu;
497   } else {
498     int i;
499     const int numItems = this->items->getLength();
500     for (i = 0; i < numItems; i++) {
501       ItemRecord * rec = (ItemRecord *) (*this->items)[i];
502       if (rec->parent == menu) {
503         if (rec->pos >= pos)
504           rec->pos = rec->pos + 1;
505       }
506     }
507     const int numMenus = this->menus->getLength();
508     for (i = 0; i < numMenus; i++) {
509       MenuRecord * rec = (MenuRecord *) (*this->menus)[i];
510       if (rec->parent == menu) {
511         if (rec->pos >= pos)
512           rec->pos = rec->pos + 1;
513       }
514     }
515     sep->pos = pos;
516     sep->parent = menu;
517   }
518   this->items->append(sep);
519 } // addSeparator()
520 
521 /*!
522   This method removes the submenu with the given \a menuid.
523 
524   A removed menu can be attached again later - its menuid will still be
525   allocated.
526 */
527 
528 void
removeMenu(int menuid)529 XtNativePopupMenu::removeMenu(
530   int menuid)
531 {
532   MenuRecord * rec = this->getMenuRecord(menuid);
533   if (rec == NULL) {
534 #if SOXT_DEBUG
535     SoDebugError::postInfo("XtNativePopupMenu::RemoveMenu", "no such menu");
536 #endif // SOXT_DEBUG
537     return;
538   }
539   if (rec->menuid == 0) {
540 #if SOXT_DEBUG
541     SoDebugError::postInfo("XtNativePopupMenu::RemoveMenu", "can't remove root");
542 #endif // SOXT_DEBUG
543     return;
544   }
545   // if (rec->parent == NULL) {
546 // #if SOXT_DEBUG
547   //   SoDebugError::postInfo("XtNativePopupMenu::RemoveMenu", "menu not attached");
548 // #endif // SOXT_DEBUG
549   //   return;
550   // }
551   // rec->parent->removeItem(rec->menuid);
552   // rec->parent = NULL;
553 } // removeMenu()
554 
555 /*!
556   This method removes the menu item with the given \a itemid.
557 
558   A removed menu item can be attached again later - its itemid will still
559   be allocated.
560 */
561 
562 void
removeMenuItem(int itemid)563 XtNativePopupMenu::removeMenuItem(
564   int itemid)
565 {
566   ItemRecord * rec = this->getItemRecord(itemid);
567   if (rec == NULL) {
568 #if SOXT_DEBUG
569     SoDebugError::postInfo("XtNativePopupMenu::RemoveMenu", "no such item");
570 #endif // SOXT_DEBUG
571     return;
572   }
573   int idx = this->items->find(rec);
574   assert(idx != -1);
575   this->items->removeFast(idx);
576   delete [] rec->name;
577   delete [] rec->title;
578   delete rec;
579   this->dirty = TRUE;
580 } // removeMenuItem()
581 
582 // *************************************************************************
583 
584 // Doc in superclass.
585 void
popUp(Widget inside,int x,int y)586 XtNativePopupMenu::popUp(Widget inside, int x, int y)
587 {
588   assert(inside != NULL);
589 
590   MenuRecord * root = this->getMenuRecord(0);
591   if (root == NULL) {
592 #if SOXT_DEBUG
593     SoDebugError::postInfo("XtNativePopupMenu::PopUp", "no root menu");
594 #endif // SOXT_DEBUG
595     return;
596   }
597   // FIXME: build menu
598   if (this->dirty) {
599     if (this->popup != (Widget) NULL) {
600       // FIXME: destroy existing menu widget hierarchy
601     }
602     this->popup = this->build(inside);
603   }
604   this->dirty = FALSE;
605 
606   // Find global mouse pointer coordinates.
607   Display * display = XtDisplay(inside);
608   XButtonEvent pos;
609   Window dummyarg;
610   Bool b = XTranslateCoordinates(display,
611                                  XtWindow(inside),
612                                  DefaultRootWindow(display),
613                                  x, y, &pos.x_root, &pos.y_root, &dummyarg);
614   assert(b == True); // or we've got a bug
615 
616   XmMenuPosition(this->popup, &pos);
617   XtManageChild(this->popup);
618 } // popUp()
619 
620 // *************************************************************************
621 
622 /*!
623 */
624 
625 Widget
traverseBuild(Widget parent,MenuRecord * menu,int indent)626 XtNativePopupMenu::traverseBuild(
627   Widget parent,
628   MenuRecord * menu,
629   int indent)
630 {
631   assert(indent < 24);
632   int i;
633 #if SOXT_POPUPMENU_DEBUG
634   char pre[24];
635   for (i = 0; i < indent; i++) pre[i] = ' ';
636   pre[i] = '\0';
637 #endif // SOXT_POPUPMENU_DEBUG
638   int j = 0;
639   MenuRecord * sub;
640   ItemRecord * item;
641   do {
642     sub = (MenuRecord *) NULL;
643     item = (ItemRecord *) NULL;
644     const int numMenus = this->menus->getLength();
645     for (i = 0; i < numMenus; i++) {
646       sub = (MenuRecord *) (*this->menus)[i];
647       if ((sub->pos == j) && (sub->parent == menu)) {
648 #if SOXT_POPUPMENU_DEBUG
649         fprintf(stderr, "%s%s {\n", pre, sub->name);
650 #endif // SOXT_POPUPMENU_DEBUG
651         Display * dpy = SoXt::getDisplay();
652         const int screen = DefaultScreen(dpy);
653         Visual * visual = DefaultVisual(dpy, screen);
654         Colormap colormap = DefaultColormap(dpy, screen);
655         int depth = DefaultDepth(dpy, screen);
656         Arg args[10];
657         int argc = 0;
658         XtSetArg(args[argc], XmNvisual, visual); argc++;
659         XtSetArg(args[argc], XmNdepth, depth); argc++;
660         XtSetArg(args[argc], XmNcolormap, colormap); argc++;
661         Widget submenu = XmCreatePulldownMenu(parent, sub->name, args, argc);
662         sub->menu = XtVaCreateManagedWidget(sub->name,
663           xmCascadeButtonGadgetClass, parent,
664           XmNsubMenuId, submenu,
665           XtVaTypedArg,
666             XmNlabelString, XmRString,
667             sub->title, strlen(sub->title) + 1,
668           NULL);
669         this->traverseBuild(submenu, sub, indent + 2);
670 #if SOXT_POPUPMENU_DEBUG
671         fprintf(stderr, "%s}\n", pre);
672 #endif // SOXT_POPUPMENU_DEBUG
673         break;
674       } else {
675         sub = (MenuRecord *) NULL;
676       }
677     }
678     if (sub == NULL) {
679       const int numItems = this->items->getLength();
680       for (i = 0; i < numItems; i++) {
681         item = (ItemRecord *) (*this->items)[i];
682         if ((item->pos == j) && (item->parent == menu)) {
683 #if SOXT_POPUPMENU_DEBUG
684           fprintf(stderr, "%s%s\n", pre, item->name);
685 #endif // SOXT_POPUPMENU_DEBUG
686           if (item->flags & ITEM_SEPARATOR) {
687             item->item = XtVaCreateManagedWidget(item->title,
688               xmSeparatorGadgetClass, parent, NULL);
689           } else {
690             item->item = XtVaCreateManagedWidget(item->title,
691               xmToggleButtonGadgetClass, parent,
692               XmNsensitive, (item->flags & ITEM_ENABLED) ? True : False,
693               XtVaTypedArg,
694                 XmNlabelString, XmRString,
695                 item->title, strlen(item->title)+1,
696               NULL);
697             XtAddCallback(item->item, XmNvalueChangedCallback,
698                 XtNativePopupMenu::itemSelectionCallback, this);
699             XmToggleButtonSetState(item->item,
700               (item->flags & ITEM_MARKED) ? True : False,
701               False);
702           }
703           break;
704         } else {
705           item = (ItemRecord *) NULL;
706         }
707       }
708     }
709     j++;
710   } while ((sub != NULL) || (item != NULL));
711   return parent;
712 } // traverseBuild()
713 
714 /*!
715 */
716 
717 Widget
build(Widget parent)718 XtNativePopupMenu::build(
719   Widget parent)
720 {
721   MenuRecord * root = this->getMenuRecord(0);
722   assert(root != NULL);
723 
724 #if SOXT_POPUPMENU_DEBUG
725   setbuf(stderr, NULL);
726   fprintf(stderr, "building popup menu\n");
727 #endif // SOXT_POPUPMENU_DEBUG
728 
729   Display * dpy = SoXt::getDisplay();
730   const int screen = DefaultScreen(dpy);
731   Visual * visual = DefaultVisual(dpy, screen);
732   Colormap colormap = DefaultColormap(dpy, screen);
733   int depth = DefaultDepth(dpy, screen);
734 
735   Arg args[10];
736   int argc = 0;
737   XtSetArg(args[argc], XmNvisual, visual); argc++;
738   XtSetArg(args[argc], XmNdepth, depth); argc++;
739   XtSetArg(args[argc], XmNcolormap, colormap); argc++;
740   Widget popup = XmCreatePopupMenu(parent, root->name, args, argc);
741 
742 #if SOXT_POPUPMENU_DEBUG
743   fprintf(stderr, "%s {\n", root->name);
744 #endif // SOXT_POPUPMENU_DEBUG
745   (void) this->traverseBuild(popup, root, 2);
746 #if SOXT_POPUPMENU_DEBUG
747   fprintf(stderr, "}\n");
748 #endif // SOXT_POPUPMENU_DEBUG
749   return popup;
750 } // build()
751 
752 // *************************************************************************
753 
754 /*!
755 */
756 
757 MenuRecord *
getMenuRecord(int menuid)758 XtNativePopupMenu::getMenuRecord(
759   int menuid)
760 {
761   const int numMenus = this->menus->getLength();
762   int i;
763   for (i = 0; i < numMenus; i++)
764     if (((MenuRecord *) (*this->menus)[i])->menuid == menuid)
765       return (MenuRecord *) (*this->menus)[i];
766   return (MenuRecord *) NULL;
767 } // getMenuRecord()
768 
769 /*!
770 */
771 
772 ItemRecord *
getItemRecord(int itemid)773 XtNativePopupMenu::getItemRecord(
774   int itemid)
775 {
776   const int numItems = this->items->getLength();
777   int i;
778   for (i = 0; i < numItems; i++)
779     if (((ItemRecord *) (*this->items)[i])->itemid == itemid)
780       return (ItemRecord *) (*this->items)[i];
781   return (ItemRecord *) NULL;
782 } // getItemRecord()
783 
784 // *************************************************************************
785 
786 /*!
787 */
788 
789 MenuRecord *
createMenuRecord(const char * name)790 XtNativePopupMenu::createMenuRecord(
791   const char * name)
792 {
793   MenuRecord * rec = new MenuRecord;
794   rec->menuid = -1;
795   rec->pos = -1;
796   rec->name = strcpy(new char [strlen(name)+1], name);
797   rec->title = strcpy(new char [strlen(name)+1], name);
798   rec->menu = (Widget) NULL;
799   rec->parent = NULL;
800   return rec;
801 } // create()
802 
803 /*!
804 */
805 
806 ItemRecord *
createItemRecord(const char * name)807 XtNativePopupMenu::createItemRecord(
808   const char * name)
809 {
810   ItemRecord * rec = new ItemRecord;
811   rec->itemid = -1;
812   rec->pos = -1;
813   rec->flags = 0 | ITEM_ENABLED;
814   rec->name = strcpy(new char [strlen(name)+1], name);
815   rec->title = strcpy(new char [strlen(name)+1], name);
816   rec->item = (Widget) NULL;
817   rec->parent = NULL;
818   return rec;
819 } // create()
820 
821 // *************************************************************************
822 
823 /*!
824 */
825 
826 void
itemSelection(Widget w,XtPointer call)827 XtNativePopupMenu::itemSelection(// private
828   Widget w,
829   XtPointer call)
830 {
831   if (w == NULL)
832     return;
833   XmToggleButtonCallbackStruct * data = (XmToggleButtonCallbackStruct *) call;
834   const int numItems = this->items->getLength();
835   int i;
836   for (i = 0; i < numItems; i++) {
837     ItemRecord * rec = (ItemRecord *) (*this->items)[i];
838     if (rec->item == w) {
839       int groupid = this->getRadioGroup(rec->itemid);
840       if (data->set && groupid != -1) {
841         this->setMenuItemMarked(rec->itemid, TRUE);
842         this->invokeMenuSelection(rec->itemid);
843       } else {
844         if (groupid == -1) {
845           this->setMenuItemMarked(rec->itemid, FALSE);
846           this->invokeMenuSelection(rec->itemid);
847         } else if (this->getRadioGroupSize(groupid) > 1) {
848           this->setMenuItemMarked(rec->itemid, TRUE);
849           this->invokeMenuSelection(rec->itemid);
850         } else {
851           this->setMenuItemMarked(rec->itemid, FALSE);
852           this->invokeMenuSelection(rec->itemid);
853         }
854       }
855     }
856   }
857 } // itemSelection()
858 
859 /*!
860 */
861 
862 void
itemSelectionCallback(Widget w,XtPointer client_data,XtPointer call_data)863 XtNativePopupMenu::itemSelectionCallback(// private, static
864   Widget w,
865   XtPointer client_data,
866   XtPointer call_data)
867 {
868   assert(client_data != NULL);
869   XtNativePopupMenu * popup = (XtNativePopupMenu *) client_data;
870   popup->itemSelection(w, call_data);
871 } // itemSelectionCallback()
872 
873 // *************************************************************************
874 
875