1 /*
2  * tkUnixMenu.c --
3  *
4  *	This module implements the UNIX platform-specific features of menus.
5  *
6  * Copyright (c) 1996-1998 by Sun Microsystems, Inc.
7  *
8  * See the file "license.terms" for information on usage and redistribution
9  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  *
11  * RCS: @(#) $Id: tkUnixMenu.c,v 1.7 2002/02/22 13:13:13 dkf Exp $
12  */
13 
14 #include "tkPort.h"
15 #include "default.h"
16 #include "tkInt.h"
17 #include "tkUnixInt.h"
18 #include "tkMenu.h"
19 
20 /*
21  * Constants used for menu drawing.
22  */
23 
24 #define MENU_MARGIN_WIDTH	2
25 #define MENU_DIVIDER_HEIGHT	2
26 
27 /*
28  * Platform specific flags for Unix.
29  */
30 
31 #define ENTRY_HELP_MENU		ENTRY_PLATFORM_FLAG1
32 
33 /*
34  * Procedures used internally.
35  */
36 
37 static void		SetHelpMenu _ANSI_ARGS_((TkMenu *menuPtr));
38 static void		DrawMenuEntryAccelerator _ANSI_ARGS_((
39 			    TkMenu *menuPtr, TkMenuEntry *mePtr,
40 			    Drawable d, GC gc, Tk_Font tkfont,
41 			    CONST Tk_FontMetrics *fmPtr,
42 			    Tk_3DBorder activeBorder, int x, int y,
43 			    int width, int height, int drawArrow));
44 static void		DrawMenuEntryBackground _ANSI_ARGS_((
45 			    TkMenu *menuPtr, TkMenuEntry *mePtr,
46 			    Drawable d, Tk_3DBorder activeBorder,
47 			    Tk_3DBorder bgBorder, int x, int y,
48 			    int width, int heigth));
49 static void		DrawMenuEntryIndicator _ANSI_ARGS_((
50 			    TkMenu *menuPtr, TkMenuEntry *mePtr,
51 			    Drawable d, GC gc, GC indicatorGC,
52 			    Tk_Font tkfont,
53 			    CONST Tk_FontMetrics *fmPtr, int x, int y,
54 			    int width, int height));
55 static void		DrawMenuEntryLabel _ANSI_ARGS_((
56 			    TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d,
57 			    GC gc, Tk_Font tkfont,
58 			    CONST Tk_FontMetrics *fmPtr, int x, int y,
59 			    int width, int height));
60 static void		DrawMenuSeparator _ANSI_ARGS_((TkMenu *menuPtr,
61 			    TkMenuEntry *mePtr, Drawable d, GC gc,
62 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
63 			    int x, int y, int width, int height));
64 static void		DrawTearoffEntry _ANSI_ARGS_((TkMenu *menuPtr,
65 			    TkMenuEntry *mePtr, Drawable d, GC gc,
66 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
67 			    int x, int y, int width, int height));
68 static void		DrawMenuUnderline _ANSI_ARGS_((TkMenu *menuPtr,
69 			    TkMenuEntry *mePtr, Drawable d, GC gc,
70 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, int x,
71 			    int y, int width, int height));
72 static void		GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
73 			    TkMenuEntry *mePtr, Tk_Font tkfont,
74 			    CONST Tk_FontMetrics *fmPtr, int *widthPtr,
75 			    int *heightPtr));
76 static void		GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
77 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
78 			    int *widthPtr, int *heightPtr));
79 static void		GetMenuIndicatorGeometry _ANSI_ARGS_((
80 			    TkMenu *menuPtr, TkMenuEntry *mePtr,
81 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
82 			    int *widthPtr, int *heightPtr));
83 static void		GetMenuSeparatorGeometry _ANSI_ARGS_((
84 			    TkMenu *menuPtr, TkMenuEntry *mePtr,
85 			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
86 			    int *widthPtr, int *heightPtr));
87 static void		GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
88 			    TkMenuEntry *mePtr, Tk_Font tkfont,
89 			    CONST Tk_FontMetrics *fmPtr, int *widthPtr,
90 			    int *heightPtr));
91 
92 
93 /*
94  *----------------------------------------------------------------------
95  *
96  * TkpNewMenu --
97  *
98  *	Gets the platform-specific piece of the menu. Invoked during idle
99  *	after the generic part of the menu has been created.
100  *
101  * Results:
102  *	Standard TCL error.
103  *
104  * Side effects:
105  *	Allocates any platform specific allocations and places them
106  *	in the platformData field of the menuPtr.
107  *
108  *----------------------------------------------------------------------
109  */
110 
111 int
TkpNewMenu(menuPtr)112 TkpNewMenu(menuPtr)
113     TkMenu *menuPtr;
114 {
115     SetHelpMenu(menuPtr);
116     return TCL_OK;
117 }
118 
119 /*
120  *----------------------------------------------------------------------
121  *
122  * TkpDestroyMenu --
123  *
124  *	Destroys platform-specific menu structures. Called when the
125  *	generic menu structure is destroyed for the menu.
126  *
127  * Results:
128  *	None.
129  *
130  * Side effects:
131  *	All platform-specific allocations are freed up.
132  *
133  *----------------------------------------------------------------------
134  */
135 
136 void
TkpDestroyMenu(menuPtr)137 TkpDestroyMenu(menuPtr)
138     TkMenu *menuPtr;
139 {
140     /*
141      * Nothing to do.
142      */
143 }
144 
145 /*
146  *----------------------------------------------------------------------
147  *
148  * TkpDestroyMenuEntry --
149  *
150  *	Cleans up platform-specific menu entry items. Called when entry
151  *	is destroyed in the generic code.
152  *
153  * Results:
154  *	None.
155  *
156  * Side effects:
157  *	All platform specific allocations are freed up.
158  *
159  *----------------------------------------------------------------------
160  */
161 
162 void
TkpDestroyMenuEntry(mEntryPtr)163 TkpDestroyMenuEntry(mEntryPtr)
164     TkMenuEntry *mEntryPtr;
165 {
166     /*
167      * Nothing to do.
168      */
169 }
170 
171 /*
172  *----------------------------------------------------------------------
173  *
174  * TkpConfigureMenuEntry --
175  *
176  *	Processes configuration options for menu entries. Called when
177  *	the generic options are processed for the menu.
178  *
179  * Results:
180  *	Returns standard TCL result. If TCL_ERROR is returned, then
181  *	the interp's result contains an error message.
182  *
183  * Side effects:
184  *	Configuration information get set for mePtr; old resources
185  *	get freed, if any need it.
186  *
187  *----------------------------------------------------------------------
188  */
189 
190 int
TkpConfigureMenuEntry(mePtr)191 TkpConfigureMenuEntry(mePtr)
192     register TkMenuEntry *mePtr;	/* Information about menu entry;  may
193 					 * or may not already have values for
194 					 * some fields. */
195 {
196     /*
197      * If this is a cascade menu, and the child menu exists, check to
198      * see if the child menu is a help menu.
199      */
200 
201     if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) {
202 	TkMenuReferences *menuRefPtr;
203 
204 	menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp,
205 		mePtr->namePtr);
206 	if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
207 	    SetHelpMenu(menuRefPtr->menuPtr);
208 	}
209     }
210     return TCL_OK;
211 }
212 
213 /*
214  *----------------------------------------------------------------------
215  *
216  * TkpMenuNewEntry --
217  *
218  *	Called when a new entry is created in a menu. Fills in platform
219  *	specific data for the entry. The platformEntryData field
220  *	is used to store the indicator diameter for radio button
221  *	and check box entries.
222  *
223  * Results:
224  * 	Standard TCL error.
225  *
226  * Side effects:
227  *	None on Unix.
228  *
229  *----------------------------------------------------------------------
230  */
231 
232 int
TkpMenuNewEntry(mePtr)233 TkpMenuNewEntry(mePtr)
234     TkMenuEntry *mePtr;
235 {
236     return TCL_OK;
237 }
238 
239 /*
240  *----------------------------------------------------------------------
241  *
242  * TkpSetWindowMenuBar --
243  *
244  *	Sets up the menu as a menubar in the given window.
245  *
246  * Results:
247  *	None.
248  *
249  * Side effects:
250  *	Recomputes geometry of given window.
251  *
252  *----------------------------------------------------------------------
253  */
254 
255 void
TkpSetWindowMenuBar(tkwin,menuPtr)256 TkpSetWindowMenuBar(tkwin, menuPtr)
257     Tk_Window tkwin;			/* The window we are setting */
258     TkMenu *menuPtr;			/* The menu we are setting */
259 {
260     if (menuPtr == NULL) {
261 	TkUnixSetMenubar(tkwin, NULL);
262     } else {
263 	TkUnixSetMenubar(tkwin, menuPtr->tkwin);
264     }
265 }
266 
267 /*
268  *----------------------------------------------------------------------
269  *
270  * TkpSetMainMenuBar --
271  *
272  *	Called when a toplevel widget is brought to front. On the
273  *	Macintosh, sets up the menubar that goes accross the top
274  *	of the main monitor. On other platforms, nothing is necessary.
275  *
276  * Results:
277  *	None.
278  *
279  * Side effects:
280  *	Recompute geometry of given window.
281  *
282  *----------------------------------------------------------------------
283  */
284 
285 void
TkpSetMainMenubar(interp,tkwin,menuName)286 TkpSetMainMenubar(interp, tkwin, menuName)
287     Tcl_Interp *interp;
288     Tk_Window tkwin;
289     char *menuName;
290 {
291     /*
292      * Nothing to do.
293      */
294 }
295 
296 /*
297  *----------------------------------------------------------------------
298  *
299  * GetMenuIndicatorGeometry --
300  *
301  *	Fills out the geometry of the indicator in a menu item. Note
302  *	that the mePtr->height field must have already been filled in
303  *	by GetMenuLabelGeometry since this height depends on the label
304  *	height.
305  *
306  * Results:
307  *	widthPtr and heightPtr point to the new geometry values.
308  *
309  * Side effects:
310  *	None.
311  *
312  *----------------------------------------------------------------------
313  */
314 
315 static void
GetMenuIndicatorGeometry(menuPtr,mePtr,tkfont,fmPtr,widthPtr,heightPtr)316 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
317     TkMenu *menuPtr;			/* The menu we are drawing. */
318     TkMenuEntry *mePtr;			/* The entry we are interested in. */
319     Tk_Font tkfont;			/* The precalculated font */
320     CONST Tk_FontMetrics *fmPtr;	/* The precalculated metrics */
321     int *widthPtr;			/* The resulting width */
322     int *heightPtr;			/* The resulting height */
323 {
324     if ((mePtr->type == CHECK_BUTTON_ENTRY)
325 	    || (mePtr->type == RADIO_BUTTON_ENTRY)) {
326 	if (!mePtr->hideMargin && mePtr->indicatorOn) {
327 	    if ((mePtr->image != NULL) || (mePtr->bitmapPtr != NULL)) {
328 		*widthPtr = (14 * mePtr->height) / 10;
329 		*heightPtr = mePtr->height;
330 		if (mePtr->type == CHECK_BUTTON_ENTRY) {
331 		    mePtr->platformEntryData =
332 			    (TkMenuPlatformEntryData) ((65 * mePtr->height)
333 			    / 100);
334 		} else {
335 		    mePtr->platformEntryData =
336 			    (TkMenuPlatformEntryData) ((75 * mePtr->height)
337 			    / 100);
338 		}
339 	    } else {
340 		*widthPtr = *heightPtr = mePtr->height;
341 		if (mePtr->type == CHECK_BUTTON_ENTRY) {
342 		    mePtr->platformEntryData = (TkMenuPlatformEntryData)
343 			((80 * mePtr->height) / 100);
344 		} else {
345 		    mePtr->platformEntryData = (TkMenuPlatformEntryData)
346 			mePtr->height;
347 		}
348 	    }
349 	} else {
350 	    int borderWidth;
351 
352 	    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
353 		    menuPtr->borderWidthPtr, &borderWidth);
354 	    *heightPtr = 0;
355 	    *widthPtr = borderWidth;
356 	}
357     } else {
358 	int borderWidth;
359 
360 	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
361 		&borderWidth);
362         *heightPtr = 0;
363         *widthPtr = borderWidth;
364     }
365 }
366 
367 
368 /*
369  *----------------------------------------------------------------------
370  *
371  * GetMenuAccelGeometry --
372  *
373  *	Get the geometry of the accelerator area of a menu item.
374  *
375  * Results:
376  *	heightPtr and widthPtr are set.
377  *
378  * Side effects:
379  *	None.
380  *
381  *----------------------------------------------------------------------
382  */
383 
384 static void
GetMenuAccelGeometry(menuPtr,mePtr,tkfont,fmPtr,widthPtr,heightPtr)385 GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
386     TkMenu *menuPtr;		/* The menu was are drawing */
387     TkMenuEntry *mePtr;		/* The entry we are getting the geometry for */
388     Tk_Font tkfont;		/* The precalculated font */
389     CONST Tk_FontMetrics *fmPtr;/* The precalculated font metrics */
390     int *widthPtr;		/* The width of the acclerator area */
391     int *heightPtr;		/* The height of the accelerator area */
392 {
393     *heightPtr = fmPtr->linespace;
394     if (mePtr->type == CASCADE_ENTRY) {
395     	*widthPtr = 2 * CASCADE_ARROW_WIDTH;
396     } else if ((menuPtr->menuType != MENUBAR)
397 	    && (mePtr->accelPtr != NULL)) {
398 	char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL);
399 
400     	*widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
401     } else {
402     	*widthPtr = 0;
403     }
404 }
405 
406 /*
407  *----------------------------------------------------------------------
408  *
409  * DrawMenuEntryBackground --
410  *
411  *	This procedure draws the background part of a menu.
412  *
413  * Results:
414  *	None.
415  *
416  * Side effects:
417  *	Commands are output to X to display the menu in its
418  *	current mode.
419  *
420  *----------------------------------------------------------------------
421  */
422 
423 static void
DrawMenuEntryBackground(menuPtr,mePtr,d,activeBorder,bgBorder,x,y,width,height)424 DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, bgBorder, x, y,
425 	width, height)
426     TkMenu *menuPtr;		/* The menu we are drawing */
427     TkMenuEntry *mePtr;		/* The entry we are drawing. */
428     Drawable d;			/* The drawable we are drawing into */
429     Tk_3DBorder activeBorder;	/* The border for an active item */
430     Tk_3DBorder bgBorder;	/* The background border */
431     int x;			/* Left coordinate of entry rect */
432     int y;			/* Right coordinate of entry rect */
433     int width;			/* Width of entry rect */
434     int height;			/* Height of entry rect */
435 {
436     if (mePtr->state == ENTRY_ACTIVE) {
437 	int relief;
438 	int activeBorderWidth;
439 
440     	bgBorder = activeBorder;
441 
442 	if ((menuPtr->menuType == MENUBAR)
443 		&& ((menuPtr->postedCascade == NULL)
444 		|| (menuPtr->postedCascade != mePtr))) {
445 	    relief = TK_RELIEF_FLAT;
446 	} else {
447 	    relief = TK_RELIEF_RAISED;
448 	}
449 
450 	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
451 		menuPtr->activeBorderWidthPtr, &activeBorderWidth);
452 	Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
453 		activeBorderWidth, relief);
454     } else {
455 	Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
456 		0, TK_RELIEF_FLAT);
457     }
458 }
459 
460 /*
461  *----------------------------------------------------------------------
462  *
463  * DrawMenuEntryAccelerator --
464  *
465  *	This procedure draws the background part of a menu.
466  *
467  * Results:
468  *	None.
469  *
470  * Side effects:
471  *	Commands are output to X to display the menu in its
472  *	current mode.
473  *
474  *----------------------------------------------------------------------
475  */
476 
477 static void
DrawMenuEntryAccelerator(menuPtr,mePtr,d,gc,tkfont,fmPtr,activeBorder,x,y,width,height,drawArrow)478 DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, activeBorder,
479 	x, y, width, height, drawArrow)
480     TkMenu *menuPtr;			/* The menu we are drawing */
481     TkMenuEntry *mePtr;			/* The entry we are drawing */
482     Drawable d;				/* The drawable we are drawing into */
483     GC gc;				/* The precalculated gc to draw with */
484     Tk_Font tkfont;			/* The precalculated font */
485     CONST Tk_FontMetrics *fmPtr;	/* The precalculated metrics */
486     Tk_3DBorder activeBorder;		/* The border for an active item */
487     int x;				/* Left coordinate of entry rect */
488     int y;				/* Top coordinate of entry rect */
489     int width;				/* Width of entry */
490     int height;				/* Height of entry */
491     int drawArrow;			/* Whether or not to draw arrow. */
492 {
493     XPoint points[3];
494     int borderWidth, activeBorderWidth;
495 
496     /*
497      * Draw accelerator or cascade arrow.
498      */
499 
500     if (menuPtr->menuType == MENUBAR) {
501 	return;
502     }
503 
504     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
505 	    &borderWidth);
506     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
507 	    &activeBorderWidth);
508     if ((mePtr->type == CASCADE_ENTRY) && drawArrow) {
509     	points[0].x = x + width - borderWidth - activeBorderWidth
510 	        - CASCADE_ARROW_WIDTH;
511     	points[0].y = y + (height - CASCADE_ARROW_HEIGHT)/2;
512     	points[1].x = points[0].x;
513     	points[1].y = points[0].y + CASCADE_ARROW_HEIGHT;
514     	points[2].x = points[0].x + CASCADE_ARROW_WIDTH;
515     	points[2].y = points[0].y + CASCADE_ARROW_HEIGHT/2;
516     	Tk_Fill3DPolygon(menuPtr->tkwin, d, activeBorder, points, 3,
517 		DECORATION_BORDER_WIDTH,
518 	    	(menuPtr->postedCascade == mePtr)
519 	    	? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
520     } else if (mePtr->accelPtr != NULL) {
521 	char *accel = Tcl_GetStringFromObj(mePtr->accelPtr, NULL);
522 	int left = x + mePtr->labelWidth + activeBorderWidth
523 	        + mePtr->indicatorSpace;
524 
525 	if (menuPtr->menuType == MENUBAR) {
526 	    left += 5;
527 	}
528     	Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
529 		mePtr->accelLength, left,
530 		(y + (height + fmPtr->ascent - fmPtr->descent) / 2));
531     }
532 }
533 
534 /*
535  *----------------------------------------------------------------------
536  *
537  * DrawMenuEntryIndicator --
538  *
539  *	This procedure draws the background part of a menu.
540  *
541  * Results:
542  *	None.
543  *
544  * Side effects:
545  *	Commands are output to X to display the menu in its
546  *	current mode.
547  *
548  *----------------------------------------------------------------------
549  */
550 
551 static void
DrawMenuEntryIndicator(menuPtr,mePtr,d,gc,indicatorGC,tkfont,fmPtr,x,y,width,height)552 DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr,
553 	x, y, width, height)
554     TkMenu *menuPtr;			/* The menu we are drawing */
555     TkMenuEntry *mePtr;			/* The entry we are drawing */
556     Drawable d;				/* The drawable to draw into */
557     GC gc;				/* The gc to draw with */
558     GC indicatorGC;			/* The gc that indicators draw with */
559     Tk_Font tkfont;			/* The font to draw with */
560     CONST Tk_FontMetrics *fmPtr;	/* The font metrics of the font */
561     int x;				/* The left of the entry rect */
562     int y;				/* The top of the entry rect */
563     int width;				/* Width of menu entry */
564     int height;				/* Height of menu entry */
565 {
566     /*
567      * Draw check-button indicator.
568      */
569 
570     if ((mePtr->type == CHECK_BUTTON_ENTRY) && mePtr->indicatorOn) {
571 	int dim, top, left;
572 	int activeBorderWidth;
573 	Tk_3DBorder border;
574 
575 	dim = (int) mePtr->platformEntryData;
576 	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
577 		menuPtr->activeBorderWidthPtr, &activeBorderWidth);
578 	left = x + activeBorderWidth + (mePtr->indicatorSpace - dim)/2;
579 	if (menuPtr->menuType == MENUBAR) {
580 	    left += 5;
581 	}
582 	top = y + (height - dim)/2;
583 	border = Tk_Get3DBorderFromObj(menuPtr->tkwin,
584 		menuPtr->borderPtr);
585 	Tk_Fill3DRectangle(menuPtr->tkwin, d, border, left, top, dim,
586 		dim, DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN);
587 	left += DECORATION_BORDER_WIDTH;
588 	top += DECORATION_BORDER_WIDTH;
589 	dim -= 2*DECORATION_BORDER_WIDTH;
590 	if ((dim > 0) && (mePtr->entryFlags
591 		& ENTRY_SELECTED)) {
592 	    XFillRectangle(menuPtr->display, d, indicatorGC, left, top,
593 		    (unsigned int) dim, (unsigned int) dim);
594 	}
595     }
596 
597     /*
598      * Draw radio-button indicator.
599      */
600 
601     if ((mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn) {
602 	XPoint points[4];
603 	int radius;
604 	Tk_3DBorder border;
605 
606 	border = Tk_Get3DBorderFromObj(menuPtr->tkwin,
607 		menuPtr->borderPtr);
608 	radius = ((int) mePtr->platformEntryData)/2;
609 	points[0].x = x + (mePtr->indicatorSpace
610 		- (int) mePtr->platformEntryData)/2;
611 	points[0].y = y + (height)/2;
612 	points[1].x = points[0].x + radius;
613 	points[1].y = points[0].y + radius;
614 	points[2].x = points[1].x + radius;
615 	points[2].y = points[0].y;
616 	points[3].x = points[1].x;
617 	points[3].y = points[0].y - radius;
618 	if (mePtr->entryFlags & ENTRY_SELECTED) {
619 	    XFillPolygon(menuPtr->display, d, indicatorGC, points, 4,
620 		    Convex, CoordModeOrigin);
621 	} else {
622 	    Tk_Fill3DPolygon(menuPtr->tkwin, d, border, points, 4,
623 		    DECORATION_BORDER_WIDTH, TK_RELIEF_FLAT);
624 	}
625 	Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 4,
626 		DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN);
627     }
628 }
629 
630 /*
631  *----------------------------------------------------------------------
632  *
633  * DrawMenuSeparator --
634  *
635  *	This procedure draws a separator menu item.
636  *
637  * Results:
638  *	None.
639  *
640  * Side effects:
641  *	Commands are output to X to display the menu in its
642  *	current mode.
643  *
644  *----------------------------------------------------------------------
645  */
646 
647 static void
DrawMenuSeparator(menuPtr,mePtr,d,gc,tkfont,fmPtr,x,y,width,height)648 DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
649     TkMenu *menuPtr;			/* The menu we are drawing */
650     TkMenuEntry *mePtr;			/* The entry we are drawing */
651     Drawable d;				/* The drawable we are using */
652     GC gc;				/* The gc to draw into */
653     Tk_Font tkfont;			/* The font to draw with */
654     CONST Tk_FontMetrics *fmPtr;	/* The font metrics from the font */
655     int x;
656     int y;
657     int width;
658     int height;
659 {
660     XPoint points[2];
661     Tk_3DBorder border;
662 
663     if (menuPtr->menuType == MENUBAR) {
664 	return;
665     }
666 
667     points[0].x = x;
668     points[0].y = y + height/2;
669     points[1].x = width - 1;
670     points[1].y = points[0].y;
671     border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
672     Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
673 	    TK_RELIEF_RAISED);
674 }
675 
676 /*
677  *----------------------------------------------------------------------
678  *
679  * DrawMenuEntryLabel --
680  *
681  *	This procedure draws the label part of a menu.
682  *
683  * Results:
684  *	None.
685  *
686  * Side effects:
687  *	Commands are output to X to display the menu in its
688  *	current mode.
689  *
690  *----------------------------------------------------------------------
691  */
692 
693 static void
DrawMenuEntryLabel(menuPtr,mePtr,d,gc,tkfont,fmPtr,x,y,width,height)694 DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
695     TkMenu *menuPtr;		/* The menu we are drawing. */
696     TkMenuEntry *mePtr;		/* The entry we are drawing. */
697     Drawable d;			/* What we are drawing into. */
698     GC gc;			/* The gc we are drawing into.*/
699     Tk_Font tkfont;		/* The precalculated font. */
700     CONST Tk_FontMetrics *fmPtr;/* The precalculated font metrics. */
701     int x;			/* Left edge. */
702     int y;			/* Top edge. */
703     int width;			/* width of entry. */
704     int height;			/* height of entry. */
705 {
706     int indicatorSpace =  mePtr->indicatorSpace;
707     int activeBorderWidth;
708     int leftEdge;
709     int imageHeight, imageWidth;
710     int textHeight = 0, textWidth = 0;	/* stop GCC warning */
711     int haveImage = 0, haveText = 0;
712     int imageXOffset = 0, imageYOffset = 0;
713     int textXOffset = 0, textYOffset = 0;
714 
715     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
716 	    &activeBorderWidth);
717     leftEdge = x + indicatorSpace + activeBorderWidth;
718     if (menuPtr->menuType == MENUBAR) {
719 	leftEdge += 5;
720     }
721 
722     /*
723      * Work out what we will need to draw first.
724      */
725 
726     if (mePtr->image != NULL) {
727     	Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
728 	haveImage = 1;
729     } else if (mePtr->bitmapPtr != NULL) {
730 	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
731 	Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight);
732 	haveImage = 1;
733     }
734     if (!haveImage || (mePtr->compound != COMPOUND_NONE)) {
735 	if (mePtr->labelLength > 0) {
736 	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
737 	    textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength);
738 	    textHeight = fmPtr->linespace;
739 	    haveText = 1;
740 	}
741     }
742 
743     /*
744      * Now work out what the relative positions are.
745      */
746 
747     if (haveImage && haveText) {
748 	int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth);
749 	switch ((enum compound) mePtr->compound) {
750 	    case COMPOUND_TOP: {
751 		textXOffset = (fullWidth - textWidth)/2;
752 		textYOffset = imageHeight/2 + 2;
753 		imageXOffset = (fullWidth - imageWidth)/2;
754 		imageYOffset = -textHeight/2;
755 		break;
756 	    }
757 	    case COMPOUND_BOTTOM: {
758 		textXOffset = (fullWidth - textWidth)/2;
759 		textYOffset = -imageHeight/2;
760 		imageXOffset = (fullWidth - imageWidth)/2;
761 		imageYOffset = textHeight/2 + 2;
762 		break;
763 	    }
764 	    case COMPOUND_LEFT: {
765 		textXOffset = imageWidth + 2;
766 		textYOffset = 0;
767 		imageXOffset = 0;
768 		imageYOffset = 0;
769 		break;
770 	    }
771 	    case COMPOUND_RIGHT: {
772 		textXOffset = 0;
773 		textYOffset = 0;
774 		imageXOffset = textWidth + 2;
775 		imageYOffset = 0;
776 		break;
777 	    }
778 	    case COMPOUND_CENTER: {
779 		textXOffset = (fullWidth - textWidth)/2;
780 		textYOffset = 0;
781 		imageXOffset = (fullWidth - imageWidth)/2;
782 		imageYOffset = 0;
783 		break;
784 	    }
785 	    case COMPOUND_NONE: {break;}
786 	}
787     } else {
788 	textXOffset = 0;
789 	textYOffset = 0;
790 	imageXOffset = 0;
791 	imageYOffset = 0;
792     }
793 
794     /*
795      * Draw label and/or bitmap or image for entry.
796      */
797 
798     if (mePtr->image != NULL) {
799     	if ((mePtr->selectImage != NULL)
800 	    	&& (mePtr->entryFlags & ENTRY_SELECTED)) {
801 	    Tk_RedrawImage(mePtr->selectImage, 0, 0,
802 		    imageWidth, imageHeight, d, leftEdge + imageXOffset,
803 		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
804     	} else {
805 	    Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
806 		    imageHeight, d, leftEdge + imageXOffset,
807 		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
808     	}
809     } else if (mePtr->bitmapPtr != None) {
810 	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
811 	XCopyPlane(menuPtr->display, bitmap, d,	gc, 0, 0,
812 		(unsigned) imageWidth, (unsigned) imageHeight,
813 		leftEdge + imageXOffset,
814 		(int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1);
815     }
816     if ((mePtr->compound != COMPOUND_NONE) || !haveImage) {
817 	int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
818     	if (mePtr->labelLength > 0) {
819 	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
820 	    Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
821 		    mePtr->labelLength, leftEdge + textXOffset,
822 		    baseline + textYOffset);
823 	    DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr,
824 		    x + textXOffset, y + textYOffset,
825 		    width, height);
826     	}
827     }
828 
829     if (mePtr->state == ENTRY_DISABLED) {
830 	if (menuPtr->disabledFgPtr == NULL) {
831 	    XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
832 		    (unsigned) width, (unsigned) height);
833 	} else if ((mePtr->image != NULL)
834 		&& (menuPtr->disabledImageGC != None)) {
835 	    XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
836 		    leftEdge + imageXOffset,
837 		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset),
838 		    (unsigned) imageWidth, (unsigned) imageHeight);
839 	}
840     }
841 }
842 
843 /*
844  *----------------------------------------------------------------------
845  *
846  * DrawMenuUnderline --
847  *
848  *	On appropriate platforms, draw the underline character for the
849  *	menu.
850  *
851  * Results:
852  *	None.
853  *
854  * Side effects:
855  *	Commands are output to X to display the menu in its
856  *	current mode.
857  *
858  *----------------------------------------------------------------------
859  */
860 
861 static void
DrawMenuUnderline(menuPtr,mePtr,d,gc,tkfont,fmPtr,x,y,width,height)862 DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
863     TkMenu *menuPtr;			/* The menu to draw into */
864     TkMenuEntry *mePtr;			/* The entry we are drawing */
865     Drawable d;				/* What we are drawing into */
866     GC gc;				/* The gc to draw into */
867     Tk_Font tkfont;			/* The precalculated font */
868     CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
869     int x;
870     int y;
871     int width;
872     int height;
873 {
874     int indicatorSpace = mePtr->indicatorSpace;
875 
876     if (mePtr->underline >= 0) {
877 	int activeBorderWidth;
878 	int leftEdge;
879 	char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
880 	CONST char *start = Tcl_UtfAtIndex(label, mePtr->underline);
881 	CONST char *end = Tcl_UtfNext(start);
882 
883 	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
884 		menuPtr->activeBorderWidthPtr, &activeBorderWidth);
885 	leftEdge = x + indicatorSpace + activeBorderWidth;
886 	if (menuPtr->menuType == MENUBAR) {
887 	    leftEdge += 5;
888 	}
889 
890 	Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label,
891     		leftEdge, y + (height + fmPtr->ascent - fmPtr->descent) / 2,
892 		start - label, end - label);
893     }
894 }
895 
896 /*
897  *----------------------------------------------------------------------
898  *
899  * TkpPostMenu --
900  *
901  *	Posts a menu on the screen
902  *
903  * Results:
904  *	None.
905  *
906  * Side effects:
907  *	The menu is posted and handled.
908  *
909  *----------------------------------------------------------------------
910  */
911 
912 int
TkpPostMenu(interp,menuPtr,x,y)913 TkpPostMenu(interp, menuPtr, x, y)
914     Tcl_Interp *interp;
915     TkMenu *menuPtr;
916     int x;
917     int y;
918 {
919     return TkPostTearoffMenu(interp, menuPtr, x, y);
920 }
921 
922 /*
923  *----------------------------------------------------------------------
924  *
925  * GetMenuSeparatorGeometry --
926  *
927  *	Gets the width and height of the indicator area of a menu.
928  *
929  * Results:
930  *	widthPtr and heightPtr are set.
931  *
932  * Side effects:
933  *	None.
934  *
935  *----------------------------------------------------------------------
936  */
937 
938 static void
GetMenuSeparatorGeometry(menuPtr,mePtr,tkfont,fmPtr,widthPtr,heightPtr)939 GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr,
940 	heightPtr)
941     TkMenu *menuPtr;			/* The menu we are measuring */
942     TkMenuEntry *mePtr;			/* The entry we are measuring */
943     Tk_Font tkfont;			/* The precalculated font */
944     CONST Tk_FontMetrics *fmPtr;	/* The precalcualted font metrics */
945     int *widthPtr;			/* The resulting width */
946     int *heightPtr;			/* The resulting height */
947 {
948     *widthPtr = 0;
949     *heightPtr = fmPtr->linespace;
950 }
951 
952 /*
953  *----------------------------------------------------------------------
954  *
955  * GetTearoffEntryGeometry --
956  *
957  *	Gets the width and height of the indicator area of a menu.
958  *
959  * Results:
960  *	widthPtr and heightPtr are set.
961  *
962  * Side effects:
963  *	None.
964  *
965  *----------------------------------------------------------------------
966  */
967 
968 static void
GetTearoffEntryGeometry(menuPtr,mePtr,tkfont,fmPtr,widthPtr,heightPtr)969 GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
970     TkMenu *menuPtr;			/* The menu we are drawing */
971     TkMenuEntry *mePtr;			/* The entry we are measuring */
972     Tk_Font tkfont;			/* The precalculated font */
973     CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
974     int *widthPtr;			/* The resulting width */
975     int *heightPtr;			/* The resulting height */
976 {
977     if (menuPtr->menuType != MASTER_MENU) {
978 	*heightPtr = 0;
979 	*widthPtr = 0;
980     } else {
981 	*heightPtr = fmPtr->linespace;
982 	*widthPtr = Tk_TextWidth(tkfont, "W", 1);
983     }
984 }
985 
986 /*
987  *--------------------------------------------------------------
988  *
989  * TkpComputeMenubarGeometry --
990  *
991  *	This procedure is invoked to recompute the size and
992  *	layout of a menu that is a menubar clone.
993  *
994  * Results:
995  *	None.
996  *
997  * Side effects:
998  *	Fields of menu entries are changed to reflect their
999  *	current positions, and the size of the menu window
1000  *	itself may be changed.
1001  *
1002  *--------------------------------------------------------------
1003  */
1004 
1005 void
TkpComputeMenubarGeometry(menuPtr)1006 TkpComputeMenubarGeometry(menuPtr)
1007     TkMenu *menuPtr;		/* Structure describing menu. */
1008 {
1009     Tk_Font tkfont;
1010     Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
1011     int width, height;
1012     int i, j;
1013     int x, y, currentRowHeight, maxWidth;
1014     int maxWindowWidth;
1015     int lastRowBreak  = 0;
1016     int helpMenuIndex = -1;
1017     TkMenuEntry *mePtr;
1018     int lastEntry;
1019     Tk_Font menuFont;
1020     int borderWidth;
1021     int activeBorderWidth;
1022     int lastSeparator = -1;
1023     int maxEWidth   = 0;
1024     int maxEHeight  = 0;
1025     int totalWidth  = 0;
1026     int rightWidth  = 0;
1027     int helpWidth   = 0;
1028     int multiColumn = 0;
1029 
1030     if (menuPtr->tkwin == NULL) {
1031 	return;
1032     }
1033 
1034     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1035 	    &borderWidth);
1036     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
1037 	    &activeBorderWidth);
1038     maxWidth = 0;
1039     if (menuPtr->numEntries == 0) {
1040 	height = 0;
1041     } else {
1042 	int borderWidth;
1043 
1044 	maxWindowWidth = Tk_Width(menuPtr->tkwin);
1045 	if (maxWindowWidth == 1) {
1046 	    maxWindowWidth = 0x7ffffff;
1047 	}
1048 	currentRowHeight = 0;
1049 	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1050 		&borderWidth);
1051 	x = y = borderWidth;
1052 	lastRowBreak = 0;
1053 
1054 	/*
1055 	 * On the Mac especially, getting font metrics can be quite slow,
1056 	 * so we want to do it intelligently. We are going to precalculate
1057 	 * them and pass them down to all of the measureing and drawing
1058 	 * routines. We will measure the font metrics of the menu once,
1059 	 * and if an entry has a font set, we will measure it as we come
1060 	 * to it, and then we decide which set to give the geometry routines.
1061 	 */
1062 
1063 	menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1064 	Tk_GetFontMetrics(menuFont, &menuMetrics);
1065 
1066 	for (i = 0; i < menuPtr->numEntries; i++) {
1067 	    mePtr = menuPtr->entries[i];
1068 	    mePtr->entryFlags &= ~ENTRY_LAST_COLUMN;
1069 	    if (mePtr->fontPtr != NULL) {
1070 		tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1071 		Tk_GetFontMetrics(tkfont, &entryMetrics);
1072 		fmPtr = &entryMetrics;
1073 	    } else {
1074 		tkfont = menuFont;
1075 		fmPtr = &menuMetrics;
1076 	    }
1077 
1078 	    /*
1079 	     * For every entry, we need to check to see whether or not we
1080 	     * wrap. If we do wrap, then we have to adjust all of the previous
1081 	     * entries' height and y position, because when we see them
1082 	     * the first time, we don't know how big its neighbor might
1083 	     * be.
1084 	     */
1085 
1086 	    if ((mePtr->type == SEPARATOR_ENTRY)
1087 		    || (mePtr->type == TEAROFF_ENTRY)) {
1088 		mePtr->height = mePtr->width = 0;
1089 		if (mePtr->type == SEPARATOR_ENTRY) {
1090 		    lastSeparator = i;
1091 		    rightWidth = 0;
1092 		}
1093 	    } else {
1094 		GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height);
1095 		mePtr->height = height + 2 * activeBorderWidth + 10;
1096 		mePtr->width = width;
1097 
1098 		GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr,
1099 			&width, &height);
1100 		mePtr->indicatorSpace = width;
1101 		if (width > 0) {
1102 		    mePtr->width += width;
1103 		}
1104 		mePtr->width += 2 * activeBorderWidth + 10;
1105 		if (mePtr->entryFlags & ENTRY_HELP_MENU) {
1106 		    helpMenuIndex = i;
1107 		    helpWidth = mePtr->width;
1108 		} else {
1109 		    if (mePtr->width > maxEWidth)
1110 			maxEWidth = mePtr->width;
1111 		    if (mePtr->height > maxEHeight)
1112 			maxEHeight = mePtr->height;
1113 		    if (lastSeparator != -1)
1114 			rightWidth += mePtr->width;
1115 		}
1116 	    }
1117 	}
1118 	/*
1119 	 * Now we have all the widths and heights we can tell if
1120 	 * menu as a whole wraps
1121 	 */
1122 	if (2*borderWidth + totalWidth > maxWindowWidth) {
1123 	    multiColumn   = 1;
1124 	    lastSeparator = -1;
1125 	}
1126 	/*
1127 	 * Do another pass setting the x,y positions
1128 	 */
1129 	x = borderWidth;
1130 	y = borderWidth;
1131 	maxWidth = x;
1132 	for (i = 0; i < menuPtr->numEntries; i++) {
1133 	    mePtr = menuPtr->entries[i];
1134 	    if (i == lastSeparator) {
1135 		x = maxWindowWidth - borderWidth - rightWidth - helpWidth;
1136 	    }
1137 	    if ((mePtr->type == SEPARATOR_ENTRY) || (mePtr->type == TEAROFF_ENTRY)) {
1138 		continue;
1139 	    }
1140 	    if (mePtr->entryFlags & ENTRY_HELP_MENU) {
1141 		continue;
1142 	    }
1143 	    if (multiColumn) {
1144 		mePtr->width  = maxEWidth;
1145 	    }
1146 	    if (x + mePtr->width + borderWidth > maxWindowWidth - helpWidth) {
1147 		y += currentRowHeight;
1148 		x = borderWidth;
1149 		currentRowHeight = 0;
1150 	    }
1151 	    mePtr->y = y;
1152 	    mePtr->x = x;
1153 	    x += mePtr->width;
1154 	    if (mePtr->height > currentRowHeight) {
1155 		currentRowHeight = mePtr->height;
1156 	    }
1157 	    if (x > maxWidth) {
1158 		maxWidth = x;
1159 	    }
1160 	}
1161 	height = y + currentRowHeight;
1162 
1163 	if (helpMenuIndex != -1) {
1164 	    mePtr = menuPtr->entries[helpMenuIndex];
1165 	    mePtr->x = maxWindowWidth - borderWidth - mePtr->width;
1166 	    mePtr->y = borderWidth;
1167 	    if (mePtr->y + mePtr->height > height) {
1168 		height = mePtr->y + mePtr->height;
1169 	    }
1170 	}
1171     }
1172     width = Tk_Width(menuPtr->tkwin);
1173 
1174     /*
1175      * The X server doesn't like zero dimensions, so round up to at least
1176      * 1 (a zero-sized menu should never really occur, anyway).
1177      */
1178 
1179     if (width <= 0) {
1180 	width = 1;
1181     }
1182     if (height <= 0) {
1183 	height = 1;
1184     }
1185     menuPtr->totalWidth = maxWidth + helpWidth + borderWidth;
1186     menuPtr->totalHeight = height + borderWidth;
1187 }
1188 
1189 /*
1190  *----------------------------------------------------------------------
1191  *
1192  * DrawTearoffEntry --
1193  *
1194  *	This procedure draws the background part of a menu.
1195  *
1196  * Results:
1197  *	None.
1198  *
1199  * Side effects:
1200  *	Commands are output to X to display the menu in its
1201  *	current mode.
1202  *
1203  *----------------------------------------------------------------------
1204  */
1205 
1206 static void
DrawTearoffEntry(menuPtr,mePtr,d,gc,tkfont,fmPtr,x,y,width,height)1207 DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1208     TkMenu *menuPtr;			/* The menu we are drawing */
1209     TkMenuEntry *mePtr;			/* The entry we are drawing */
1210     Drawable d;				/* The drawable we are drawing into */
1211     GC gc;				/* The gc we are drawing with */
1212     Tk_Font tkfont;			/* The font we are drawing with */
1213     CONST Tk_FontMetrics *fmPtr;	/* The metrics we are drawing with */
1214     int x;
1215     int y;
1216     int width;
1217     int height;
1218 {
1219     XPoint points[2];
1220     int segmentWidth, maxX;
1221     Tk_3DBorder border;
1222 
1223     if (menuPtr->menuType != MASTER_MENU) {
1224 	return;
1225     }
1226 
1227     points[0].x = x;
1228     points[0].y = y + height/2;
1229     points[1].y = points[0].y;
1230     segmentWidth = 6;
1231     maxX  = width - 1;
1232     border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
1233 
1234     while (points[0].x < maxX) {
1235 	points[1].x = points[0].x + segmentWidth;
1236 	if (points[1].x > maxX) {
1237 	    points[1].x = maxX;
1238 	}
1239 	Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
1240 		TK_RELIEF_RAISED);
1241 	points[0].x += 2 * segmentWidth;
1242     }
1243 }
1244 
1245 /*
1246  *--------------------------------------------------------------
1247  *
1248  * TkpInitializeMenuBindings --
1249  *
1250  *	For every interp, initializes the bindings for Windows
1251  *	menus. Does nothing on Mac or XWindows.
1252  *
1253  * Results:
1254  *	None.
1255  *
1256  * Side effects:
1257  *	C-level bindings are setup for the interp which will
1258  *	handle Alt-key sequences for menus without beeping
1259  *	or interfering with user-defined Alt-key bindings.
1260  *
1261  *--------------------------------------------------------------
1262  */
1263 
1264 void
TkpInitializeMenuBindings(interp,bindingTable)1265 TkpInitializeMenuBindings(interp, bindingTable)
1266     Tcl_Interp *interp;		    /* The interpreter to set. */
1267     Tk_BindingTable bindingTable;   /* The table to add to. */
1268 {
1269     /*
1270      * Nothing to do.
1271      */
1272 }
1273 
1274 /*
1275  *----------------------------------------------------------------------
1276  *
1277  * SetHelpMenu --
1278  *
1279  *	Given a menu, check to see whether or not it is a help menu
1280  *	cascade in a menubar. If it is, the entry that points to
1281  *	this menu will be marked.
1282  *
1283  * RESULTS:
1284  *	None.
1285  *
1286  * Side effects:
1287  *	Will set the ENTRY_HELP_MENU flag appropriately.
1288  *
1289  *----------------------------------------------------------------------
1290  */
1291 
1292 static void
SetHelpMenu(menuPtr)1293 SetHelpMenu(menuPtr)
1294      TkMenu *menuPtr;		/* The menu we are checking */
1295 {
1296     TkMenuEntry *cascadeEntryPtr;
1297 
1298     for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1299 	    cascadeEntryPtr != NULL;
1300 	    cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1301 	if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR)
1302 		&& (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL)
1303 		&& (menuPtr->masterMenuPtr->tkwin != NULL)) {
1304 	    TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr;
1305 	    char *helpMenuName = ckalloc(strlen(Tk_PathName(
1306 		    masterMenuPtr->tkwin)) + strlen(".help") + 1);
1307 
1308 	    strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin));
1309 	    strcat(helpMenuName, ".help");
1310 	    if (strcmp(helpMenuName,
1311 		    Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) {
1312 		cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU;
1313 	    } else {
1314 		cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU;
1315 	    }
1316 	    ckfree(helpMenuName);
1317 	}
1318     }
1319 }
1320 
1321 /*
1322  *----------------------------------------------------------------------
1323  *
1324  * TkpDrawMenuEntry --
1325  *
1326  *	Draws the given menu entry at the given coordinates with the
1327  *	given attributes.
1328  *
1329  * Results:
1330  *	None.
1331  *
1332  * Side effects:
1333  *	X Server commands are executed to display the menu entry.
1334  *
1335  *----------------------------------------------------------------------
1336  */
1337 
1338 void
TkpDrawMenuEntry(mePtr,d,tkfont,menuMetricsPtr,x,y,width,height,strictMotif,drawArrow)1339 TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
1340 	strictMotif, drawArrow)
1341     TkMenuEntry *mePtr;		    /* The entry to draw */
1342     Drawable d;			    /* What to draw into */
1343     Tk_Font tkfont;		    /* Precalculated font for menu */
1344     CONST Tk_FontMetrics *menuMetricsPtr;
1345 				    /* Precalculated metrics for menu */
1346     int x;			    /* X-coordinate of topleft of entry */
1347     int y;			    /* Y-coordinate of topleft of entry */
1348     int width;			    /* Width of the entry rectangle */
1349     int height;			    /* Height of the current rectangle */
1350     int strictMotif;		    /* Boolean flag */
1351     int drawArrow;		    /* Whether or not to draw the cascade
1352 				     * arrow for cascade items. Only applies
1353 				     * to Windows. */
1354 {
1355     GC gc, indicatorGC;
1356     TkMenu *menuPtr = mePtr->menuPtr;
1357     Tk_3DBorder bgBorder, activeBorder;
1358     CONST Tk_FontMetrics *fmPtr;
1359     Tk_FontMetrics entryMetrics;
1360     int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
1361     int adjustedY = y + padY;
1362     int adjustedHeight = height - 2 * padY;
1363 
1364     /*
1365      * Choose the gc for drawing the foreground part of the entry.
1366      */
1367 
1368     if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) {
1369 	gc = mePtr->activeGC;
1370 	if (gc == NULL) {
1371 	    gc = menuPtr->activeGC;
1372 	}
1373     } else {
1374     	TkMenuEntry *cascadeEntryPtr;
1375     	int parentDisabled = 0;
1376 
1377     	for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1378     		cascadeEntryPtr != NULL;
1379     		cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1380 	    if (cascadeEntryPtr->namePtr != NULL) {
1381 		char *name = Tcl_GetStringFromObj(cascadeEntryPtr->namePtr,
1382 			NULL);
1383 
1384 		if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
1385 		    if (cascadeEntryPtr->state == ENTRY_DISABLED) {
1386 			parentDisabled = 1;
1387 		    }
1388 		    break;
1389     	    	}
1390     	    }
1391     	}
1392 
1393 	if (((parentDisabled || (mePtr->state == ENTRY_DISABLED)))
1394 		&& (menuPtr->disabledFgPtr != NULL)) {
1395 	    gc = mePtr->disabledGC;
1396 	    if (gc == NULL) {
1397 		gc = menuPtr->disabledGC;
1398 	    }
1399 	} else {
1400 	    gc = mePtr->textGC;
1401 	    if (gc == NULL) {
1402 		gc = menuPtr->textGC;
1403 	    }
1404 	}
1405     }
1406     indicatorGC = mePtr->indicatorGC;
1407     if (indicatorGC == NULL) {
1408 	indicatorGC = menuPtr->indicatorGC;
1409     }
1410 
1411     bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1412 	    (mePtr->borderPtr == NULL)
1413 	    ? menuPtr->borderPtr : mePtr->borderPtr);
1414     if (strictMotif) {
1415 	activeBorder = bgBorder;
1416     } else {
1417 	activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1418 	    (mePtr->activeBorderPtr == NULL)
1419 	    ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr);
1420     }
1421 
1422     if (mePtr->fontPtr == NULL) {
1423 	fmPtr = menuMetricsPtr;
1424     } else {
1425 	tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1426 	Tk_GetFontMetrics(tkfont, &entryMetrics);
1427 	fmPtr = &entryMetrics;
1428     }
1429 
1430     /*
1431      * Need to draw the entire background, including padding. On Unix,
1432      * for menubars, we have to draw the rest of the entry taking
1433      * into account the padding.
1434      */
1435 
1436     DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
1437 	    bgBorder, x, y, width, height);
1438 
1439     if (mePtr->type == SEPARATOR_ENTRY) {
1440 	DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
1441 		fmPtr, x, adjustedY, width, adjustedHeight);
1442     } else if (mePtr->type == TEAROFF_ENTRY) {
1443 	DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1444 		width, adjustedHeight);
1445     } else {
1446 	DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1447 		width, adjustedHeight);
1448 	DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
1449 		activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
1450 	if (!mePtr->hideMargin) {
1451 	    DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
1452 		    fmPtr, x, adjustedY, width, adjustedHeight);
1453 	}
1454     }
1455 }
1456 
1457 /*
1458  *----------------------------------------------------------------------
1459  *
1460  * GetMenuLabelGeometry --
1461  *
1462  *	Figures out the size of the label portion of a menu item.
1463  *
1464  * Results:
1465  *	widthPtr and heightPtr are filled in with the correct geometry
1466  *	information.
1467  *
1468  * Side effects:
1469  *	None.
1470  *
1471  *----------------------------------------------------------------------
1472  */
1473 
1474 static void
GetMenuLabelGeometry(mePtr,tkfont,fmPtr,widthPtr,heightPtr)1475 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
1476     TkMenuEntry *mePtr;			/* The entry we are computing */
1477     Tk_Font tkfont;			/* The precalculated font */
1478     CONST Tk_FontMetrics *fmPtr;	/* The precalculated metrics */
1479     int *widthPtr;			/* The resulting width of the label
1480 					 * portion */
1481     int *heightPtr;			/* The resulting height of the label
1482 					 * portion */
1483 {
1484     TkMenu *menuPtr = mePtr->menuPtr;
1485     int haveImage = 0;
1486 
1487     if (mePtr->image != NULL) {
1488     	Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
1489 	haveImage = 1;
1490     } else if (mePtr->bitmapPtr !=  NULL) {
1491 	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
1492     	Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr);
1493 	haveImage = 1;
1494     } else {
1495 	*heightPtr = 0;
1496 	*widthPtr = 0;
1497     }
1498 
1499     if (haveImage && (mePtr->compound == COMPOUND_NONE)) {
1500 	/* We don't care about the text in this case */
1501     } else {
1502 	/* Either it is compound or we don't have an image */
1503     	if (mePtr->labelPtr != NULL) {
1504 	    int textWidth;
1505 	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
1506 	    textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength);
1507 
1508 	    if ((mePtr->compound != COMPOUND_NONE) && haveImage) {
1509 		switch ((enum compound) mePtr->compound) {
1510 		    case COMPOUND_TOP:
1511 		    case COMPOUND_BOTTOM: {
1512 			if (textWidth > *widthPtr) {
1513 			    *widthPtr = textWidth;
1514 			}
1515 			/* Add text and padding */
1516 			*heightPtr += fmPtr->linespace + 2;
1517 			break;
1518 		    }
1519 		    case COMPOUND_LEFT:
1520 		    case COMPOUND_RIGHT: {
1521 			if (fmPtr->linespace > *heightPtr) {
1522 			    *heightPtr = fmPtr->linespace;
1523 			}
1524 			/* Add text and padding */
1525 			*widthPtr += textWidth + 2;
1526 			break;
1527 		    }
1528 		    case COMPOUND_CENTER: {
1529 			if (fmPtr->linespace > *heightPtr) {
1530 			    *heightPtr = fmPtr->linespace;
1531 			}
1532 			if (textWidth > *widthPtr) {
1533 			    *widthPtr = textWidth;
1534 			}
1535 			break;
1536 		    }
1537 		    case COMPOUND_NONE: {break;}
1538 		}
1539     	} else {
1540 		/* We don't have an image or we're not compound */
1541 		*heightPtr = fmPtr->linespace;
1542 		*widthPtr = textWidth;
1543 	    }
1544 	} else {
1545 	    /* An empty entry still has this height */
1546 	    *heightPtr = fmPtr->linespace;
1547     	}
1548     }
1549     *heightPtr += 1;
1550 }
1551 
1552 /*
1553  *--------------------------------------------------------------
1554  *
1555  * TkpComputeStandardMenuGeometry --
1556  *
1557  *	This procedure is invoked to recompute the size and
1558  *	layout of a menu that is not a menubar clone.
1559  *
1560  * Results:
1561  *	None.
1562  *
1563  * Side effects:
1564  *	Fields of menu entries are changed to reflect their
1565  *	current positions, and the size of the menu window
1566  *	itself may be changed.
1567  *
1568  *--------------------------------------------------------------
1569  */
1570 
1571 void
TkpComputeStandardMenuGeometry(menuPtr)1572 TkpComputeStandardMenuGeometry(
1573     menuPtr)		/* Structure describing menu. */
1574     TkMenu *menuPtr;
1575 {
1576     Tk_Font tkfont, menuFont;
1577     Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
1578     int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
1579     int windowWidth, windowHeight, accelSpace;
1580     int i, j, lastColumnBreak = 0;
1581     TkMenuEntry *mePtr;
1582     int borderWidth, activeBorderWidth;
1583 
1584     if (menuPtr->tkwin == NULL) {
1585 	return;
1586     }
1587 
1588     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1589 	    &borderWidth);
1590     Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
1591 	    &activeBorderWidth);
1592     x = y = borderWidth;
1593     indicatorSpace = labelWidth = accelWidth = 0;
1594     windowHeight = windowWidth = 0;
1595 
1596     /*
1597      * On the Mac especially, getting font metrics can be quite slow,
1598      * so we want to do it intelligently. We are going to precalculate
1599      * them and pass them down to all of the measuring and drawing
1600      * routines. We will measure the font metrics of the menu once.
1601      * If an entry does not have its own font set, then we give
1602      * the geometry/drawing routines the menu's font and metrics.
1603      * If an entry has its own font, we will measure that font and
1604      * give all of the geometry/drawing the entry's font and metrics.
1605      */
1606 
1607     menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1608     Tk_GetFontMetrics(menuFont, &menuMetrics);
1609     accelSpace = Tk_TextWidth(menuFont, "M", 1);
1610 
1611     for (i = 0; i < menuPtr->numEntries; i++) {
1612 	mePtr = menuPtr->entries[i];
1613 	if (mePtr->fontPtr == NULL) {
1614 	    tkfont = menuFont;
1615 	    fmPtr = &menuMetrics;
1616 	} else {
1617 	    tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1618 	    Tk_GetFontMetrics(tkfont, &entryMetrics);
1619 	    fmPtr = &entryMetrics;
1620 	}
1621 
1622 	if ((i > 0) && mePtr->columnBreak) {
1623 	    if (accelWidth != 0) {
1624 		labelWidth += accelSpace;
1625 	    }
1626 	    for (j = lastColumnBreak; j < i; j++) {
1627 		menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1628 		menuPtr->entries[j]->labelWidth = labelWidth;
1629 		menuPtr->entries[j]->width = indicatorSpace + labelWidth
1630 			+ accelWidth + 2 * activeBorderWidth;
1631 		menuPtr->entries[j]->x = x;
1632 		menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
1633 	    }
1634 	    x += indicatorSpace + labelWidth + accelWidth
1635 		    + 2 * activeBorderWidth;
1636 	    windowWidth = x;
1637 	    indicatorSpace = labelWidth = accelWidth = 0;
1638 	    lastColumnBreak = i;
1639 	    y = borderWidth;
1640 	}
1641 
1642 	if (mePtr->type == SEPARATOR_ENTRY) {
1643 	    GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont,
1644 	    	    fmPtr, &width, &height);
1645 	    mePtr->height = height;
1646 	} else if (mePtr->type == TEAROFF_ENTRY) {
1647 	    GetTearoffEntryGeometry(menuPtr, mePtr, tkfont,
1648 	    	    fmPtr, &width, &height);
1649 	    mePtr->height = height;
1650 	    labelWidth = width;
1651 	} else {
1652 
1653 	    /*
1654 	     * For each entry, compute the height required by that
1655 	     * particular entry, plus three widths:  the width of the
1656 	     * label, the width to allow for an indicator to be displayed
1657 	     * to the left of the label (if any), and the width of the
1658 	     * accelerator to be displayed to the right of the label
1659 	     * (if any).  These sizes depend, of course, on the type
1660 	     * of the entry.
1661 	     */
1662 
1663 	    GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width,
1664 	    	    &height);
1665 	    mePtr->height = height;
1666 	    if (!mePtr->hideMargin) {
1667 		width += MENU_MARGIN_WIDTH;
1668 	    }
1669 	    if (width > labelWidth) {
1670 	    	labelWidth = width;
1671 	    }
1672 
1673 	    GetMenuAccelGeometry(menuPtr, mePtr, tkfont,
1674 		    fmPtr, &width, &height);
1675 	    if (height > mePtr->height) {
1676 	    	mePtr->height = height;
1677 	    }
1678 	    if (!mePtr->hideMargin) {
1679 		width += MENU_MARGIN_WIDTH;
1680 	    }
1681 	    if (width > accelWidth) {
1682 	    	accelWidth = width;
1683 	    }
1684 
1685 	    GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont,
1686 	    	    fmPtr, &width, &height);
1687 	    if (height > mePtr->height) {
1688 	    	mePtr->height = height;
1689 	    }
1690 	    if (!mePtr->hideMargin) {
1691 		width += MENU_MARGIN_WIDTH;
1692 	    }
1693 	    if (width > indicatorSpace) {
1694 	    	indicatorSpace = width;
1695 	    }
1696 
1697 	    mePtr->height += 2 * activeBorderWidth + MENU_DIVIDER_HEIGHT;
1698     	}
1699         mePtr->y = y;
1700 	y += mePtr->height;
1701 	if (y > windowHeight) {
1702 	    windowHeight = y;
1703 	}
1704     }
1705 
1706     if (accelWidth != 0) {
1707 	labelWidth += accelSpace;
1708     }
1709     for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
1710 	menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1711 	menuPtr->entries[j]->labelWidth = labelWidth;
1712 	menuPtr->entries[j]->width = indicatorSpace + labelWidth
1713 		+ accelWidth + 2 * activeBorderWidth;
1714 	menuPtr->entries[j]->x = x;
1715 	menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
1716     }
1717     windowWidth = x + indicatorSpace + labelWidth + accelWidth
1718 	    + 2 * activeBorderWidth + 2 * borderWidth;
1719 
1720 
1721     windowHeight += borderWidth;
1722 
1723     /*
1724      * The X server doesn't like zero dimensions, so round up to at least
1725      * 1 (a zero-sized menu should never really occur, anyway).
1726      */
1727 
1728     if (windowWidth <= 0) {
1729 	windowWidth = 1;
1730     }
1731     if (windowHeight <= 0) {
1732 	windowHeight = 1;
1733     }
1734     menuPtr->totalWidth = windowWidth;
1735     menuPtr->totalHeight = windowHeight;
1736 }
1737 
1738 /*
1739  *----------------------------------------------------------------------
1740  *
1741  * TkpMenuNotifyToplevelCreate --
1742  *
1743  *	This routine reconfigures the menu and the clones indicated by
1744  *	menuName becuase a toplevel has been created and any system
1745  *	menus need to be created. Not applicable to UNIX.
1746  *
1747  * Results:
1748  *	None.
1749  *
1750  * Side effects:
1751  *	An idle handler is set up to do the reconfiguration.
1752  *
1753  *----------------------------------------------------------------------
1754  */
1755 
1756 void
TkpMenuNotifyToplevelCreate(interp,menuName)1757 TkpMenuNotifyToplevelCreate(interp, menuName)
1758     Tcl_Interp *interp;			/* The interp the menu lives in. */
1759     char *menuName;			/* The name of the menu to
1760 					 * reconfigure. */
1761 {
1762     /*
1763      * Nothing to do.
1764      */
1765 }
1766 
1767 /*
1768  *----------------------------------------------------------------------
1769  *
1770  * TkpMenuInit --
1771  *
1772  *	Does platform-specific initialization of menus.
1773  *
1774  * Results:
1775  *	None.
1776  *
1777  * Side effects:
1778  *	None.
1779  *
1780  *----------------------------------------------------------------------
1781  */
1782 
1783 void
TkpMenuInit()1784 TkpMenuInit()
1785 {
1786     /*
1787      * Nothing to do.
1788      */
1789 }
1790 
1791 
1792 /*
1793  *----------------------------------------------------------------------
1794  *
1795  * TkpMenuThreadInit --
1796  *
1797  *	Does platform-specific initialization of thread-specific
1798  *      menu state.
1799  *
1800  * Results:
1801  *	None.
1802  *
1803  * Side effects:
1804  *	None.
1805  *
1806  *----------------------------------------------------------------------
1807  */
1808 
1809 void
TkpMenuThreadInit()1810 TkpMenuThreadInit()
1811 {
1812     /*
1813      * Nothing to do.
1814      */
1815 }
1816 
1817 
1818