4 * This module implements the UNIX platform-specific features of menus.
6 * Copyright (c) 1996-1998 by Sun Microsystems, Inc.
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17 #include "tkUnixInt.h"
21 * Constants used for menu drawing.
24 #define MENU_MARGIN_WIDTH 2
25 #define MENU_DIVIDER_HEIGHT 2
28 * Platform specific flags for Unix.
31 #define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1
34 * Procedures used internally.
37 static void SetHelpMenu _ANSI_ARGS_((TkMenu *menuPtr));
38 static void DrawMenuEntryAccelerator _ANSI_ARGS_((
39 TkMenu *menuPtr, TkMenuEntry *mePtr,
40 Drawable d, GC gc, Tk_Font tkfont,
41 CONST Tk_FontMetrics *fmPtr,
42 Tk_3DBorder activeBorder, int x, int y,
43 int width, int height, int drawArrow));
44 static void DrawMenuEntryBackground _ANSI_ARGS_((
45 TkMenu *menuPtr, TkMenuEntry *mePtr,
46 Drawable d, Tk_3DBorder activeBorder,
47 Tk_3DBorder bgBorder, int x, int y,
48 int width, int heigth));
49 static void DrawMenuEntryIndicator _ANSI_ARGS_((
50 TkMenu *menuPtr, TkMenuEntry *mePtr,
51 Drawable d, GC gc, GC indicatorGC,
53 CONST Tk_FontMetrics *fmPtr, int x, int y,
54 int width, int height));
55 static void DrawMenuEntryLabel _ANSI_ARGS_((
56 TkMenu * menuPtr, TkMenuEntry *mePtr, Drawable d,
57 GC gc, Tk_Font tkfont,
58 CONST Tk_FontMetrics *fmPtr, int x, int y,
59 int width, int height));
60 static void DrawMenuSeparator _ANSI_ARGS_((TkMenu *menuPtr,
61 TkMenuEntry *mePtr, Drawable d, GC gc,
62 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
63 int x, int y, int width, int height));
64 static void DrawTearoffEntry _ANSI_ARGS_((TkMenu *menuPtr,
65 TkMenuEntry *mePtr, Drawable d, GC gc,
66 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
67 int x, int y, int width, int height));
68 static void DrawMenuUnderline _ANSI_ARGS_((TkMenu *menuPtr,
69 TkMenuEntry *mePtr, Drawable d, GC gc,
70 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr, int x,
71 int y, int width, int height));
72 static void GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
73 TkMenuEntry *mePtr, Tk_Font tkfont,
74 CONST Tk_FontMetrics *fmPtr, int *widthPtr,
76 static void GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
77 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
78 int *widthPtr, int *heightPtr));
79 static void GetMenuIndicatorGeometry _ANSI_ARGS_((
80 TkMenu *menuPtr, TkMenuEntry *mePtr,
81 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
82 int *widthPtr, int *heightPtr));
83 static void GetMenuSeparatorGeometry _ANSI_ARGS_((
84 TkMenu *menuPtr, TkMenuEntry *mePtr,
85 Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
86 int *widthPtr, int *heightPtr));
87 static void GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
88 TkMenuEntry *mePtr, Tk_Font tkfont,
89 CONST Tk_FontMetrics *fmPtr, int *widthPtr,
94 *----------------------------------------------------------------------
98 * Gets the platform-specific piece of the menu. Invoked during idle
99 * after the generic part of the menu has been created.
102 * Standard TCL error.
105 * Allocates any platform specific allocations and places them
106 * in the platformData field of the menuPtr.
108 *----------------------------------------------------------------------
115 SetHelpMenu(menuPtr);
120 *----------------------------------------------------------------------
124 * Destroys platform-specific menu structures. Called when the
125 * generic menu structure is destroyed for the menu.
131 * All platform-specific allocations are freed up.
133 *----------------------------------------------------------------------
137 TkpDestroyMenu(menuPtr)
146 *----------------------------------------------------------------------
148 * TkpDestroyMenuEntry --
150 * Cleans up platform-specific menu entry items. Called when entry
151 * is destroyed in the generic code.
157 * All platform specific allocations are freed up.
159 *----------------------------------------------------------------------
163 TkpDestroyMenuEntry(mEntryPtr)
164 TkMenuEntry *mEntryPtr;
172 *----------------------------------------------------------------------
174 * TkpConfigureMenuEntry --
176 * Processes configuration options for menu entries. Called when
177 * the generic options are processed for the menu.
180 * Returns standard TCL result. If TCL_ERROR is returned, then
181 * the interp's result contains an error message.
184 * Configuration information get set for mePtr; old resources
185 * get freed, if any need it.
187 *----------------------------------------------------------------------
191 TkpConfigureMenuEntry(mePtr)
192 register TkMenuEntry *mePtr; /* Information about menu entry; may
193 * or may not already have values for
197 * If this is a cascade menu, and the child menu exists, check to
198 * see if the child menu is a help menu.
201 if ((mePtr->type == CASCADE_ENTRY) && (mePtr->namePtr != NULL)) {
202 TkMenuReferences *menuRefPtr;
204 menuRefPtr = TkFindMenuReferencesObj(mePtr->menuPtr->interp,
206 if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
207 SetHelpMenu(menuRefPtr->menuPtr);
214 *----------------------------------------------------------------------
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.
224 * Standard TCL error.
229 *----------------------------------------------------------------------
233 TkpMenuNewEntry(mePtr)
240 *----------------------------------------------------------------------
242 * TkpSetWindowMenuBar --
244 * Sets up the menu as a menubar in the given window.
250 * Recomputes geometry of given window.
252 *----------------------------------------------------------------------
256 TkpSetWindowMenuBar(tkwin, menuPtr)
257 Tk_Window tkwin; /* The window we are setting */
258 TkMenu *menuPtr; /* The menu we are setting */
260 if (menuPtr == NULL) {
261 TkUnixSetMenubar(tkwin, NULL);
263 TkUnixSetMenubar(tkwin, menuPtr->tkwin);
268 *----------------------------------------------------------------------
270 * TkpSetMainMenuBar --
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.
280 * Recompute geometry of given window.
282 *----------------------------------------------------------------------
286 TkpSetMainMenubar(interp, tkwin, menuName)
297 *----------------------------------------------------------------------
299 * GetMenuIndicatorGeometry --
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
307 * widthPtr and heightPtr point to the new geometry values.
312 *----------------------------------------------------------------------
316 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
317 TkMenu *menuPtr; /* The menu we are drawing. */
318 TkMenuEntry *mePtr; /* The entry we are interested in. */
319 Tk_Font tkfont; /* The precalculated font */
320 CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
321 int *widthPtr; /* The resulting width */
322 int *heightPtr; /* The resulting height */
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)
335 mePtr->platformEntryData =
336 (TkMenuPlatformEntryData) ((75 * mePtr->height)
340 *widthPtr = *heightPtr = mePtr->height;
341 if (mePtr->type == CHECK_BUTTON_ENTRY) {
342 mePtr->platformEntryData = (TkMenuPlatformEntryData)
343 ((80 * mePtr->height) / 100);
345 mePtr->platformEntryData = (TkMenuPlatformEntryData)
352 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
353 menuPtr->borderWidthPtr, &borderWidth);
355 *widthPtr = borderWidth;
360 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
363 *widthPtr = borderWidth;
369 *----------------------------------------------------------------------
371 * GetMenuAccelGeometry --
373 * Get the geometry of the accelerator area of a menu item.
376 * heightPtr and widthPtr are set.
381 *----------------------------------------------------------------------
385 GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
386 TkMenu *menuPtr; /* The menu was are drawing */
387 TkMenuEntry *mePtr; /* The entry we are getting the geometry for */
388 Tk_Font tkfont; /* The precalculated font */
389 CONST Tk_FontMetrics *fmPtr;/* The precalculated font metrics */
390 int *widthPtr; /* The width of the acclerator area */
391 int *heightPtr; /* The height of the accelerator area */
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);
400 *widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength);
407 *----------------------------------------------------------------------
409 * DrawMenuEntryBackground --
411 * This procedure draws the background part of a menu.
417 * Commands are output to X to display the menu in its
420 *----------------------------------------------------------------------
424 DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, bgBorder, x, y,
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 */
436 if (mePtr->state == ENTRY_ACTIVE) {
438 int activeBorderWidth;
440 bgBorder = activeBorder;
442 if ((menuPtr->menuType == MENUBAR)
443 && ((menuPtr->postedCascade == NULL)
444 || (menuPtr->postedCascade != mePtr))) {
445 relief = TK_RELIEF_FLAT;
447 relief = TK_RELIEF_RAISED;
450 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
451 menuPtr->activeBorderWidthPtr, &activeBorderWidth);
452 Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
453 activeBorderWidth, relief);
455 Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
461 *----------------------------------------------------------------------
463 * DrawMenuEntryAccelerator --
465 * This procedure draws the background part of a menu.
471 * Commands are output to X to display the menu in its
474 *----------------------------------------------------------------------
478 DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, activeBorder,
479 x, y, width, height, drawArrow)
480 TkMenu *menuPtr; /* The menu we are drawing */
481 TkMenuEntry *mePtr; /* The entry we are drawing */
482 Drawable d; /* The drawable we are drawing into */
483 GC gc; /* The precalculated gc to draw with */
484 Tk_Font tkfont; /* The precalculated font */
485 CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
486 Tk_3DBorder activeBorder; /* The border for an active item */
487 int x; /* Left coordinate of entry rect */
488 int y; /* Top coordinate of entry rect */
489 int width; /* Width of entry */
490 int height; /* Height of entry */
491 int drawArrow; /* Whether or not to draw arrow. */
494 int borderWidth, activeBorderWidth;
497 * Draw accelerator or cascade arrow.
500 if (menuPtr->menuType == MENUBAR) {
504 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
506 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
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;
525 if (menuPtr->menuType == MENUBAR) {
528 Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel,
529 mePtr->accelLength, left,
530 (y + (height + fmPtr->ascent - fmPtr->descent) / 2));
535 *----------------------------------------------------------------------
537 * DrawMenuEntryIndicator --
539 * This procedure draws the background part of a menu.
545 * Commands are output to X to display the menu in its
548 *----------------------------------------------------------------------
552 DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr,
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 */
567 * Draw check-button indicator.
570 if ((mePtr->type == CHECK_BUTTON_ENTRY) && mePtr->indicatorOn) {
572 int activeBorderWidth;
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) {
582 top = y + (height - dim)/2;
583 border = Tk_Get3DBorderFromObj(menuPtr->tkwin,
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
592 XFillRectangle(menuPtr->display, d, indicatorGC, left, top,
593 (unsigned int) dim, (unsigned int) dim);
598 * Draw radio-button indicator.
601 if ((mePtr->type == RADIO_BUTTON_ENTRY) && mePtr->indicatorOn) {
606 border = Tk_Get3DBorderFromObj(menuPtr->tkwin,
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);
622 Tk_Fill3DPolygon(menuPtr->tkwin, d, border, points, 4,
623 DECORATION_BORDER_WIDTH, TK_RELIEF_FLAT);
625 Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 4,
626 DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN);
631 *----------------------------------------------------------------------
633 * DrawMenuSeparator --
635 * This procedure draws a separator menu item.
641 * Commands are output to X to display the menu in its
644 *----------------------------------------------------------------------
648 DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
649 TkMenu *menuPtr; /* The menu we are drawing */
650 TkMenuEntry *mePtr; /* The entry we are drawing */
651 Drawable d; /* The drawable we are using */
652 GC gc; /* The gc to draw into */
653 Tk_Font tkfont; /* The font to draw with */
654 CONST Tk_FontMetrics *fmPtr; /* The font metrics from the font */
663 if (menuPtr->menuType == MENUBAR) {
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,
677 *----------------------------------------------------------------------
679 * DrawMenuEntryLabel --
681 * This procedure draws the label part of a menu.
687 * Commands are output to X to display the menu in its
690 *----------------------------------------------------------------------
694 DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
695 TkMenu *menuPtr; /* The menu we are drawing. */
696 TkMenuEntry *mePtr; /* The entry we are drawing. */
697 Drawable d; /* What we are drawing into. */
698 GC gc; /* The gc we are drawing into.*/
699 Tk_Font tkfont; /* The precalculated font. */
700 CONST Tk_FontMetrics *fmPtr;/* The precalculated font metrics. */
701 int x; /* Left edge. */
702 int y; /* Top edge. */
703 int width; /* width of entry. */
704 int height; /* height of entry. */
707 int indicatorSpace = mePtr->indicatorSpace;
708 int activeBorderWidth;
710 int imageHeight, imageWidth;
712 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
714 leftEdge = x + indicatorSpace + activeBorderWidth;
715 if (menuPtr->menuType == MENUBAR) {
720 * Draw label or bitmap or image for entry.
723 baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
724 if (mePtr->image != NULL) {
725 Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
726 if ((mePtr->selectImage != NULL)
727 && (mePtr->entryFlags & ENTRY_SELECTED)) {
728 Tk_RedrawImage(mePtr->selectImage, 0, 0,
729 imageWidth, imageHeight, d, leftEdge,
730 (int) (y + (mePtr->height - imageHeight)/2));
732 Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
733 imageHeight, d, leftEdge,
734 (int) (y + (mePtr->height - imageHeight)/2));
736 } else if (mePtr->bitmapPtr != None) {
738 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
739 Tk_SizeOfBitmap(menuPtr->display,bitmap, &width, &height);
740 XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, (unsigned) width,
741 (unsigned) height, leftEdge,
742 (int) (y + (mePtr->height - height)/2), 1);
744 if (mePtr->labelLength > 0) {
745 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
746 Tk_DrawChars(menuPtr->display, d, gc, tkfont, label,
747 mePtr->labelLength, leftEdge, baseline);
748 DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y,
753 if (mePtr->state == ENTRY_DISABLED) {
754 if (menuPtr->disabledFgPtr == NULL) {
755 XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y,
756 (unsigned) width, (unsigned) height);
757 } else if ((mePtr->image != NULL)
758 && (menuPtr->disabledImageGC != None)) {
759 XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC,
761 (int) (y + (mePtr->height - imageHeight)/2),
762 (unsigned) imageWidth, (unsigned) imageHeight);
768 *----------------------------------------------------------------------
770 * DrawMenuUnderline --
772 * On appropriate platforms, draw the underline character for the
779 * Commands are output to X to display the menu in its
782 *----------------------------------------------------------------------
786 DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
787 TkMenu *menuPtr; /* The menu to draw into */
788 TkMenuEntry *mePtr; /* The entry we are drawing */
789 Drawable d; /* What we are drawing into */
790 GC gc; /* The gc to draw into */
791 Tk_Font tkfont; /* The precalculated font */
792 CONST Tk_FontMetrics *fmPtr; /* The precalculated font metrics */
798 int indicatorSpace = mePtr->indicatorSpace;
800 if (mePtr->underline >= 0) {
801 int activeBorderWidth;
803 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
804 char *start = Tcl_UtfAtIndex(label, mePtr->underline);
805 char *end = Tcl_UtfNext(start);
807 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin,
808 menuPtr->activeBorderWidthPtr, &activeBorderWidth);
809 leftEdge = x + indicatorSpace + activeBorderWidth;
810 if (menuPtr->menuType == MENUBAR) {
814 Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label,
815 leftEdge, y + (height + fmPtr->ascent - fmPtr->descent) / 2,
816 start - label, end - label);
821 *----------------------------------------------------------------------
825 * Posts a menu on the screen
831 * The menu is posted and handled.
833 *----------------------------------------------------------------------
837 TkpPostMenu(interp, menuPtr, x, y)
843 return TkPostTearoffMenu(interp, menuPtr, x, y);
847 *----------------------------------------------------------------------
849 * GetMenuSeparatorGeometry --
851 * Gets the width and height of the indicator area of a menu.
854 * widthPtr and heightPtr are set.
859 *----------------------------------------------------------------------
863 GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr,
865 TkMenu *menuPtr; /* The menu we are measuring */
866 TkMenuEntry *mePtr; /* The entry we are measuring */
867 Tk_Font tkfont; /* The precalculated font */
868 CONST Tk_FontMetrics *fmPtr; /* The precalcualted font metrics */
869 int *widthPtr; /* The resulting width */
870 int *heightPtr; /* The resulting height */
873 *heightPtr = fmPtr->linespace;
877 *----------------------------------------------------------------------
879 * GetTearoffEntryGeometry --
881 * Gets the width and height of the indicator area of a menu.
884 * widthPtr and heightPtr are set.
889 *----------------------------------------------------------------------
893 GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
894 TkMenu *menuPtr; /* The menu we are drawing */
895 TkMenuEntry *mePtr; /* The entry we are measuring */
896 Tk_Font tkfont; /* The precalculated font */
897 CONST Tk_FontMetrics *fmPtr; /* The precalculated font metrics */
898 int *widthPtr; /* The resulting width */
899 int *heightPtr; /* The resulting height */
901 if (menuPtr->menuType != MASTER_MENU) {
905 *heightPtr = fmPtr->linespace;
906 *widthPtr = Tk_TextWidth(tkfont, "W", 1);
911 *--------------------------------------------------------------
913 * TkpComputeMenubarGeometry --
915 * This procedure is invoked to recompute the size and
916 * layout of a menu that is a menubar clone.
922 * Fields of menu entries are changed to reflect their
923 * current positions, and the size of the menu window
924 * itself may be changed.
926 *--------------------------------------------------------------
930 TkpComputeMenubarGeometry(menuPtr)
931 TkMenu *menuPtr; /* Structure describing menu. */
934 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
937 int x, y, currentRowHeight, maxWidth;
940 int helpMenuIndex = -1;
945 int activeBorderWidth;
947 if (menuPtr->tkwin == NULL) {
951 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
953 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
956 if (menuPtr->numEntries == 0) {
961 maxWindowWidth = Tk_Width(menuPtr->tkwin);
962 if (maxWindowWidth == 1) {
963 maxWindowWidth = 0x7ffffff;
965 currentRowHeight = 0;
966 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
972 * On the Mac especially, getting font metrics can be quite slow,
973 * so we want to do it intelligently. We are going to precalculate
974 * them and pass them down to all of the measureing and drawing
975 * routines. We will measure the font metrics of the menu once,
976 * and if an entry has a font set, we will measure it as we come
977 * to it, and then we decide which set to give the geometry routines.
980 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
981 Tk_GetFontMetrics(menuFont, &menuMetrics);
983 for (i = 0; i < menuPtr->numEntries; i++) {
984 mePtr = menuPtr->entries[i];
985 mePtr->entryFlags &= ~ENTRY_LAST_COLUMN;
986 if (mePtr->fontPtr != NULL) {
987 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
988 Tk_GetFontMetrics(tkfont, &entryMetrics);
989 fmPtr = &entryMetrics;
992 fmPtr = &menuMetrics;
996 * For every entry, we need to check to see whether or not we
997 * wrap. If we do wrap, then we have to adjust all of the previous
998 * entries' height and y position, because when we see them
999 * the first time, we don't know how big its neighbor might
1003 if ((mePtr->type == SEPARATOR_ENTRY)
1004 || (mePtr->type == TEAROFF_ENTRY)) {
1005 mePtr->height = mePtr->width = 0;
1007 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width, &height);
1008 mePtr->height = height + 2 * activeBorderWidth + 10;
1009 mePtr->width = width;
1011 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr,
1013 mePtr->indicatorSpace = width;
1015 mePtr->width += width;
1017 mePtr->width += 2 * activeBorderWidth + 10;
1019 if (mePtr->entryFlags & ENTRY_HELP_MENU) {
1021 } else if (x + mePtr->width + borderWidth > maxWindowWidth) {
1023 if (i == lastRowBreak) {
1028 currentRowHeight = 0;
1031 for (j = lastRowBreak; j < i; j++) {
1032 menuPtr->entries[j]->y = y + currentRowHeight
1033 - menuPtr->entries[j]->height;
1034 menuPtr->entries[j]->x = x;
1035 x += menuPtr->entries[j]->width;
1038 y += currentRowHeight;
1039 currentRowHeight = mePtr->height;
1047 if (mePtr->height > currentRowHeight) {
1048 currentRowHeight = mePtr->height;
1053 lastEntry = menuPtr->numEntries - 1;
1054 if (helpMenuIndex == lastEntry) {
1057 if ((lastEntry >= 0) && (x + menuPtr->entries[lastEntry]->width
1058 + borderWidth > maxWidth)) {
1059 maxWidth = x + menuPtr->entries[lastEntry]->width + borderWidth;
1062 for (j = lastRowBreak; j < menuPtr->numEntries; j++) {
1063 if (j == helpMenuIndex) {
1066 menuPtr->entries[j]->y = y + currentRowHeight
1067 - menuPtr->entries[j]->height;
1068 menuPtr->entries[j]->x = x;
1069 x += menuPtr->entries[j]->width;
1073 if (helpMenuIndex != -1) {
1074 mePtr = menuPtr->entries[helpMenuIndex];
1075 if (x + mePtr->width + borderWidth > maxWindowWidth) {
1076 y += currentRowHeight;
1077 currentRowHeight = mePtr->height;
1079 } else if (mePtr->height > currentRowHeight) {
1080 currentRowHeight = mePtr->height;
1082 mePtr->x = maxWindowWidth - borderWidth - mePtr->width;
1083 mePtr->y = y + currentRowHeight - mePtr->height;
1085 height = y + currentRowHeight + borderWidth;
1087 width = Tk_Width(menuPtr->tkwin);
1090 * The X server doesn't like zero dimensions, so round up to at least
1091 * 1 (a zero-sized menu should never really occur, anyway).
1100 menuPtr->totalWidth = maxWidth;
1101 menuPtr->totalHeight = height;
1105 *----------------------------------------------------------------------
1107 * DrawTearoffEntry --
1109 * This procedure draws the background part of a menu.
1115 * Commands are output to X to display the menu in its
1118 *----------------------------------------------------------------------
1122 DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
1123 TkMenu *menuPtr; /* The menu we are drawing */
1124 TkMenuEntry *mePtr; /* The entry we are drawing */
1125 Drawable d; /* The drawable we are drawing into */
1126 GC gc; /* The gc we are drawing with */
1127 Tk_Font tkfont; /* The font we are drawing with */
1128 CONST Tk_FontMetrics *fmPtr; /* The metrics we are drawing with */
1135 int segmentWidth, maxX;
1138 if (menuPtr->menuType != MASTER_MENU) {
1143 points[0].y = y + height/2;
1144 points[1].y = points[0].y;
1147 border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr);
1149 while (points[0].x < maxX) {
1150 points[1].x = points[0].x + segmentWidth;
1151 if (points[1].x > maxX) {
1154 Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1,
1156 points[0].x += 2 * segmentWidth;
1161 *--------------------------------------------------------------
1163 * TkpInitializeMenuBindings --
1165 * For every interp, initializes the bindings for Windows
1166 * menus. Does nothing on Mac or XWindows.
1172 * C-level bindings are setup for the interp which will
1173 * handle Alt-key sequences for menus without beeping
1174 * or interfering with user-defined Alt-key bindings.
1176 *--------------------------------------------------------------
1180 TkpInitializeMenuBindings(interp, bindingTable)
1181 Tcl_Interp *interp; /* The interpreter to set. */
1182 Tk_BindingTable bindingTable; /* The table to add to. */
1190 *----------------------------------------------------------------------
1194 * Given a menu, check to see whether or not it is a help menu
1195 * cascade in a menubar. If it is, the entry that points to
1196 * this menu will be marked.
1202 * Will set the ENTRY_HELP_MENU flag appropriately.
1204 *----------------------------------------------------------------------
1208 SetHelpMenu(menuPtr)
1209 TkMenu *menuPtr; /* The menu we are checking */
1211 TkMenuEntry *cascadeEntryPtr;
1213 for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1214 cascadeEntryPtr != NULL;
1215 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1216 if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR)
1217 && (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL)
1218 && (menuPtr->masterMenuPtr->tkwin != NULL)) {
1219 TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr;
1220 char *helpMenuName = ckalloc(strlen(Tk_PathName(
1221 masterMenuPtr->tkwin)) + strlen(".help") + 1);
1223 strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin));
1224 strcat(helpMenuName, ".help");
1225 if (strcmp(helpMenuName,
1226 Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) {
1227 cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU;
1229 cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU;
1231 ckfree(helpMenuName);
1237 *----------------------------------------------------------------------
1239 * TkpDrawMenuEntry --
1241 * Draws the given menu entry at the given coordinates with the
1248 * X Server commands are executed to display the menu entry.
1250 *----------------------------------------------------------------------
1254 TkpDrawMenuEntry(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
1255 strictMotif, drawArrow)
1256 TkMenuEntry *mePtr; /* The entry to draw */
1257 Drawable d; /* What to draw into */
1258 Tk_Font tkfont; /* Precalculated font for menu */
1259 CONST Tk_FontMetrics *menuMetricsPtr;
1260 /* Precalculated metrics for menu */
1261 int x; /* X-coordinate of topleft of entry */
1262 int y; /* Y-coordinate of topleft of entry */
1263 int width; /* Width of the entry rectangle */
1264 int height; /* Height of the current rectangle */
1265 int strictMotif; /* Boolean flag */
1266 int drawArrow; /* Whether or not to draw the cascade
1267 * arrow for cascade items. Only applies
1271 TkMenu *menuPtr = mePtr->menuPtr;
1272 Tk_3DBorder bgBorder, activeBorder;
1273 CONST Tk_FontMetrics *fmPtr;
1274 Tk_FontMetrics entryMetrics;
1275 int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
1276 int adjustedY = y + padY;
1277 int adjustedHeight = height - 2 * padY;
1280 * Choose the gc for drawing the foreground part of the entry.
1283 if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) {
1284 gc = mePtr->activeGC;
1286 gc = menuPtr->activeGC;
1289 TkMenuEntry *cascadeEntryPtr;
1290 int parentDisabled = 0;
1292 for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
1293 cascadeEntryPtr != NULL;
1294 cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
1295 if (cascadeEntryPtr->namePtr != NULL) {
1296 char *name = Tcl_GetStringFromObj(cascadeEntryPtr->namePtr,
1299 if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) {
1300 if (cascadeEntryPtr->state == ENTRY_DISABLED) {
1308 if (((parentDisabled || (mePtr->state == ENTRY_DISABLED)))
1309 && (menuPtr->disabledFgPtr != NULL)) {
1310 gc = mePtr->disabledGC;
1312 gc = menuPtr->disabledGC;
1317 gc = menuPtr->textGC;
1321 indicatorGC = mePtr->indicatorGC;
1322 if (indicatorGC == NULL) {
1323 indicatorGC = menuPtr->indicatorGC;
1326 bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1327 (mePtr->borderPtr == NULL)
1328 ? menuPtr->borderPtr : mePtr->borderPtr);
1330 activeBorder = bgBorder;
1332 activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin,
1333 (mePtr->activeBorderPtr == NULL)
1334 ? menuPtr->activeBorderPtr : mePtr->activeBorderPtr);
1337 if (mePtr->fontPtr == NULL) {
1338 fmPtr = menuMetricsPtr;
1340 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1341 Tk_GetFontMetrics(tkfont, &entryMetrics);
1342 fmPtr = &entryMetrics;
1346 * Need to draw the entire background, including padding. On Unix,
1347 * for menubars, we have to draw the rest of the entry taking
1348 * into account the padding.
1351 DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder,
1352 bgBorder, x, y, width, height);
1354 if (mePtr->type == SEPARATOR_ENTRY) {
1355 DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
1356 fmPtr, x, adjustedY, width, adjustedHeight);
1357 } else if (mePtr->type == TEAROFF_ENTRY) {
1358 DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1359 width, adjustedHeight);
1361 DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
1362 width, adjustedHeight);
1363 DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
1364 activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
1365 if (!mePtr->hideMargin) {
1366 DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
1367 fmPtr, x, adjustedY, width, adjustedHeight);
1373 *----------------------------------------------------------------------
1375 * GetMenuLabelGeometry --
1377 * Figures out the size of the label portion of a menu item.
1380 * widthPtr and heightPtr are filled in with the correct geometry
1386 *----------------------------------------------------------------------
1390 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
1391 TkMenuEntry *mePtr; /* The entry we are computing */
1392 Tk_Font tkfont; /* The precalculated font */
1393 CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
1394 int *widthPtr; /* The resulting width of the label
1396 int *heightPtr; /* The resulting height of the label
1399 TkMenu *menuPtr = mePtr->menuPtr;
1401 if (mePtr->image != NULL) {
1402 Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
1403 } else if (mePtr->bitmapPtr != NULL) {
1404 Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr);
1405 Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr);
1407 *heightPtr = fmPtr->linespace;
1409 if (mePtr->labelPtr != NULL) {
1410 char *label = Tcl_GetStringFromObj(mePtr->labelPtr, NULL);
1412 *widthPtr = Tk_TextWidth(tkfont, label, mePtr->labelLength);
1421 *--------------------------------------------------------------
1423 * TkpComputeStandardMenuGeometry --
1425 * This procedure is invoked to recompute the size and
1426 * layout of a menu that is not a menubar clone.
1432 * Fields of menu entries are changed to reflect their
1433 * current positions, and the size of the menu window
1434 * itself may be changed.
1436 *--------------------------------------------------------------
1440 TkpComputeStandardMenuGeometry(
1441 menuPtr) /* Structure describing menu. */
1444 Tk_Font tkfont, menuFont;
1445 Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
1446 int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
1447 int windowWidth, windowHeight, accelSpace;
1448 int i, j, lastColumnBreak = 0;
1450 int borderWidth, activeBorderWidth;
1452 if (menuPtr->tkwin == NULL) {
1456 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->borderWidthPtr,
1458 Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, menuPtr->activeBorderWidthPtr,
1459 &activeBorderWidth);
1460 x = y = borderWidth;
1461 indicatorSpace = labelWidth = accelWidth = 0;
1462 windowHeight = windowWidth = 0;
1465 * On the Mac especially, getting font metrics can be quite slow,
1466 * so we want to do it intelligently. We are going to precalculate
1467 * them and pass them down to all of the measuring and drawing
1468 * routines. We will measure the font metrics of the menu once.
1469 * If an entry does not have its own font set, then we give
1470 * the geometry/drawing routines the menu's font and metrics.
1471 * If an entry has its own font, we will measure that font and
1472 * give all of the geometry/drawing the entry's font and metrics.
1475 menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr);
1476 Tk_GetFontMetrics(menuFont, &menuMetrics);
1477 accelSpace = Tk_TextWidth(menuFont, "M", 1);
1479 for (i = 0; i < menuPtr->numEntries; i++) {
1480 mePtr = menuPtr->entries[i];
1481 if (mePtr->fontPtr == NULL) {
1483 fmPtr = &menuMetrics;
1485 tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr);
1486 Tk_GetFontMetrics(tkfont, &entryMetrics);
1487 fmPtr = &entryMetrics;
1490 if ((i > 0) && mePtr->columnBreak) {
1491 if (accelWidth != 0) {
1492 labelWidth += accelSpace;
1494 for (j = lastColumnBreak; j < i; j++) {
1495 menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1496 menuPtr->entries[j]->labelWidth = labelWidth;
1497 menuPtr->entries[j]->width = indicatorSpace + labelWidth
1498 + accelWidth + 2 * activeBorderWidth;
1499 menuPtr->entries[j]->x = x;
1500 menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
1502 x += indicatorSpace + labelWidth + accelWidth
1503 + 2 * activeBorderWidth;
1505 indicatorSpace = labelWidth = accelWidth = 0;
1506 lastColumnBreak = i;
1510 if (mePtr->type == SEPARATOR_ENTRY) {
1511 GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont,
1512 fmPtr, &width, &height);
1513 mePtr->height = height;
1514 } else if (mePtr->type == TEAROFF_ENTRY) {
1515 GetTearoffEntryGeometry(menuPtr, mePtr, tkfont,
1516 fmPtr, &width, &height);
1517 mePtr->height = height;
1522 * For each entry, compute the height required by that
1523 * particular entry, plus three widths: the width of the
1524 * label, the width to allow for an indicator to be displayed
1525 * to the left of the label (if any), and the width of the
1526 * accelerator to be displayed to the right of the label
1527 * (if any). These sizes depend, of course, on the type
1531 GetMenuLabelGeometry(mePtr, tkfont, fmPtr, &width,
1533 mePtr->height = height;
1534 if (!mePtr->hideMargin) {
1535 width += MENU_MARGIN_WIDTH;
1537 if (width > labelWidth) {
1541 GetMenuAccelGeometry(menuPtr, mePtr, tkfont,
1542 fmPtr, &width, &height);
1543 if (height > mePtr->height) {
1544 mePtr->height = height;
1546 if (!mePtr->hideMargin) {
1547 width += MENU_MARGIN_WIDTH;
1549 if (width > accelWidth) {
1553 GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont,
1554 fmPtr, &width, &height);
1555 if (height > mePtr->height) {
1556 mePtr->height = height;
1558 if (!mePtr->hideMargin) {
1559 width += MENU_MARGIN_WIDTH;
1561 if (width > indicatorSpace) {
1562 indicatorSpace = width;
1565 mePtr->height += 2 * activeBorderWidth + MENU_DIVIDER_HEIGHT;
1569 if (y > windowHeight) {
1574 if (accelWidth != 0) {
1575 labelWidth += accelSpace;
1577 for (j = lastColumnBreak; j < menuPtr->numEntries; j++) {
1578 menuPtr->entries[j]->indicatorSpace = indicatorSpace;
1579 menuPtr->entries[j]->labelWidth = labelWidth;
1580 menuPtr->entries[j]->width = indicatorSpace + labelWidth
1581 + accelWidth + 2 * activeBorderWidth;
1582 menuPtr->entries[j]->x = x;
1583 menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
1585 windowWidth = x + indicatorSpace + labelWidth + accelWidth
1586 + 2 * activeBorderWidth + 2 * borderWidth;
1589 windowHeight += borderWidth;
1592 * The X server doesn't like zero dimensions, so round up to at least
1593 * 1 (a zero-sized menu should never really occur, anyway).
1596 if (windowWidth <= 0) {
1599 if (windowHeight <= 0) {
1602 menuPtr->totalWidth = windowWidth;
1603 menuPtr->totalHeight = windowHeight;
1607 *----------------------------------------------------------------------
1609 * TkpMenuNotifyToplevelCreate --
1611 * This routine reconfigures the menu and the clones indicated by
1612 * menuName becuase a toplevel has been created and any system
1613 * menus need to be created. Not applicable to UNIX.
1619 * An idle handler is set up to do the reconfiguration.
1621 *----------------------------------------------------------------------
1625 TkpMenuNotifyToplevelCreate(interp, menuName)
1626 Tcl_Interp *interp; /* The interp the menu lives in. */
1627 char *menuName; /* The name of the menu to
1636 *----------------------------------------------------------------------
1640 * Does platform-specific initialization of menus.
1648 *----------------------------------------------------------------------
1661 *----------------------------------------------------------------------
1663 * TkpMenuThreadInit --
1665 * Does platform-specific initialization of thread-specific
1674 *----------------------------------------------------------------------