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.2.2 2006/11/24 18:11:54 hobbs 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
37static void		SetHelpMenu _ANSI_ARGS_((TkMenu *menuPtr));
38static 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));
44static 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));
49static 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));
55static 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));
60static 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));
64static 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));
68static 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));
72static void		GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
73			    TkMenuEntry *mePtr, Tk_Font tkfont,
74			    CONST Tk_FontMetrics *fmPtr, int *widthPtr,
75			    int *heightPtr));
76static void		GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
77			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
78			    int *widthPtr, int *heightPtr));
79static void		GetMenuIndicatorGeometry _ANSI_ARGS_((
80			    TkMenu *menuPtr, TkMenuEntry *mePtr,
81			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
82			    int *widthPtr, int *heightPtr));
83static void		GetMenuSeparatorGeometry _ANSI_ARGS_((
84			    TkMenu *menuPtr, TkMenuEntry *mePtr,
85			    Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
86			    int *widthPtr, int *heightPtr));
87static 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
111int
112TkpNewMenu(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
136void
137TkpDestroyMenu(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
162void
163TkpDestroyMenuEntry(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
190int
191TkpConfigureMenuEntry(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
232int
233TkpMenuNewEntry(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
255void
256TkpSetWindowMenuBar(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
285void
286TkpSetMainMenubar(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
315static void
316GetMenuIndicatorGeometry(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
384static void
385GetMenuAccelGeometry(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
423static void
424DrawMenuEntryBackground(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
477static void
478DrawMenuEntryAccelerator(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
551static void
552DrawMenuEntryIndicator(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
647static void
648DrawMenuSeparator(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
693static void
694DrawMenuEntryLabel(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		/*
766		 * Position image in the indicator space to the left of the
767		 * entries, unless this entry is a radio|check button because
768		 * then the indicator space will be used.
769		 */
770		textXOffset = imageWidth + 2;
771		textYOffset = 0;
772		imageXOffset = 0;
773		imageYOffset = 0;
774		if ((mePtr->type != CHECK_BUTTON_ENTRY)
775			&& (mePtr->type != RADIO_BUTTON_ENTRY)) {
776		    textXOffset -= indicatorSpace;
777		    if (textXOffset < 0) {
778			textXOffset = 0;
779		    }
780		    imageXOffset = -indicatorSpace;
781		}
782		break;
783	    }
784	    case COMPOUND_RIGHT: {
785		textXOffset = 0;
786		textYOffset = 0;
787		imageXOffset = textWidth + 2;
788		imageYOffset = 0;
789		break;
790	    }
791	    case COMPOUND_CENTER: {
792		textXOffset = (fullWidth - textWidth)/2;
793		textYOffset = 0;
794		imageXOffset = (fullWidth - imageWidth)/2;
795		imageYOffset = 0;
796		break;
797	    }
798	    case COMPOUND_NONE: {break;}
799	}
800    } else {
801	textXOffset = 0;
802	textYOffset = 0;
803	imageXOffset = 0;
804	imageYOffset = 0;
805    }
806
807    /*
808     * Draw label and/or bitmap or image for entry.
809     */
810
811    if (mePtr->image != NULL) {
812    	if ((mePtr->selectImage != NULL)
813	    	&& (mePtr->entryFlags & ENTRY_SELECTED)) {
814	    Tk_RedrawImage(mePtr->selectImage, 0, 0,
815		    imageWidth, imageHeight, d, leftEdge + imageXOffset,
816		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
817    	} else {
818	    Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
819		    imageHeight, d, leftEdge + imageXOffset,
820		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset));
821    	}
822    } else if (mePtr->bitmapPtr != None) {
823	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
824	XCopyPlane(menuPtr->display, bitmap, d,	gc, 0, 0,
825		(unsigned) imageWidth, (unsigned) imageHeight,
826		leftEdge + imageXOffset,
827		(int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1);
828    }
829    if ((mePtr->compound != COMPOUND_NONE) || !haveImage) {
830	int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
831    	if (mePtr->labelLength > 0) {
832	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
833	    Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
834		    mePtr->labelLength, leftEdge + textXOffset,
835		    baseline + textYOffset);
836	    DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr,
837		    x + textXOffset, y + textYOffset,
838		    width, height);
839    	}
840    }
841
842    if (mePtr->state == ENTRY_DISABLED) {
843	if (menuPtr->disabledFgPtr == NULL) {
844	    XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
845		    (unsigned) width, (unsigned) height);
846	} else if ((mePtr->image != NULL)
847		&& (menuPtr->disabledImageGC != None)) {
848	    XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
849		    leftEdge + imageXOffset,
850		    (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset),
851		    (unsigned) imageWidth, (unsigned) imageHeight);
852	}
853    }
854}
855
856/*
857 *----------------------------------------------------------------------
858 *
859 * DrawMenuUnderline --
860 *
861 *	On appropriate platforms, draw the underline character for the
862 *	menu.
863 *
864 * Results:
865 *	None.
866 *
867 * Side effects:
868 *	Commands are output to X to display the menu in its
869 *	current mode.
870 *
871 *----------------------------------------------------------------------
872 */
873
874static void
875DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
876    TkMenu *menuPtr;			/* The menu to draw into */
877    TkMenuEntry *mePtr;			/* The entry we are drawing */
878    Drawable d;				/* What we are drawing into */
879    GC gc;				/* The gc to draw into */
880    Tk_Font tkfont;			/* The precalculated font */
881    CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
882    int x;
883    int y;
884    int width;
885    int height;
886{
887    if ((mePtr->underline >= 0) && (mePtr->labelPtr != NULL)) {
888	int len;
889
890	/* do the unicode call just to prevent overruns */
891	Tcl_GetUnicodeFromObj(mePtr->labelPtr, &len);
892	if (mePtr->underline < len) {
893	    int activeBorderWidth;
894	    int leftEdge;
895	    CONST char *label, *start, *end;
896
897	    label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
898	    start = Tcl_UtfAtIndex(label, mePtr->underline);
899	    end = Tcl_UtfNext(start);
900
901	    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
902		    menuPtr->activeBorderWidthPtr, &activeBorderWidth);
903	    leftEdge = x + mePtr->indicatorSpace + activeBorderWidth;
904	    if (menuPtr->menuType == MENUBAR) {
905		leftEdge += 5;
906	    }
907
908	    Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label, leftEdge,
909		    y + (height + fmPtr->ascent - fmPtr->descent) / 2,
910		    start - label, end - label);
911	}
912    }
913}
914
915/*
916 *----------------------------------------------------------------------
917 *
918 * TkpPostMenu --
919 *
920 *	Posts a menu on the screen
921 *
922 * Results:
923 *	None.
924 *
925 * Side effects:
926 *	The menu is posted and handled.
927 *
928 *----------------------------------------------------------------------
929 */
930
931int
932TkpPostMenu(interp, menuPtr, x, y)
933    Tcl_Interp *interp;
934    TkMenu *menuPtr;
935    int x;
936    int y;
937{
938    return TkPostTearoffMenu(interp, menuPtr, x, y);
939}
940
941/*
942 *----------------------------------------------------------------------
943 *
944 * GetMenuSeparatorGeometry --
945 *
946 *	Gets the width and height of the indicator area of a menu.
947 *
948 * Results:
949 *	widthPtr and heightPtr are set.
950 *
951 * Side effects:
952 *	None.
953 *
954 *----------------------------------------------------------------------
955 */
956
957static void
958GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr,
959	heightPtr)
960    TkMenu *menuPtr;			/* The menu we are measuring */
961    TkMenuEntry *mePtr;			/* The entry we are measuring */
962    Tk_Font tkfont;			/* The precalculated font */
963    CONST Tk_FontMetrics *fmPtr;	/* The precalcualted font metrics */
964    int *widthPtr;			/* The resulting width */
965    int *heightPtr;			/* The resulting height */
966{
967    *widthPtr = 0;
968    *heightPtr = fmPtr->linespace;
969}
970
971/*
972 *----------------------------------------------------------------------
973 *
974 * GetTearoffEntryGeometry --
975 *
976 *	Gets the width and height of the indicator area of a menu.
977 *
978 * Results:
979 *	widthPtr and heightPtr are set.
980 *
981 * Side effects:
982 *	None.
983 *
984 *----------------------------------------------------------------------
985 */
986
987static void
988GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
989    TkMenu *menuPtr;			/* The menu we are drawing */
990    TkMenuEntry *mePtr;			/* The entry we are measuring */
991    Tk_Font tkfont;			/* The precalculated font */
992    CONST Tk_FontMetrics *fmPtr;	/* The precalculated font metrics */
993    int *widthPtr;			/* The resulting width */
994    int *heightPtr;			/* The resulting height */
995{
996    if (menuPtr->menuType != MASTER_MENU) {
997	*heightPtr = 0;
998	*widthPtr = 0;
999    } else {
1000	*heightPtr = fmPtr->linespace;
1001	*widthPtr = Tk_TextWidth(tkfont, "W", 1);
1002    }
1003}
1004
1005/*
1006 *--------------------------------------------------------------
1007 *
1008 * TkpComputeMenubarGeometry --
1009 *
1010 *	This procedure is invoked to recompute the size and
1011 *	layout of a menu that is a menubar clone.
1012 *
1013 * Results:
1014 *	None.
1015 *
1016 * Side effects:
1017 *	Fields of menu entries are changed to reflect their
1018 *	current positions, and the size of the menu window
1019 *	itself may be changed.
1020 *
1021 *--------------------------------------------------------------
1022 */
1023
1024void
1025TkpComputeMenubarGeometry(menuPtr)
1026    TkMenu *menuPtr;		/* Structure describing menu. */
1027{
1028    Tk_Font tkfont;
1029    Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
1030    int width, height;
1031    int i, j;
1032    int x, y, currentRowHeight, maxWidth;
1033    int maxWindowWidth;
1034    int lastRowBreak;
1035    int helpMenuIndex = -1;
1036    TkMenuEntry *mePtr;
1037    int lastEntry;
1038    Tk_Font menuFont;
1039    int borderWidth;
1040    int activeBorderWidth;
1041
1042    if (menuPtr->tkwin == NULL) {
1043	return;
1044    }
1045
1046    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1047	    &borderWidth);
1048    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
1049	    &activeBorderWidth);
1050    maxWidth = 0;
1051    if (menuPtr->numEntries == 0) {
1052	height = 0;
1053    } else {
1054	int borderWidth;
1055
1056	maxWindowWidth = Tk_Width(menuPtr->tkwin);
1057	if (maxWindowWidth == 1) {
1058	    maxWindowWidth = 0x7ffffff;
1059	}
1060	currentRowHeight = 0;
1061	Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1062		&borderWidth);
1063	x = y = borderWidth;
1064	lastRowBreak = 0;
1065
1066	/*
1067	 * On the Mac especially, getting font metrics can be quite slow,
1068	 * so we want to do it intelligently. We are going to precalculate
1069	 * them and pass them down to all of the measureing and drawing
1070	 * routines. We will measure the font metrics of the menu once,
1071	 * and if an entry has a font set, we will measure it as we come
1072	 * to it, and then we decide which set to give the geometry routines.
1073	 */
1074
1075	menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1076	Tk_GetFontMetrics(menuFont, &menuMetrics);
1077
1078	for (i = 0; i < menuPtr->numEntries; i++) {
1079	    mePtr = menuPtr->entries[i];
1080	    mePtr->entryFlags &= ~ENTRY_LAST_COLUMN;
1081	    if (mePtr->fontPtr != NULL) {
1082		tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1083		Tk_GetFontMetrics(tkfont, &entryMetrics);
1084		fmPtr = &entryMetrics;
1085	    } else {
1086		tkfont = menuFont;
1087		fmPtr = &menuMetrics;
1088	    }
1089
1090	    /*
1091	     * For every entry, we need to check to see whether or not we
1092	     * wrap. If we do wrap, then we have to adjust all of the previous
1093	     * entries' height and y position, because when we see them
1094	     * the first time, we don't know how big its neighbor might
1095	     * be.
1096	     */
1097
1098	    if ((mePtr->type == SEPARATOR_ENTRY)
1099		    || (mePtr->type == TEAROFF_ENTRY)) {
1100		mePtr->height = mePtr->width = 0;
1101	    } else {
1102		GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height);
1103		mePtr->height = height + 2 * activeBorderWidth + 10;
1104		mePtr->width = width;
1105
1106		GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr,
1107			&width, &height);
1108		mePtr->indicatorSpace = width;
1109		if (width > 0) {
1110		    mePtr->width += width;
1111		}
1112		mePtr->width += 2 * activeBorderWidth + 10;
1113	    }
1114	    if (mePtr->entryFlags & ENTRY_HELP_MENU) {
1115		helpMenuIndex = i;
1116	    } else if (x + mePtr->width + borderWidth > maxWindowWidth) {
1117
1118		if (i == lastRowBreak) {
1119		    mePtr->y = y;
1120		    mePtr->x = x;
1121		    lastRowBreak++;
1122		    y += mePtr->height;
1123		    currentRowHeight = 0;
1124		} else {
1125		    x = borderWidth;
1126		    for (j = lastRowBreak; j < i; j++) {
1127			menuPtr->entries[j]->y = y + currentRowHeight
1128			        - menuPtr->entries[j]->height;
1129			menuPtr->entries[j]->x = x;
1130			x += menuPtr->entries[j]->width;
1131		    }
1132		    lastRowBreak = i;
1133		    y += currentRowHeight;
1134		    currentRowHeight = mePtr->height;
1135		}
1136		if (x > maxWidth) {
1137		    maxWidth = x;
1138		}
1139		x = borderWidth;
1140	    } else {
1141		x += mePtr->width;
1142		if (mePtr->height > currentRowHeight) {
1143		    currentRowHeight = mePtr->height;
1144		}
1145	    }
1146	}
1147
1148	lastEntry = menuPtr->numEntries - 1;
1149	if (helpMenuIndex == lastEntry) {
1150	    lastEntry--;
1151	}
1152	if ((lastEntry >= 0) && (x + menuPtr->entries[lastEntry]->width
1153		+ borderWidth > maxWidth)) {
1154	    maxWidth = x + menuPtr->entries[lastEntry]->width + borderWidth;
1155	}
1156	x = borderWidth;
1157	for (j = lastRowBreak; j < menuPtr->numEntries; j++) {
1158	    if (j == helpMenuIndex) {
1159		continue;
1160	    }
1161	    menuPtr->entries[j]->y = y + currentRowHeight
1162		    - menuPtr->entries[j]->height;
1163	    menuPtr->entries[j]->x = x;
1164	    x += menuPtr->entries[j]->width;
1165	}
1166
1167
1168	if (helpMenuIndex != -1) {
1169	    mePtr = menuPtr->entries[helpMenuIndex];
1170	    if (x + mePtr->width + borderWidth > maxWindowWidth) {
1171		y += currentRowHeight;
1172		currentRowHeight = mePtr->height;
1173		x = borderWidth;
1174	    } else if (mePtr->height > currentRowHeight) {
1175		currentRowHeight = mePtr->height;
1176	    }
1177	    mePtr->x = maxWindowWidth - borderWidth - mePtr->width;
1178	    mePtr->y = y + currentRowHeight - mePtr->height;
1179	}
1180	height = y + currentRowHeight + borderWidth;
1181    }
1182    width = Tk_Width(menuPtr->tkwin);
1183
1184    /*
1185     * The X server doesn't like zero dimensions, so round up to at least
1186     * 1 (a zero-sized menu should never really occur, anyway).
1187     */
1188
1189    if (width <= 0) {
1190	width = 1;
1191    }
1192    if (height <= 0) {
1193	height = 1;
1194    }
1195    menuPtr->totalWidth = maxWidth;
1196    menuPtr->totalHeight = height;
1197}
1198
1199/*
1200 *----------------------------------------------------------------------
1201 *
1202 * DrawTearoffEntry --
1203 *
1204 *	This procedure draws the background part of a menu.
1205 *
1206 * Results:
1207 *	None.
1208 *
1209 * Side effects:
1210 *	Commands are output to X to display the menu in its
1211 *	current mode.
1212 *
1213 *----------------------------------------------------------------------
1214 */
1215
1216static void
1217DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1218    TkMenu *menuPtr;			/* The menu we are drawing */
1219    TkMenuEntry *mePtr;			/* The entry we are drawing */
1220    Drawable d;				/* The drawable we are drawing into */
1221    GC gc;				/* The gc we are drawing with */
1222    Tk_Font tkfont;			/* The font we are drawing with */
1223    CONST Tk_FontMetrics *fmPtr;	/* The metrics we are drawing with */
1224    int x;
1225    int y;
1226    int width;
1227    int height;
1228{
1229    XPoint points[2];
1230    int segmentWidth, maxX;
1231    Tk_3DBorder border;
1232
1233    if (menuPtr->menuType != MASTER_MENU) {
1234	return;
1235    }
1236
1237    points[0].x = x;
1238    points[0].y = y + height/2;
1239    points[1].y = points[0].y;
1240    segmentWidth = 6;
1241    maxX  = width - 1;
1242    border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
1243
1244    while (points[0].x < maxX) {
1245	points[1].x = points[0].x + segmentWidth;
1246	if (points[1].x > maxX) {
1247	    points[1].x = maxX;
1248	}
1249	Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
1250		TK_RELIEF_RAISED);
1251	points[0].x += 2 * segmentWidth;
1252    }
1253}
1254
1255/*
1256 *--------------------------------------------------------------
1257 *
1258 * TkpInitializeMenuBindings --
1259 *
1260 *	For every interp, initializes the bindings for Windows
1261 *	menus. Does nothing on Mac or XWindows.
1262 *
1263 * Results:
1264 *	None.
1265 *
1266 * Side effects:
1267 *	C-level bindings are setup for the interp which will
1268 *	handle Alt-key sequences for menus without beeping
1269 *	or interfering with user-defined Alt-key bindings.
1270 *
1271 *--------------------------------------------------------------
1272 */
1273
1274void
1275TkpInitializeMenuBindings(interp, bindingTable)
1276    Tcl_Interp *interp;		    /* The interpreter to set. */
1277    Tk_BindingTable bindingTable;   /* The table to add to. */
1278{
1279    /*
1280     * Nothing to do.
1281     */
1282}
1283
1284/*
1285 *----------------------------------------------------------------------
1286 *
1287 * SetHelpMenu --
1288 *
1289 *	Given a menu, check to see whether or not it is a help menu
1290 *	cascade in a menubar. If it is, the entry that points to
1291 *	this menu will be marked.
1292 *
1293 * RESULTS:
1294 *	None.
1295 *
1296 * Side effects:
1297 *	Will set the ENTRY_HELP_MENU flag appropriately.
1298 *
1299 *----------------------------------------------------------------------
1300 */
1301
1302static void
1303SetHelpMenu(menuPtr)
1304     TkMenu *menuPtr;		/* The menu we are checking */
1305{
1306    TkMenuEntry *cascadeEntryPtr;
1307
1308    for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1309	    cascadeEntryPtr != NULL;
1310	    cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1311	if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR)
1312		&& (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL)
1313		&& (menuPtr->masterMenuPtr->tkwin != NULL)) {
1314	    TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr;
1315	    char *helpMenuName = ckalloc(strlen(Tk_PathName(
1316		    masterMenuPtr->tkwin)) + strlen(".help") + 1);
1317
1318	    strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin));
1319	    strcat(helpMenuName, ".help");
1320	    if (strcmp(helpMenuName,
1321		    Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) {
1322		cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU;
1323	    } else {
1324		cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU;
1325	    }
1326	    ckfree(helpMenuName);
1327	}
1328    }
1329}
1330
1331/*
1332 *----------------------------------------------------------------------
1333 *
1334 * TkpDrawMenuEntry --
1335 *
1336 *	Draws the given menu entry at the given coordinates with the
1337 *	given attributes.
1338 *
1339 * Results:
1340 *	None.
1341 *
1342 * Side effects:
1343 *	X Server commands are executed to display the menu entry.
1344 *
1345 *----------------------------------------------------------------------
1346 */
1347
1348void
1349TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
1350	strictMotif, drawArrow)
1351    TkMenuEntry *mePtr;		    /* The entry to draw */
1352    Drawable d;			    /* What to draw into */
1353    Tk_Font tkfont;		    /* Precalculated font for menu */
1354    CONST Tk_FontMetrics *menuMetricsPtr;
1355				    /* Precalculated metrics for menu */
1356    int x;			    /* X-coordinate of topleft of entry */
1357    int y;			    /* Y-coordinate of topleft of entry */
1358    int width;			    /* Width of the entry rectangle */
1359    int height;			    /* Height of the current rectangle */
1360    int strictMotif;		    /* Boolean flag */
1361    int drawArrow;		    /* Whether or not to draw the cascade
1362				     * arrow for cascade items. Only applies
1363				     * to Windows. */
1364{
1365    GC gc, indicatorGC;
1366    TkMenu *menuPtr = mePtr->menuPtr;
1367    Tk_3DBorder bgBorder, activeBorder;
1368    CONST Tk_FontMetrics *fmPtr;
1369    Tk_FontMetrics entryMetrics;
1370    int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
1371    int adjustedY = y + padY;
1372    int adjustedHeight = height - 2 * padY;
1373
1374    /*
1375     * Choose the gc for drawing the foreground part of the entry.
1376     */
1377
1378    if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) {
1379	gc = mePtr->activeGC;
1380	if (gc == NULL) {
1381	    gc = menuPtr->activeGC;
1382	}
1383    } else {
1384    	TkMenuEntry *cascadeEntryPtr;
1385    	int parentDisabled = 0;
1386
1387    	for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1388    		cascadeEntryPtr != NULL;
1389    		cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1390	    if (cascadeEntryPtr->namePtr != NULL) {
1391		char *name = Tcl_GetStringFromObj(cascadeEntryPtr->namePtr,
1392			NULL);
1393
1394		if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
1395		    if (cascadeEntryPtr->state == ENTRY_DISABLED) {
1396			parentDisabled = 1;
1397		    }
1398		    break;
1399    	    	}
1400    	    }
1401    	}
1402
1403	if (((parentDisabled || (mePtr->state == ENTRY_DISABLED)))
1404		&& (menuPtr->disabledFgPtr != NULL)) {
1405	    gc = mePtr->disabledGC;
1406	    if (gc == NULL) {
1407		gc = menuPtr->disabledGC;
1408	    }
1409	} else {
1410	    gc = mePtr->textGC;
1411	    if (gc == NULL) {
1412		gc = menuPtr->textGC;
1413	    }
1414	}
1415    }
1416    indicatorGC = mePtr->indicatorGC;
1417    if (indicatorGC == NULL) {
1418	indicatorGC = menuPtr->indicatorGC;
1419    }
1420
1421    bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1422	    (mePtr->borderPtr == NULL)
1423	    ? menuPtr->borderPtr : mePtr->borderPtr);
1424    if (strictMotif) {
1425	activeBorder = bgBorder;
1426    } else {
1427	activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1428	    (mePtr->activeBorderPtr == NULL)
1429	    ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr);
1430    }
1431
1432    if (mePtr->fontPtr == NULL) {
1433	fmPtr = menuMetricsPtr;
1434    } else {
1435	tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1436	Tk_GetFontMetrics(tkfont, &entryMetrics);
1437	fmPtr = &entryMetrics;
1438    }
1439
1440    /*
1441     * Need to draw the entire background, including padding. On Unix,
1442     * for menubars, we have to draw the rest of the entry taking
1443     * into account the padding.
1444     */
1445
1446    DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
1447	    bgBorder, x, y, width, height);
1448
1449    if (mePtr->type == SEPARATOR_ENTRY) {
1450	DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
1451		fmPtr, x, adjustedY, width, adjustedHeight);
1452    } else if (mePtr->type == TEAROFF_ENTRY) {
1453	DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1454		width, adjustedHeight);
1455    } else {
1456	DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1457		width, adjustedHeight);
1458	DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
1459		activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
1460	if (!mePtr->hideMargin) {
1461	    DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
1462		    fmPtr, x, adjustedY, width, adjustedHeight);
1463	}
1464    }
1465}
1466
1467/*
1468 *----------------------------------------------------------------------
1469 *
1470 * GetMenuLabelGeometry --
1471 *
1472 *	Figures out the size of the label portion of a menu item.
1473 *
1474 * Results:
1475 *	widthPtr and heightPtr are filled in with the correct geometry
1476 *	information.
1477 *
1478 * Side effects:
1479 *	None.
1480 *
1481 *----------------------------------------------------------------------
1482 */
1483
1484static void
1485GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
1486    TkMenuEntry *mePtr;			/* The entry we are computing */
1487    Tk_Font tkfont;			/* The precalculated font */
1488    CONST Tk_FontMetrics *fmPtr;	/* The precalculated metrics */
1489    int *widthPtr;			/* The resulting width of the label
1490					 * portion */
1491    int *heightPtr;			/* The resulting height of the label
1492					 * portion */
1493{
1494    TkMenu *menuPtr = mePtr->menuPtr;
1495    int haveImage = 0;
1496
1497    if (mePtr->image != NULL) {
1498    	Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
1499	haveImage = 1;
1500    } else if (mePtr->bitmapPtr !=  NULL) {
1501	Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
1502    	Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr);
1503	haveImage = 1;
1504    } else {
1505	*heightPtr = 0;
1506	*widthPtr = 0;
1507    }
1508
1509    if (haveImage && (mePtr->compound == COMPOUND_NONE)) {
1510	/* We don't care about the text in this case */
1511    } else {
1512	/* Either it is compound or we don't have an image */
1513    	if (mePtr->labelPtr != NULL) {
1514	    int textWidth;
1515	    char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
1516	    textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength);
1517
1518	    if ((mePtr->compound != COMPOUND_NONE) && haveImage) {
1519		switch ((enum compound) mePtr->compound) {
1520		    case COMPOUND_TOP:
1521		    case COMPOUND_BOTTOM: {
1522			if (textWidth > *widthPtr) {
1523			    *widthPtr = textWidth;
1524			}
1525			/* Add text and padding */
1526			*heightPtr += fmPtr->linespace + 2;
1527			break;
1528		    }
1529		    case COMPOUND_LEFT:
1530		    case COMPOUND_RIGHT: {
1531			if (fmPtr->linespace > *heightPtr) {
1532			    *heightPtr = fmPtr->linespace;
1533			}
1534			/* Add text and padding */
1535			*widthPtr += textWidth + 2;
1536			break;
1537		    }
1538		    case COMPOUND_CENTER: {
1539			if (fmPtr->linespace > *heightPtr) {
1540			    *heightPtr = fmPtr->linespace;
1541			}
1542			if (textWidth > *widthPtr) {
1543			    *widthPtr = textWidth;
1544			}
1545			break;
1546		    }
1547		    case COMPOUND_NONE: {break;}
1548		}
1549    	} else {
1550		/* We don't have an image or we're not compound */
1551		*heightPtr = fmPtr->linespace;
1552		*widthPtr = textWidth;
1553	    }
1554	} else {
1555	    /* An empty entry still has this height */
1556	    *heightPtr = fmPtr->linespace;
1557    	}
1558    }
1559    *heightPtr += 1;
1560}
1561
1562/*
1563 *--------------------------------------------------------------
1564 *
1565 * TkpComputeStandardMenuGeometry --
1566 *
1567 *	This procedure is invoked to recompute the size and
1568 *	layout of a menu that is not a menubar clone.
1569 *
1570 * Results:
1571 *	None.
1572 *
1573 * Side effects:
1574 *	Fields of menu entries are changed to reflect their
1575 *	current positions, and the size of the menu window
1576 *	itself may be changed.
1577 *
1578 *--------------------------------------------------------------
1579 */
1580
1581void
1582TkpComputeStandardMenuGeometry(
1583    menuPtr)		/* Structure describing menu. */
1584    TkMenu *menuPtr;
1585{
1586    Tk_Font tkfont, menuFont;
1587    Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
1588    int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
1589    int windowWidth, windowHeight, accelSpace;
1590    int i, j, lastColumnBreak = 0;
1591    TkMenuEntry *mePtr;
1592    int borderWidth, activeBorderWidth;
1593
1594    if (menuPtr->tkwin == NULL) {
1595	return;
1596    }
1597
1598    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1599	    &borderWidth);
1600    Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
1601	    &activeBorderWidth);
1602    x = y = borderWidth;
1603    indicatorSpace = labelWidth = accelWidth = 0;
1604    windowHeight = windowWidth = 0;
1605
1606    /*
1607     * On the Mac especially, getting font metrics can be quite slow,
1608     * so we want to do it intelligently. We are going to precalculate
1609     * them and pass them down to all of the measuring and drawing
1610     * routines. We will measure the font metrics of the menu once.
1611     * If an entry does not have its own font set, then we give
1612     * the geometry/drawing routines the menu's font and metrics.
1613     * If an entry has its own font, we will measure that font and
1614     * give all of the geometry/drawing the entry's font and metrics.
1615     */
1616
1617    menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1618    Tk_GetFontMetrics(menuFont, &menuMetrics);
1619    accelSpace = Tk_TextWidth(menuFont, "M", 1);
1620
1621    for (i = 0; i < menuPtr->numEntries; i++) {
1622	mePtr = menuPtr->entries[i];
1623	if (mePtr->fontPtr == NULL) {
1624	    tkfont = menuFont;
1625	    fmPtr = &menuMetrics;
1626	} else {
1627	    tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1628	    Tk_GetFontMetrics(tkfont, &entryMetrics);
1629	    fmPtr = &entryMetrics;
1630	}
1631
1632	if ((i > 0) && mePtr->columnBreak) {
1633	    if (accelWidth != 0) {
1634		labelWidth += accelSpace;
1635	    }
1636	    for (j = lastColumnBreak; j < i; j++) {
1637		menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1638		menuPtr->entries[j]->labelWidth = labelWidth;
1639		menuPtr->entries[j]->width = indicatorSpace + labelWidth
1640			+ accelWidth + 2 * activeBorderWidth;
1641		menuPtr->entries[j]->x = x;
1642		menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
1643	    }
1644	    x += indicatorSpace + labelWidth + accelWidth
1645		    + 2 * activeBorderWidth;
1646	    windowWidth = x;
1647	    indicatorSpace = labelWidth = accelWidth = 0;
1648	    lastColumnBreak = i;
1649	    y = borderWidth;
1650	}
1651
1652	if (mePtr->type == SEPARATOR_ENTRY) {
1653	    GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont,
1654	    	    fmPtr, &width, &height);
1655	    mePtr->height = height;
1656	} else if (mePtr->type == TEAROFF_ENTRY) {
1657	    GetTearoffEntryGeometry(menuPtr, mePtr, tkfont,
1658	    	    fmPtr, &width, &height);
1659	    mePtr->height = height;
1660	    labelWidth = width;
1661	} else {
1662
1663	    /*
1664	     * For each entry, compute the height required by that
1665	     * particular entry, plus three widths:  the width of the
1666	     * label, the width to allow for an indicator to be displayed
1667	     * to the left of the label (if any), and the width of the
1668	     * accelerator to be displayed to the right of the label
1669	     * (if any).  These sizes depend, of course, on the type
1670	     * of the entry.
1671	     */
1672
1673	    GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width,
1674	    	    &height);
1675	    mePtr->height = height;
1676	    if (!mePtr->hideMargin) {
1677		width += MENU_MARGIN_WIDTH;
1678	    }
1679	    if (width > labelWidth) {
1680	    	labelWidth = width;
1681	    }
1682
1683	    GetMenuAccelGeometry(menuPtr, mePtr, tkfont,
1684		    fmPtr, &width, &height);
1685	    if (height > mePtr->height) {
1686	    	mePtr->height = height;
1687	    }
1688	    if (!mePtr->hideMargin) {
1689		width += MENU_MARGIN_WIDTH;
1690	    }
1691	    if (width > accelWidth) {
1692	    	accelWidth = width;
1693	    }
1694
1695	    GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont,
1696	    	    fmPtr, &width, &height);
1697	    if (height > mePtr->height) {
1698	    	mePtr->height = height;
1699	    }
1700	    if (!mePtr->hideMargin) {
1701		width += MENU_MARGIN_WIDTH;
1702	    }
1703	    if (width > indicatorSpace) {
1704	    	indicatorSpace = width;
1705	    }
1706
1707	    mePtr->height += 2 * activeBorderWidth + MENU_DIVIDER_HEIGHT;
1708    	}
1709        mePtr->y = y;
1710	y += mePtr->height;
1711	if (y > windowHeight) {
1712	    windowHeight = y;
1713	}
1714    }
1715
1716    if (accelWidth != 0) {
1717	labelWidth += accelSpace;
1718    }
1719    for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
1720	menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1721	menuPtr->entries[j]->labelWidth = labelWidth;
1722	menuPtr->entries[j]->width = indicatorSpace + labelWidth
1723		+ accelWidth + 2 * activeBorderWidth;
1724	menuPtr->entries[j]->x = x;
1725	menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
1726    }
1727    windowWidth = x + indicatorSpace + labelWidth + accelWidth
1728	    + 2 * activeBorderWidth + 2 * borderWidth;
1729
1730
1731    windowHeight += borderWidth;
1732
1733    /*
1734     * The X server doesn't like zero dimensions, so round up to at least
1735     * 1 (a zero-sized menu should never really occur, anyway).
1736     */
1737
1738    if (windowWidth <= 0) {
1739	windowWidth = 1;
1740    }
1741    if (windowHeight <= 0) {
1742	windowHeight = 1;
1743    }
1744    menuPtr->totalWidth = windowWidth;
1745    menuPtr->totalHeight = windowHeight;
1746}
1747
1748/*
1749 *----------------------------------------------------------------------
1750 *
1751 * TkpMenuNotifyToplevelCreate --
1752 *
1753 *	This routine reconfigures the menu and the clones indicated by
1754 *	menuName becuase a toplevel has been created and any system
1755 *	menus need to be created. Not applicable to UNIX.
1756 *
1757 * Results:
1758 *	None.
1759 *
1760 * Side effects:
1761 *	An idle handler is set up to do the reconfiguration.
1762 *
1763 *----------------------------------------------------------------------
1764 */
1765
1766void
1767TkpMenuNotifyToplevelCreate(interp, menuName)
1768    Tcl_Interp *interp;			/* The interp the menu lives in. */
1769    char *menuName;			/* The name of the menu to
1770					 * reconfigure. */
1771{
1772    /*
1773     * Nothing to do.
1774     */
1775}
1776
1777/*
1778 *----------------------------------------------------------------------
1779 *
1780 * TkpMenuInit --
1781 *
1782 *	Does platform-specific initialization of menus.
1783 *
1784 * Results:
1785 *	None.
1786 *
1787 * Side effects:
1788 *	None.
1789 *
1790 *----------------------------------------------------------------------
1791 */
1792
1793void
1794TkpMenuInit()
1795{
1796    /*
1797     * Nothing to do.
1798     */
1799}
1800
1801
1802/*
1803 *----------------------------------------------------------------------
1804 *
1805 * TkpMenuThreadInit --
1806 *
1807 *	Does platform-specific initialization of thread-specific
1808 *      menu state.
1809 *
1810 * Results:
1811 *	None.
1812 *
1813 * Side effects:
1814 *	None.
1815 *
1816 *----------------------------------------------------------------------
1817 */
1818
1819void
1820TkpMenuThreadInit()
1821{
1822    /*
1823     * Nothing to do.
1824     */
1825}
1826
1827