Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtWidget / MenuButton.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: MenuButton.c /main/9 1998/04/09 17:51:40 mgreess $
24  *
25  * (c) Copyright 1996 Digital Equipment Corporation.
26  * (c) Copyright 1996 Hewlett-Packard Company.
27  * (c) Copyright 1996 International Business Machines Corp.
28  * (c) Copyright 1986, 1991, 1996 Sun Microsystems, Inc.
29  * (c) Copyright 1996 Novell, Inc. 
30  * (c) Copyright 1996 FUJITSU LIMITED.
31  * (c) Copyright 1996 Hitachi.
32  */
33
34 /*
35  *        Copyright (C) 1986,1991  Sun Microsystems, Inc
36  *                    All rights reserved.
37  *          Notice of copyright on this source code
38  *          product does not indicate publication.
39  *
40  * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by
41  * the U.S. Government is subject to restrictions as set forth
42  * in subparagraph (c)(1)(ii) of the Rights in Technical Data
43  * and Computer Software Clause at DFARS 252.227-7013 (Oct. 1988)
44  * and FAR 52.227-19 (c) (June 1987).
45  *
46  *    Sun Microsystems, Inc., 2550 Garcia Avenue,
47  *    Mountain View, California 94043.
48  *
49  */
50 /*
51  * The DtMenuButton widget is rigged with the Motif widget binary compatibilit
52  * mechanism. All Motif-specific changes for this mechanism are preceded
53  * by a comment including the string "MotifBc".
54  *
55  * For a description of the Motif widget binary compatibility mechanism
56  * see the reference manual entry on XmResolveAllPartOffsets().
57  *
58  */
59
60 #include <Dt/DtMsgsP.h>
61 #include "MenuButtonP.h"
62
63 #include <Xm/LabelP.h>
64 #include <Xm/MenuUtilP.h>
65 #include <Xm/DrawP.h>
66
67 #include <X11/keysymdef.h>
68 #include "DtWidgetI.h"
69
70 /*
71  * MotifBc
72  */
73 #define DtMenuButtonIndex (XmLabelIndex + 1)
74 static XmOffsetPtr ipot; /* Instance part offset table */
75 static XmOffsetPtr cpot; /* Constraint part offset table */
76
77 #ifndef Max
78 #define Max(x, y)       (((x) > (y)) ? (x) : (y))
79 #endif
80
81 #define GLYPH_PIX_SPACE    4     /* pixels between label and bit map */
82
83 /*
84  * MotifBc
85  */
86 #define CascadingCallback(w) XmField(w,ipot,DtMenuButton,cascading_callback,XtCallbackList)
87 #define MenuRect(w) XmField(w,ipot,DtMenuButton,menu_rect,XRectangle)
88 #define LNormalGC(w) XmField(w,ipot,XmLabel,normal_GC,GC)
89 #define LAlignment(w) XmField(w,ipot,XmLabel,alignment,unsigned char)
90 #define LRecomputeSize(w) XmField(w,ipot,XmLabel,recompute_size,Boolean)
91 #define LLabelType(w) XmField(w,ipot,XmLabel,label_type,unsigned char)
92
93 /* Access macro definitions */
94 #define MB_PVT_SMENU(mb)        XmField(mb,ipot,DtMenuButton,private_submenu,Boolean)
95 #define MB_LAST_TIMESTAMP(mb)   XmField(mb,ipot,DtMenuButton,last_timestamp,Time)
96 #define MB_SMENU(mb)            XmField(mb,ipot,DtMenuButton,submenu,Widget)
97 #define MB_PIXMAP(mb)           XmField(mb,ipot,DtMenuButton,menu_pixmap,Pixmap)
98 #define MB_GLYPH_X(mb)          (XmField(mb,ipot,DtMenuButton,menu_rect,XRectangle)).x
99 #define MB_GLYPH_Y(mb)          (XmField(mb,ipot,DtMenuButton,menu_rect,XRectangle)).y
100 #define MB_GLYPH_WIDTH(mb)      (XmField(mb,ipot,DtMenuButton,menu_rect,XRectangle)).width
101 #define MB_GLYPH_HEIGHT(mb)     (XmField(mb,ipot,DtMenuButton,menu_rect,XRectangle)).height
102 #define MB_ARMED(mb)            XmField(mb,ipot,DtMenuButton,armed,Boolean)
103 #define MB_POPPED_UP(mb)        XmField(mb,ipot,DtMenuButton,popped_up,Boolean)
104 #define MB_GC(mb)               XmField(mb,ipot,DtMenuButton,gc,GC)
105
106
107 /********    Static Function Declarations    ********/
108 static void     ClassInitialize (void);
109
110 static void AdjustMenuButtonSize( 
111         DtMenuButtonWidget menubtn,
112         Boolean adjustWidth,
113         Boolean adjustHeight) ;
114 static void Arm( 
115         DtMenuButtonWidget mb) ;
116 static void ArmAndActivate( 
117         Widget wid,
118         XEvent *event,
119         String *params,
120         Cardinal *num_params) ;
121
122 static void CalculateMenuGlyphSize( 
123         DtMenuButtonWidget menubtn) ;
124 static void CallCascadingCallbacks( 
125         Widget w,
126         XEvent *event) ;
127
128 static void Destroy( 
129         Widget wid) ;
130 static void Disarm( 
131         DtMenuButtonWidget mb,
132         Boolean pop_down) ;
133 static void DrawArrow(
134         Widget wid);
135 static void Draw3DShadows( 
136         DtMenuButtonWidget mb) ;
137 static void DrawMenuGlyph( 
138         DtMenuButtonWidget mb) ;
139
140 static void GetGC(
141         DtMenuButtonWidget mb);
142
143 static void Initialize( 
144         Widget w_req,
145         Widget w_new,
146         ArgList args,
147         Cardinal *num_args) ;
148
149 static void LocateMenuGlyph( 
150         DtMenuButtonWidget menubtn) ;
151
152 static void MenuButtonHandler( 
153         Widget wid,
154         XtPointer cd,   
155         XEvent *event,
156         Boolean *cont_to_dispatch) ;
157
158 static void Popup(
159         DtMenuButtonWidget mb,
160         XEvent *event,
161         Boolean call_cbacks );
162
163 static void Redisplay( 
164         Widget mb,
165         XEvent *event,
166         Region region) ;
167 static void Resize( 
168         Widget mb) ;
169
170 static void Select( 
171         Widget wid,
172         XEvent *event,
173         String *param,
174         Cardinal *num_param) ;
175 static Boolean SetValues( 
176         Widget cw,
177         Widget rw,
178         Widget nw,
179         ArgList args,
180         Cardinal *num_args) ;
181 static void GetTopManager(
182         Widget w,
183         Widget *topManager) ;
184
185
186 /********    End Static Function Declarations    ********/
187
188
189 /*
190  * event translation tables for menubutton.
191  */
192
193 static char menuButton_translations[] = "\
194         <Key>space:Select()\n\
195         <Key>osfSelect:Select()";
196
197 static XtActionsRec menuButton_actions [] = {
198         {"Select",      Select}
199 };
200         
201 /* MotifBc */
202 #define DtOffset(field) XmPartOffset(DtMenuButton,field)
203 #define XmPrimOffset(field) XmPartOffset(XmPrimitive,field)
204 #define XmLabelOffset(field) XmPartOffset(XmLabel,field)
205 static XmPartResource resources[] = {
206         {       
207         DtNcascadingCallback, 
208         DtCCallback, 
209         XmRCallback,
210         sizeof (XtCallbackList),
211         DtOffset(cascading_callback), 
212         XmRCallback,
213         NULL
214         },
215         {       
216         DtNsubMenuId, 
217         DtCMenuWidget,                          /* submenu */
218         XmRMenuWidget, 
219         sizeof (Widget),
220         DtOffset(submenu), 
221         XmRMenuWidget, 
222         (XtPointer) NULL
223         },
224         {       
225         DtNcascadePixmap, 
226         DtCPixmap, 
227         XmRPrimForegroundPixmap,
228         sizeof(Pixmap),
229         DtOffset(menu_pixmap), 
230         XmRImmediate,
231         (XtPointer) XmUNSPECIFIED_PIXMAP
232         },
233         {
234         XmNshadowThickness,
235         XmCShadowThickness,
236         XmRHorizontalDimension,
237         sizeof (Dimension),
238         XmPrimOffset(shadow_thickness),
239         XmRImmediate,
240         (XtPointer) 2
241         },
242         {
243         XmNtraversalOn,
244         XmCTraversalOn,
245         XmRBoolean,
246         sizeof(Boolean),
247         XmPrimOffset(traversal_on),
248         XmRImmediate,
249         (XtPointer) TRUE
250         },
251         {
252         XmNhighlightThickness,
253         XmCHighlightThickness,
254         XmRHorizontalDimension,
255         sizeof (Dimension),
256         XmPrimOffset(highlight_thickness),
257         XmRImmediate,
258         (XtPointer) 2
259         },
260         {
261         XmNmarginWidth, 
262         XmCMarginWidth, 
263         XmRHorizontalDimension, 
264         sizeof (Dimension),
265         XmLabelOffset(margin_width), 
266         XmRImmediate,
267         (XtPointer)6 
268         },
269         };       
270         
271 externaldef(xmmenubuttonclassrec) 
272         DtMenuButtonClassRec dtMenuButtonClassRec = {
273         {                       /* core class record */
274         (WidgetClass) &xmLabelClassRec,         /* superclass ptr       */
275         "DtMenuButton",                         /* class_name   */
276         sizeof(DtMenuButtonPart),               /* size of Pulldown widget */
277         (XtProc)ClassInitialize,                /* class init proc */
278         NULL,                                   /* chained class init */
279         FALSE,                                  /* class is not init'ed */
280         Initialize,                             /* widget init proc */
281         NULL,                                   /* init_hook proc */
282         XtInheritRealize,                       /* widget realize proc */
283         menuButton_actions,                     /* class action table */
284         XtNumber (menuButton_actions),          /* num of actions */
285         (XtResourceList)resources,              /* this class's resource list*/
286         XtNumber (resources),                   /* resource_count */
287         NULLQUARK,                              /* xrm_class            */
288         TRUE,                                   /* compress motion */
289         XtExposeCompressMaximal,                /* compress exposure */
290         TRUE,                                   /* compress enter-leave */
291         FALSE,                                  /* no VisibilityNotify */
292         Destroy,                                /* class destroy proc */
293         Resize,                                 /* class resize proc */
294         Redisplay,                              /* expose proc */
295         SetValues,                              /* set_value proc */
296         NULL,                                   /* set_value_hook proc */
297         XtInheritSetValuesAlmost,               /* set_value_almost proc */
298         NULL,                                   /* get_values_hook */
299         NULL,                                   /* class accept focus proc */
300         XtVersionDontCheck,                     /* current version */
301         NULL,                                   /* callback offset list */
302         menuButton_translations,                /* default translation table */
303         XtInheritQueryGeometry,                 /* query geo proc */
304         NULL,                                   /* display accelerator*/
305         (XtPointer)NULL,                        /* extension */
306         },
307         {
308                         /* Primitive Class record */
309         XmInheritWidgetProc,                    /* border_highlight */
310         XmInheritWidgetProc,                    /* border_uhighlight */
311         XtInheritTranslations,                  /* translations */
312         ArmAndActivate,                         /* arm & activate */
313         NULL,                                   /* get resources */
314         0,                                      /* num get_resources */
315         (XtPointer)NULL,                        /* extension */
316         },
317         {                       /* Label Class record */
318         XmInheritWidgetProc,                    /* set override callback */
319         XmInheritMenuProc,                      /* menu procedures       */
320         XtInheritTranslations,                  /* menu traversal xlation */
321         NULL,                                   /* extension */
322         },
323         {                       /* menu_button class record */
324             NULL,                               /* extension */  
325         }
326 };
327         
328         
329 /*
330  * Now make a public symbol that points to this class record.
331  */
332         
333 externaldef(dtmenubuttonwidgetclass) 
334         WidgetClass dtMenuButtonWidgetClass = 
335                 (WidgetClass) &dtMenuButtonClassRec;
336         
337 /*
338  * MotifBc, to calculate offset var. ipot
339  */
340 static void
341 ClassInitialize(void)
342 {
343     XmResolveAllPartOffsets(dtMenuButtonWidgetClass, &ipot, &cpot);
344 }
345
346 static void 
347 Draw3DShadows(
348     DtMenuButtonWidget mb )
349 {
350         if (XtIsRealized((Widget)mb))
351           XmeDrawShadows (XtDisplay (mb), XtWindow (mb),
352                 mb->primitive.top_shadow_GC,
353                 mb->primitive.bottom_shadow_GC,
354                 mb->primitive.highlight_thickness,
355                 mb->primitive.highlight_thickness,
356                 mb->core.width - 2 * 
357                 mb->primitive.highlight_thickness,
358                 mb->core.height - 2 * 
359                 mb->primitive.highlight_thickness,
360                 mb->primitive.shadow_thickness,
361                 (MB_ARMED(mb) == TRUE) ? XmSHADOW_IN: XmSHADOW_OUT);
362 }
363         
364         
365 static void 
366 DrawMenuGlyph(
367     DtMenuButtonWidget mb )
368 {
369         if ((MB_GLYPH_WIDTH(mb) != 0))  
370                 if(MB_PIXMAP(mb) != XmUNSPECIFIED_PIXMAP) 
371                         XCopyArea (XtDisplay(mb), 
372                                 MB_PIXMAP(mb), 
373                                 XtWindow(mb),
374                                 LNormalGC(mb), 0, 0, 
375                                 MB_GLYPH_WIDTH(mb), MB_GLYPH_HEIGHT(mb),
376                                 MB_GLYPH_X(mb), MB_GLYPH_Y(mb));
377                 else 
378                         DrawArrow((Widget)mb);  
379 }
380         
381 /*
382  * Redisplay the widget.
383  */
384 static void 
385 Redisplay(
386     Widget mb,
387     XEvent *event,
388     Region region )
389 {
390         if (XtIsRealized (mb)) {
391                 /* Label expose method does the initial work */
392                 XtExposeProc expose;
393                 _DtProcessLock();
394                 expose = xmLabelClassRec.core_class.expose;
395                 _DtProcessUnlock();
396                 (* expose)(mb, event, region) ;
397         
398                 DrawMenuGlyph((DtMenuButtonWidget) mb);
399                 Draw3DShadows ((DtMenuButtonWidget) mb);
400         }
401 }
402
403 static void 
404 Arm(
405     DtMenuButtonWidget mb )
406 {
407         XmProcessTraversal( (Widget) mb, XmTRAVERSE_CURRENT);
408         if (MB_ARMED(mb) == FALSE) {
409                 MB_ARMED(mb) =  TRUE;
410                 DrawMenuGlyph(mb);
411                 Draw3DShadows (mb);
412         }
413 }
414
415 static void 
416 ArmAndActivate(
417     Widget wid,
418     XEvent *event,
419     String *params,
420     Cardinal *num_params )
421 {
422         DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
423         
424         if(MB_SMENU(mb) == (Widget)NULL ||
425                 event == (XEvent*) NULL)
426                 return;
427
428         if (!_XmIsEventUnique(event))
429            return;
430         
431         Popup(mb, event, TRUE);
432         _XmSetInDragMode((Widget)mb,FALSE);
433         if (!XmProcessTraversal(MB_SMENU(mb), XmTRAVERSE_CURRENT))
434                    XtSetKeyboardFocus(XtParent(MB_SMENU(mb)), MB_SMENU(mb));
435
436         _XmRecordEvent(event);
437 }
438
439
440 static void 
441 Disarm(
442     DtMenuButtonWidget mb,
443     Boolean pop_down )
444 {
445         if (MB_ARMED(mb) == TRUE) {
446                 MB_ARMED(mb) =  FALSE;
447         
448                 if (pop_down == TRUE    && 
449                         MB_SMENU(mb) != (Widget)NULL)
450                                 XtUnmanageChild(MB_SMENU(mb));
451         
452                 Draw3DShadows(mb);
453                 DrawMenuGlyph(mb);
454         }
455 }
456
457 /*ARGSUSED*/
458 static void 
459 Select(
460     Widget wid,
461     XEvent *event,
462     String *param,
463     Cardinal *num_param )
464 {
465         DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
466         XtActionProc arm_and_activate;
467         
468         if(MB_POPPED_UP(mb) == TRUE) {
469                 Disarm(mb,TRUE);
470                 return;
471         }
472         
473         _DtProcessLock();
474         arm_and_activate = ((DtMenuButtonClassRec *)(mb->core.widget_class))->
475                 primitive_class.arm_and_activate;
476         _DtProcessUnlock();
477         (* arm_and_activate) ((Widget) mb, event, NULL, NULL);
478 }
479
480 static void
481 PreMenuButtonHandler(
482     Widget wid,
483     XtPointer cd,
484     XEvent *event,
485 Boolean *cont_to_dispatch)
486 {
487        DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
488         
489         if(MB_SMENU(mb) == (Widget)NULL                         ||
490                 event->xany.type != ButtonPress                 ||
491                 event->xbutton.time <= MB_LAST_TIMESTAMP(mb))
492                         return;
493
494         if(event->xbutton.button==Button1||event->xbutton.button==Button3)
495         {
496                 String btnstr=NULL;
497                 XtVaGetValues(MB_SMENU(mb), XmNmenuPost, &btnstr, NULL);
498                 if (btnstr==NULL || !strcmp(btnstr,"") || 
499                     !strcmp(btnstr, "<Btn3Down>"))
500                         event->xbutton.button=Button3;
501                 else if (!strcmp(btnstr, "<Btn1Down>"))
502                         event->xbutton.button=Button1;
503                 else
504                         XtWarning(MB_POST);
505         }
506 }
507
508 static void 
509 MenuButtonHandler(
510     Widget wid,
511     XtPointer cd,
512     XEvent *event,
513 Boolean *cont_to_dispatch)
514 {
515         DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
516         
517
518         if(MB_SMENU(mb) == (Widget)NULL                         ||
519                 event->xany.type != ButtonPress                 || 
520                 event->xbutton.time <= MB_LAST_TIMESTAMP(mb))
521                         return;
522         else
523                 MB_LAST_TIMESTAMP(mb) = event->xbutton.time;
524         
525         Popup (mb, event, TRUE);
526
527 }
528
529
530 static void 
531 CallCascadingCallbacks(
532     Widget w,
533     XEvent *event )
534 {
535         DtMenuButtonWidget mb = (DtMenuButtonWidget)w;
536         XmAnyCallbackStruct cback;
537         
538         if(MB_POPPED_UP(mb) == TRUE)
539                 return;
540         
541         cback.reason = DtCR_CASCADING;
542         cback.event = event;
543         
544         XtCallCallbackList ((Widget) mb, 
545                 CascadingCallback(mb), &cback);
546 }
547
548 static void
549 PopdownCallback(Widget w, XtPointer client_data, XtPointer call_data)
550 {
551         DtMenuButtonWidget mb = (DtMenuButtonWidget)client_data;
552
553         Disarm(mb,FALSE);
554         MB_POPPED_UP(mb) = FALSE;
555         MB_LAST_TIMESTAMP(mb) =  XtLastTimestampProcessed(XtDisplay(mb));
556 }
557
558 static void
559 PopupCallback(Widget w, XtPointer client_data, XtPointer call_data)
560 {
561         DtMenuButtonWidget mb = (DtMenuButtonWidget)client_data;
562
563         Arm(mb);
564         MB_POPPED_UP(mb) = TRUE;
565 }
566
567 /*
568  * Call the cascading callbacks and popup any submenu. 
569  */
570
571 static void 
572 Popup(
573     DtMenuButtonWidget mb,
574     XEvent *event,
575     Boolean call_cbacks )
576 {
577         Position x=0, y=0; 
578         Position root_x=0, root_y=0; 
579         Position save_x = 0, save_y = 0;
580         Dimension sw=0, sh=0;
581         int dx = 0, dy = 0;
582         Screen *screen;
583
584         if((MB_SMENU(mb) == (Widget)NULL)       || 
585                 (MB_POPPED_UP(mb) == TRUE))
586                 return;
587         
588         if (call_cbacks)
589                 CallCascadingCallbacks((Widget)mb, event);
590         
591         x =  (Position)((int)XtWidth((Widget)mb) - (int)XtWidth(MB_SMENU(mb)))/2;
592         y  = XtHeight((Widget)mb) - mb->primitive.highlight_thickness + 1;
593         XtTranslateCoords((Widget)mb, x,y, &root_x, &root_y);
594         
595         /* Check if not completely on the screen */
596         screen = XtScreen(mb);
597         sw = WidthOfScreen(screen);
598         sh = HeightOfScreen(screen);
599
600         if((dx = root_x - sw + XtWidth(MB_SMENU(mb))) > 0)
601                 root_x -= dx; 
602         if((dy = root_y - sh + XtHeight(MB_SMENU(mb))) > 0) {
603                 root_y -= (2 - (mb->primitive.highlight_thickness<<1));
604                 root_y -= (XtHeight(mb) + XtHeight(MB_SMENU(mb)));
605         }
606         
607         save_x = event->xbutton.x_root;
608         save_y = event->xbutton.y_root;
609         
610         event->xbutton.x_root = root_x;
611         event->xbutton.y_root = root_y;
612
613         /* Position the menu */
614         XmMenuPosition(MB_SMENU(mb),(XButtonPressedEvent*)event);
615
616         event->xbutton.x_root = save_x;
617         event->xbutton.y_root = save_y;
618         
619         XtManageChild(MB_SMENU(mb));
620 }
621
622 /*
623  * Get the menu glyph size set up.
624  */
625 static void 
626 CalculateMenuGlyphSize(
627     DtMenuButtonWidget menubtn )
628 {
629         Window rootwin;
630         int x,y; /* must be int */
631         unsigned int width, height, border, depth; /* must be int */
632         
633         if (MB_PIXMAP(menubtn) != XmUNSPECIFIED_PIXMAP) {
634            XGetGeometry(XtDisplay(menubtn), MB_PIXMAP(menubtn),
635                         &rootwin, &x, &y, &width, &height,
636                         &border, &depth);
637         
638            MB_GLYPH_WIDTH(menubtn) = (Dimension) width;
639            MB_GLYPH_HEIGHT(menubtn) = (Dimension) height;
640         } else {
641                 int ht, st;
642                 Dimension side;
643                 unsigned int text_height;
644         
645                 ht = menubtn->primitive.highlight_thickness;
646                 st = menubtn->primitive.shadow_thickness;
647                 text_height = Lab_TextRect_height(menubtn);
648         
649                 side = Max( (text_height * 2 / 3) + 2 * (ht + st),
650                    (2*(ht + (st-1) +1)) +1 );
651                 MB_GLYPH_WIDTH(menubtn) = 
652                 MB_GLYPH_HEIGHT(menubtn) = side;
653         }
654 }
655
656 /*
657  * Set up the menu glyph location.  
658  */
659 static void 
660 LocateMenuGlyph(
661     DtMenuButtonWidget menubtn )
662 {
663         Dimension buffer;
664         
665         MB_GLYPH_X(menubtn) = 
666                 XtWidth (menubtn) -
667                 menubtn->primitive.highlight_thickness -
668                 menubtn->primitive.shadow_thickness -
669                 Lab_MarginWidth(menubtn) -
670                 MB_GLYPH_WIDTH(menubtn);
671         
672         buffer = menubtn->primitive.highlight_thickness +
673                          menubtn->primitive.shadow_thickness +
674                          Lab_MarginHeight(menubtn);
675         
676         MB_GLYPH_Y(menubtn) = (Position)((int)buffer +
677                 (((int)XtHeight(menubtn) -  2*(int)buffer) - 
678                         (int)MB_GLYPH_HEIGHT(menubtn)) / 2);
679 }
680
681 /*
682  * Make room for  menu glyph  in menu button.
683  */
684 static void 
685 AdjustMenuButtonSize(
686     DtMenuButtonWidget menubtn,
687     Boolean adjustWidth,
688     Boolean adjustHeight )
689 {
690         Dimension delta;
691         
692         /*
693          *  Modify the size of the menubutton to acommadate the menu.
694          *  The menu should fit inside MarginRight.
695          */
696         if ((int)(MB_GLYPH_WIDTH(menubtn) + GLYPH_PIX_SPACE) >
697                                         (int)Lab_MarginRight(menubtn)) {
698                 delta = MB_GLYPH_WIDTH(menubtn) + GLYPH_PIX_SPACE -
699                 Lab_MarginRight(menubtn);
700                 Lab_MarginRight(menubtn) += delta;
701         
702                 if (adjustWidth)
703                         XtWidth(menubtn) += delta;
704                 else {
705                         if (LAlignment(menubtn) == XmALIGNMENT_END)
706                                 Lab_TextRect_x(menubtn) -= delta;
707                         else 
708                                 if (LAlignment(menubtn) == XmALIGNMENT_CENTER)
709                                         Lab_TextRect_x(menubtn) -= delta/2;
710                 }
711         }
712         
713         /*
714          * the menu height should fit inside of 
715          * TextRect + marginTop + marginBottom
716          */
717         delta = MB_GLYPH_HEIGHT(menubtn) +
718                 2 * (Lab_MarginHeight(menubtn) +
719                 menubtn->primitive.shadow_thickness +
720                 menubtn->primitive.highlight_thickness);
721         
722         if (delta > XtHeight(menubtn)) {
723                 delta -= XtHeight(menubtn);
724                 Lab_MarginTop(menubtn) += delta/2;
725                 Lab_TextRect_y(menubtn) += delta/2;
726                 Lab_MarginBottom(menubtn) += delta - (delta/2);
727          
728                 if (adjustHeight)
729                         XtHeight(menubtn) += delta;
730         }
731         
732         LocateMenuGlyph(menubtn);
733 }
734
735 /*
736  * Destroy the widget
737  */
738 static void 
739 Destroy(
740     Widget wid )
741 {
742         DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
743         XmRowColumnWidget submenu = (XmRowColumnWidget) MB_SMENU(mb);
744         Widget shell;
745         Display *dpy = XtDisplay(wid);
746         
747         XtRemoveAllCallbacks ((Widget) mb, DtNcascadingCallback);
748         
749         if(submenu != (XmRowColumnWidget)NULL) {
750                 shell = XtParent((Widget)submenu);
751                 XtRemoveCallback(shell, XtNpopupCallback,PopupCallback, (XtPointer)mb);
752                 XtRemoveCallback(shell, XtNpopdownCallback, PopdownCallback,(XtPointer)mb);
753         }
754         
755         if(MB_PVT_SMENU(mb) == TRUE) {
756                 XtDestroyWidget((Widget)submenu);
757                 MB_SMENU(mb) = NULL;
758                 MB_PVT_SMENU(mb) = FALSE;
759         }
760 }
761                      
762 /*
763  * Resize Method.
764  */
765 static void 
766 Resize(
767     Widget mb )
768 {
769         if (mb != (Widget)NULL) {
770                 /* Label resize method lays out the label string */
771                 XtWidgetProc resize;
772                 _DtProcessLock();
773                 resize = xmLabelClassRec.core_class.resize;
774                 _DtProcessUnlock();
775                 (* resize) (mb);
776
777                 LocateMenuGlyph ((DtMenuButtonWidget) mb);
778         }
779 }
780
781 /*
782  * Set Values Method.
783  */
784 static Boolean 
785 SetValues(
786     Widget cw,
787     Widget rw,
788     Widget nw,
789     ArgList args,
790     Cardinal *num_args )
791 {
792         DtMenuButtonWidget old = (DtMenuButtonWidget) cw ;
793         DtMenuButtonWidget requested = (DtMenuButtonWidget) rw ;
794         DtMenuButtonWidget new_w = (DtMenuButtonWidget) nw ;
795         Boolean flag = FALSE;
796         Boolean adjustWidth = FALSE;
797         Boolean adjustHeight = FALSE;
798         unsigned char rowcol_type = NULL;
799         Display *dpy = XtDisplay(nw);
800         Boolean menu_glyph_changed = FALSE;
801         
802         if (MB_SMENU(new_w) != (Widget)NULL) {
803                 XtVaGetValues(MB_SMENU(new_w), XmNrowColumnType, 
804                                         &rowcol_type, NULL);
805                 if(rowcol_type != XmMENU_POPUP) {
806                         MB_SMENU(new_w) = NULL;
807                         XtError(MB_SUBMENU);
808                 }
809         }
810         
811         /* Never let traversal become FALSE */
812         new_w->primitive.traversal_on = TRUE;
813         
814         if ((LRecomputeSize(new_w))     || 
815                 (requested->core.width <= 0))
816                         adjustWidth = TRUE;
817         
818         if ((LRecomputeSize(new_w))     || 
819                 (requested->core.height <= 0))
820                         adjustHeight = TRUE;
821         
822         /* get new pixmap size */
823         if ((MB_PIXMAP(old) != MB_PIXMAP (new_w)) ||
824                 (Lab_TextRect_height(old) != Lab_TextRect_height(new_w))) {
825                         CalculateMenuGlyphSize (new_w);
826                         menu_glyph_changed = TRUE;
827         }
828
829         if ((old->primitive.foreground != 
830                 new_w->primitive.foreground)    ||
831                 (old->core.background_pixel != 
832                         new_w->core.background_pixel)) {
833                                 menu_glyph_changed = TRUE;
834                                 GetGC(new_w);
835         }
836
837         /*
838          * Resize widget if submenu appeared or disappeared, or if the
839          * menu glyph changed.
840          */
841         
842         if ( menu_glyph_changed == TRUE ||
843                 (LLabelType(old) != LLabelType(new_w)) ||
844                 (MB_SMENU(old) != MB_SMENU(new_w))) {
845         
846                 AdjustMenuButtonSize (new_w, adjustWidth, adjustHeight);
847         
848                 flag = TRUE;
849         
850                 if ((MB_SMENU(old) != MB_SMENU(new_w))) {
851         
852                         if(MB_SMENU(new_w)) {
853                                 XtRemoveEventHandler((Widget)new_w,
854                                         ButtonPressMask, False, 
855                                         PreMenuButtonHandler, MB_SMENU(old));
856                                 XtInsertEventHandler((Widget)new_w,
857                                         ButtonPressMask, False, 
858                                         PreMenuButtonHandler, MB_SMENU(new_w), 
859                                         XtListHead);
860                                 XtAddCallback(XtParent(
861                                         MB_SMENU(new_w)), 
862                                         XtNpopdownCallback, 
863                                         PopdownCallback, (XtPointer)new_w);
864                                 XtAddCallback(XtParent(
865                                         MB_SMENU(new_w)), 
866                                         XtNpopupCallback, 
867                                         PopupCallback, (XtPointer)new_w);
868                         }
869         
870                         if(MB_PVT_SMENU(old) == TRUE) {
871                                 XtDestroyWidget(MB_SMENU(old));
872                                 MB_PVT_SMENU(new_w) = FALSE;
873                         }
874         
875                 }
876         
877         } else  if ((new_w->primitive.highlight_thickness !=
878                                 old->primitive.highlight_thickness)               ||
879                                 (new_w->primitive.shadow_thickness !=
880                                 old->primitive.shadow_thickness)                  ||
881                                 (Lab_MarginRight (new_w) != Lab_MarginRight (old))   ||
882                                 (Lab_MarginHeight (new_w) != Lab_MarginHeight (old)) ||
883                                 (Lab_MarginTop (new_w) != Lab_MarginTop (old))   ||
884                                 (Lab_MarginBottom (new_w) != Lab_MarginBottom (old))) {
885                                         CalculateMenuGlyphSize (new_w);
886                                         AdjustMenuButtonSize (new_w,adjustWidth, adjustHeight);
887                                         flag = TRUE;
888                         } else if ((Lab_MarginWidth(new_w) != Lab_MarginWidth(old)) ||
889                                         (new_w->core.width != old->core.width)           ||
890                                         (new_w->core.height != old->core.height)) {
891                                                 LocateMenuGlyph (new_w);
892                                                 flag = TRUE;
893                         }
894         
895         return (flag);
896 }
897
898 /*
899  * Initialize
900  */
901 static void 
902 Initialize(
903     Widget w_req,
904     Widget w_new,
905     ArgList args,
906     Cardinal *num_args )
907 {
908         DtMenuButtonWidget  req = (DtMenuButtonWidget) w_req ;
909         DtMenuButtonWidget  new_w = (DtMenuButtonWidget) w_new ;
910         Widget topManager;
911         Boolean adjustWidth = FALSE;
912         Boolean adjustHeight = FALSE;
913         Widget    parent = XtParent(new_w);
914         unsigned char rowcol_type = NULL;
915         char *name = NULL;
916         
917         if ((XmIsRowColumn (parent))) {
918                 XtVaGetValues(parent, XmNrowColumnType, 
919                         &rowcol_type, NULL);
920                 if(rowcol_type != XmWORK_AREA)
921                         XtError(MB_PARENT);
922         }
923
924         name = XtMalloc(10 + strlen(new_w->core.name));
925         sprintf(name,"submenu_%s",new_w->core.name);
926         
927         GetTopManager(w_new,&topManager);
928         MB_SMENU(new_w) = XmCreatePopupMenu(topManager, name, NULL, 0);
929         /* Remove our passive grab */
930         XtUngrabButton(topManager, RC_PostButton(MB_SMENU(new_w)), AnyModifier);
931
932         MB_PVT_SMENU(new_w) = TRUE;
933         
934         MB_ARMED(new_w) =  FALSE;
935         MB_POPPED_UP(new_w) = FALSE;
936         MB_LAST_TIMESTAMP(new_w) = 0;
937         MB_GC(new_w) = (GC)NULL;
938         
939         if (req->core.width <= 0)
940                 adjustWidth = TRUE;
941         
942         if (req->core.height <= 0)
943                 adjustHeight = TRUE;
944         
945         CalculateMenuGlyphSize (new_w);
946         AdjustMenuButtonSize (new_w, adjustWidth, adjustHeight);
947         GetGC(new_w);
948         
949         new_w->primitive.traversal_on = TRUE;
950         
951         if(MB_SMENU(new_w) != (Widget)NULL) {
952                 XtInsertEventHandler((Widget)new_w, ButtonPressMask,
953                      False, PreMenuButtonHandler, MB_SMENU(new_w), XtListHead);
954                 XtAddEventHandler((Widget)new_w, ButtonPressMask,
955                         FALSE, MenuButtonHandler, MB_SMENU(new_w));
956                 XtAddCallback(XtParent(MB_SMENU(new_w)), XtNpopdownCallback, 
957                                 PopdownCallback, (XtPointer)new_w);
958                 XtAddCallback(XtParent(MB_SMENU(new_w)), XtNpopupCallback, 
959                                 PopupCallback, (XtPointer)new_w);
960         }
961
962         if (name) XtFree(name);
963 }
964
965
966 static void 
967 DrawArrow(
968     Widget wid )
969 {
970         GC gc, tsGC, bsGC;
971         Pixel tsc, bsc;
972         int ht,st;
973         unsigned int text_height;
974
975         DtMenuButtonWidget mb = (DtMenuButtonWidget) wid ;
976         Window win = XtWindow(wid);
977         Display *dpy = XtDisplay(wid);
978         
979         ht = mb->primitive.highlight_thickness;
980         st = mb->primitive.shadow_thickness;
981         text_height = Lab_TextRect_height(mb);
982
983         tsc =  mb->primitive.top_shadow_color;
984         bsc =  mb->primitive.bottom_shadow_color;
985
986         tsGC = mb->primitive.top_shadow_GC;
987         bsGC = mb->primitive.bottom_shadow_GC;
988         gc = MB_GC(mb);
989
990         /* armed arrow */
991         if(MB_ARMED(mb) == TRUE) {
992                 XFillRectangle(dpy, win, gc, 
993                         MB_GLYPH_X(mb), MB_GLYPH_Y(mb), 
994                         MB_GLYPH_WIDTH(mb), MB_GLYPH_HEIGHT(mb));
995                 XmeDrawArrow(dpy, win,
996                         bsGC, tsGC, gc,
997                         MB_GLYPH_X(mb) + ht + st - 1,
998                         MB_GLYPH_Y(mb) + ht + st - 1,
999                         MB_GLYPH_WIDTH(mb) - 2*(ht + st - 1),
1000                         MB_GLYPH_HEIGHT(mb) - 2*(ht + st - 1),
1001                         st, XmARROW_DOWN);
1002         } else {
1003                 /* standard (unarmed) arrow */
1004         
1005                 XFillRectangle(dpy, win, gc, 
1006                         MB_GLYPH_X(mb), MB_GLYPH_Y(mb),
1007                         MB_GLYPH_WIDTH(mb), MB_GLYPH_HEIGHT(mb));
1008                 XmeDrawArrow(dpy, win,
1009                         tsGC, bsGC, gc, 
1010                         MB_GLYPH_X(mb) + ht + st - 1, 
1011                         MB_GLYPH_Y(mb) + ht + st - 1, 
1012                         MB_GLYPH_WIDTH(mb) - 2*(ht + st - 1),
1013                         MB_GLYPH_HEIGHT(mb) - 2*(ht + st - 1), 
1014                         st, XmARROW_DOWN);
1015         }
1016
1017 }
1018
1019 static void
1020 GetGC(
1021         DtMenuButtonWidget mb)    
1022 {
1023         XGCValues values;
1024         Pixel bg;
1025
1026         if(MB_GC(mb) != (GC)NULL) {
1027                 XtReleaseGC((Widget)mb, MB_GC(mb));
1028                 MB_GC(mb) = (GC)NULL;
1029         }
1030
1031         bg = mb->core.background_pixel;
1032         values.foreground = values.background = bg;
1033         values.graphics_exposures = FALSE;
1034         MB_GC(mb)  = XtGetGC ((Widget) mb,
1035                 GCForeground | GCBackground | GCGraphicsExposures, &values);
1036 }
1037
1038 /*
1039  *************************************************************************
1040  *
1041  * Public Routines                                                        
1042  *
1043  *************************************************************************
1044  */
1045
1046 Widget 
1047 DtCreateMenuButton(
1048     Widget parent,
1049     char *name,
1050     ArgList al,
1051     Cardinal ac )
1052 {
1053         return XtCreateWidget(name,dtMenuButtonWidgetClass,parent, al, ac);
1054 }
1055
1056 static void
1057 GetTopManager(
1058         Widget w,
1059         Widget *topManager )
1060 {
1061    while (XmIsManager(XtParent(w)))
1062        w = XtParent(w);
1063  
1064    * topManager = w;
1065 }
1066  
1067