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