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