]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkmnbut.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tkmnbut.c
1 /*
2 * tkMenubutton.c --
3 *
4 * This module implements button-like widgets that are used
5 * to invoke pull-down menus.
6 *
7 * Copyright 1990 Regents of the University of California.
8 * Permission to use, copy, modify, and distribute this
9 * software and its documentation for any purpose and without
10 * fee is hereby granted, provided that the above copyright
11 * notice appear in all copies. The University of California
12 * makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without
14 * express or implied warranty.
15 */
16
17 #ifndef lint
18 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkMenubutton.c,v 1.33 92/08/21 16:21:47 ouster Exp $ SPRITE (Berkeley)";
19 #endif
20
21 #include "tkconfig.h"
22 #include "default.h"
23 #include "tkint.h"
24
25 /*
26 * A data structure of the following type is kept for each
27 * widget managed by this file:
28 */
29
30 typedef struct {
31 Tk_Window tkwin; /* Window that embodies the widget. NULL
32 * means that the window has been destroyed
33 * but the data structures haven't yet been
34 * cleaned up.*/
35 Tcl_Interp *interp; /* Interpreter associated with menu button. */
36 char *menuName; /* Name of menu associated with widget (used
37 * to generate post and unpost commands).
38 * Malloc-ed. */
39 Tk_Uid varName; /* Name of variable associated with collection
40 * of menu bars: used to allow scanning of
41 * menus. Also used as identifier for
42 * menu group. */
43
44 /*
45 * Information about what's displayed in the menu button:
46 */
47
48 char *text; /* Text to display in button (malloc'ed)
49 * or NULL. */
50 int textLength; /* # of characters in text. */
51 int underline; /* Index of character to underline. */
52 char *textVarName; /* Name of variable (malloc'ed) or NULL.
53 * If non-NULL, button displays the contents
54 * of this variable. */
55 Pixmap bitmap; /* Bitmap to display or None. If not None
56 * then text and textVar and underline
57 * are ignored. */
58
59 /*
60 * Information used when displaying widget:
61 */
62
63 Tk_Uid state; /* State of button for display purposes:
64 * normal, active, or disabled. */
65 Tk_3DBorder normalBorder; /* Structure used to draw 3-D
66 * border and background when window
67 * isn't active. NULL means no such
68 * border exists. */
69 Tk_3DBorder activeBorder; /* Structure used to draw 3-D
70 * border and background when window
71 * is active. NULL means no such
72 * border exists. */
73 int borderWidth; /* Width of border. */
74 int relief; /* 3-d effect: TK_RELIEF_RAISED, etc. */
75 XFontStruct *fontPtr; /* Information about text font, or NULL. */
76 XColor *normalFg; /* Foreground color in normal mode. */
77 XColor *activeFg; /* Foreground color in active mode. NULL
78 * means use normalFg instead. */
79 XColor *disabledFg; /* Foreground color when disabled. NULL
80 * means use normalFg with a 50% stipple
81 * instead. */
82 GC normalTextGC; /* GC for drawing text in normal mode. */
83 GC activeTextGC; /* GC for drawing text in active mode (NULL
84 * means use normalTextGC). */
85 Pixmap gray; /* Pixmap for displaying disabled text/icon if
86 * disabledFg is NULL. */
87 GC disabledGC; /* Used to produce disabled effect. If
88 * disabledFg isn't NULL, this GC is used to
89 * draw button text or icon. Otherwise
90 * text or icon is drawn with normalGC and
91 * this GC is used to stipple background
92 * across it. */
93 int leftBearing; /* Amount text sticks left from its origin,
94 * in pixels. */
95 int rightBearing; /* Amount text sticks right from its origin. */
96 int width, height; /* If > 0, these specify dimensions to request
97 * for window, in characters for text and in
98 * pixels for bitmaps. In this case the actual
99 * size of the text string or bitmap is
100 * ignored in computing desired window size. */
101 int padX, padY; /* Extra space around text or bitmap (pixels
102 * on each side). */
103 Tk_Anchor anchor; /* Where text/bitmap should be displayed
104 * inside window region. */
105
106 /*
107 * Miscellaneous information:
108 */
109
110 Cursor cursor; /* Current cursor for window, or None. */
111 int flags; /* Various flags; see below for
112 * definitions. */
113 } MenuButton;
114
115 /*
116 * Flag bits for buttons:
117 *
118 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
119 * has already been queued to redraw
120 * this window.
121 * POSTED: Non-zero means that the menu associated
122 * with this button has been posted (typically
123 * because of an active button press).
124 */
125
126 #define REDRAW_PENDING 1
127 #define POSTED 2
128
129 /*
130 * Information used for parsing configuration specs:
131 */
132
133 static Tk_ConfigSpec configSpecs[] = {
134 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
135 DEF_MENUBUTTON_ACTIVE_BG_COLOR, Tk_Offset(MenuButton, activeBorder),
136 TK_CONFIG_COLOR_ONLY},
137 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
138 DEF_MENUBUTTON_ACTIVE_BG_MONO, Tk_Offset(MenuButton, activeBorder),
139 TK_CONFIG_MONO_ONLY},
140 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
141 DEF_MENUBUTTON_ACTIVE_FG_COLOR, Tk_Offset(MenuButton, activeFg),
142 TK_CONFIG_COLOR_ONLY},
143 {TK_CONFIG_COLOR, "-activeforeground", "activeForeground", "Background",
144 DEF_MENUBUTTON_ACTIVE_FG_MONO, Tk_Offset(MenuButton, activeFg),
145 TK_CONFIG_MONO_ONLY},
146 {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
147 DEF_MENUBUTTON_ANCHOR, Tk_Offset(MenuButton, anchor), 0},
148 {TK_CONFIG_BORDER, "-background", "background", "Background",
149 DEF_MENUBUTTON_BG_COLOR, Tk_Offset(MenuButton, normalBorder),
150 TK_CONFIG_COLOR_ONLY},
151 {TK_CONFIG_BORDER, "-background", "background", "Background",
152 DEF_MENUBUTTON_BG_MONO, Tk_Offset(MenuButton, normalBorder),
153 TK_CONFIG_MONO_ONLY},
154 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
155 (char *) NULL, 0, 0},
156 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
157 (char *) NULL, 0, 0},
158 #if defined(USE_XPM3)
159 {TK_CONFIG_PIXMAP, "-bitmap", "bitmap", "Bitmap",
160 DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
161 TK_CONFIG_NULL_OK},
162 #else
163 {TK_CONFIG_BITMAP, "-bitmap", "bitmap", "Bitmap",
164 DEF_MENUBUTTON_BITMAP, Tk_Offset(MenuButton, bitmap),
165 TK_CONFIG_NULL_OK},
166 #endif
167 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
168 DEF_MENUBUTTON_BORDER_WIDTH, Tk_Offset(MenuButton, borderWidth), 0},
169 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
170 DEF_MENUBUTTON_CURSOR, Tk_Offset(MenuButton, cursor),
171 TK_CONFIG_NULL_OK},
172 {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
173 "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR,
174 Tk_Offset(MenuButton, disabledFg),
175 TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
176 {TK_CONFIG_COLOR, "-disabledforeground", "disabledForeground",
177 "DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_MONO,
178 Tk_Offset(MenuButton, disabledFg),
179 TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
180 {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
181 (char *) NULL, 0, 0},
182 {TK_CONFIG_FONT, "-font", "font", "Font",
183 DEF_MENUBUTTON_FONT, Tk_Offset(MenuButton, fontPtr), 0},
184 {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
185 DEF_MENUBUTTON_FG, Tk_Offset(MenuButton, normalFg), 0},
186 {TK_CONFIG_INT, "-height", "height", "Height",
187 DEF_MENUBUTTON_HEIGHT, Tk_Offset(MenuButton, height), 0},
188 {TK_CONFIG_STRING, "-menu", "menu", "Menu",
189 DEF_MENUBUTTON_MENU, Tk_Offset(MenuButton, menuName), 0},
190 {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
191 DEF_MENUBUTTON_PADX, Tk_Offset(MenuButton, padX), 0},
192 {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
193 DEF_MENUBUTTON_PADY, Tk_Offset(MenuButton, padY), 0},
194 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
195 DEF_MENUBUTTON_RELIEF, Tk_Offset(MenuButton, relief), 0},
196 {TK_CONFIG_UID, "-state", "state", "State",
197 DEF_MENUBUTTON_STATE, Tk_Offset(MenuButton, state), 0},
198 {TK_CONFIG_STRING, "-text", "text", "Text",
199 DEF_MENUBUTTON_TEXT, Tk_Offset(MenuButton, text), 0},
200 {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
201 DEF_MENUBUTTON_TEXT_VARIABLE, Tk_Offset(MenuButton, textVarName),
202 TK_CONFIG_NULL_OK},
203 {TK_CONFIG_INT, "-underline", "underline", "Underline",
204 DEF_MENUBUTTON_UNDERLINE, Tk_Offset(MenuButton, underline), 0},
205 {TK_CONFIG_UID, "-variable", "variable", "Variable",
206 DEF_MENUBUTTON_VARIABLE, Tk_Offset(MenuButton, varName), 0},
207 {TK_CONFIG_INT, "-width", "width", "Width",
208 DEF_MENUBUTTON_WIDTH, Tk_Offset(MenuButton, width), 0},
209 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
210 (char *) NULL, 0, 0}
211 };
212
213 /*
214 * Forward declarations for procedures defined later in this file:
215 */
216
217 static void ComputeMenuButtonGeometry _ANSI_ARGS_((
218 MenuButton *mbPtr));
219 static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData,
220 XEvent *eventPtr));
221 static char * MenuButtonTextVarProc _ANSI_ARGS_((
222 ClientData clientData, Tcl_Interp *interp,
223 char *name1, char *name2, int flags));
224 static char * MenuButtonVarProc _ANSI_ARGS_((ClientData clientData,
225 Tcl_Interp *interp, char *name1, char *name2,
226 int flags));
227 static int MenuButtonWidgetCmd _ANSI_ARGS_((ClientData clientData,
228 Tcl_Interp *interp, int argc, char **argv));
229 static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp,
230 MenuButton *mbPtr, int argc, char **argv,
231 int flags));
232 static void DestroyMenuButton _ANSI_ARGS_((ClientData clientData));
233 static void DisplayMenuButton _ANSI_ARGS_((ClientData clientData));
234 \f
235 /*
236 *--------------------------------------------------------------
237 *
238 * Tk_MenubuttonCmd --
239 *
240 * This procedure is invoked to process the "button", "label",
241 * "radiobutton", and "checkbutton" Tcl commands. See the
242 * user documentation for details on what it does.
243 *
244 * Results:
245 * A standard Tcl result.
246 *
247 * Side effects:
248 * See the user documentation.
249 *
250 *--------------------------------------------------------------
251 */
252
253 int
254 Tk_MenubuttonCmd (
255 ClientData clientData, /* Main window associated with
256 * interpreter. */
257 Tcl_Interp *interp, /* Current interpreter. */
258 int argc, /* Number of arguments. */
259 char **argv /* Argument strings. */
260 )
261 {
262 register MenuButton *mbPtr;
263 Tk_Window tkwin = (Tk_Window) clientData;
264 Tk_Window new;
265
266 if (argc < 2) {
267 Tcl_AppendResult(interp, "wrong # args: should be \"",
268 argv[0], " pathName ?options?\"", (char *) NULL);
269 return TCL_ERROR;
270 }
271
272 /*
273 * Create the new window.
274 */
275
276 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
277 if (new == NULL) {
278 return TCL_ERROR;
279 }
280
281 /*
282 * Initialize the data structure for the button.
283 */
284
285 mbPtr = (MenuButton *) ckalloc(sizeof(MenuButton));
286 mbPtr->tkwin = new;
287 mbPtr->interp = interp;
288 mbPtr->menuName = NULL;
289 mbPtr->varName = NULL;
290 mbPtr->text = NULL;
291 mbPtr->underline = -1;
292 mbPtr->textVarName = NULL;
293 mbPtr->bitmap = None;
294 mbPtr->state = tkNormalUid;
295 mbPtr->normalBorder = NULL;
296 mbPtr->activeBorder = NULL;
297 mbPtr->borderWidth = 0;
298 mbPtr->relief = TK_RELIEF_FLAT;
299 mbPtr->fontPtr = NULL;
300 mbPtr->normalFg = NULL;
301 mbPtr->activeFg = NULL;
302 mbPtr->disabledFg = NULL;
303 mbPtr->normalTextGC = NULL;
304 mbPtr->activeTextGC = NULL;
305 mbPtr->gray = None;
306 mbPtr->disabledGC = NULL;
307 mbPtr->cursor = None;
308 mbPtr->flags = 0;
309
310 Tk_SetClass(mbPtr->tkwin, "Menubutton");
311 Tk_CreateEventHandler(mbPtr->tkwin, ExposureMask|StructureNotifyMask,
312 MenuButtonEventProc, (ClientData) mbPtr);
313 Tcl_CreateCommand(interp, Tk_PathName(mbPtr->tkwin), MenuButtonWidgetCmd,
314 (ClientData) mbPtr, (void (*)(int *)) NULL);
315 if (ConfigureMenuButton(interp, mbPtr, argc-2, argv+2, 0) != TCL_OK) {
316 Tk_DestroyWindow(mbPtr->tkwin);
317 return TCL_ERROR;
318 }
319
320 interp->result = Tk_PathName(mbPtr->tkwin);
321 return TCL_OK;
322 }
323 \f
324 /*
325 *--------------------------------------------------------------
326 *
327 * MenuButtonWidgetCmd --
328 *
329 * This procedure is invoked to process the Tcl command
330 * that corresponds to a widget managed by this module.
331 * See the user documentation for details on what it does.
332 *
333 * Results:
334 * A standard Tcl result.
335 *
336 * Side effects:
337 * See the user documentation.
338 *
339 *--------------------------------------------------------------
340 */
341
342 static int
343 MenuButtonWidgetCmd (
344 ClientData clientData, /* Information about button widget. */
345 Tcl_Interp *interp, /* Current interpreter. */
346 int argc, /* Number of arguments. */
347 char **argv /* Argument strings. */
348 )
349 {
350 register MenuButton *mbPtr = (MenuButton *) clientData;
351 int result = TCL_OK;
352 int length;
353 char c;
354
355 if (argc < 2) {
356 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
357 " option ?arg arg ...?\"", (char *) NULL);
358 return TCL_ERROR;
359 }
360 Tk_Preserve((ClientData) mbPtr);
361 c = argv[1][0];
362 length = strlen(argv[1]);
363 if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
364 if (argc > 2) {
365 Tcl_AppendResult(interp, "wrong # args: should be \"",
366 argv[0], " activate\"", (char *) NULL);
367 goto error;
368 }
369 if (mbPtr->state != tkDisabledUid) {
370 mbPtr->state = tkActiveUid;
371 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
372 goto redisplay;
373 }
374 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
375 if (argc == 2) {
376 result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
377 (char *) mbPtr, (char *) NULL, 0);
378 } else if (argc == 3) {
379 result = Tk_ConfigureInfo(interp, mbPtr->tkwin, configSpecs,
380 (char *) mbPtr, argv[2], 0);
381 } else {
382 result = ConfigureMenuButton(interp, mbPtr, argc-2, argv+2,
383 TK_CONFIG_ARGV_ONLY);
384 }
385 } else if ((c == 'd') && (strncmp(argv[1], "deactivate", length) == 0)) {
386 if (argc > 2) {
387 Tcl_AppendResult(interp, "wrong # args: should be \"",
388 argv[0], " deactivate\"", (char *) NULL);
389 goto error;
390 }
391 if (mbPtr->state != tkDisabledUid) {
392 mbPtr->state = tkNormalUid;
393 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
394 goto redisplay;
395 }
396 } else if ((c == 'p') && (strncmp(argv[1], "post", length) == 0)) {
397 if (argc > 2) {
398 Tcl_AppendResult(interp, "wrong # args: should be \"",
399 argv[0], " post\"", (char *) NULL);
400 goto error;
401 }
402 if ((mbPtr->flags & POSTED) || (mbPtr->menuName == NULL)
403 || (mbPtr->state == tkDisabledUid)) {
404 goto done;
405 }
406
407 /*
408 * Store the name of the posted menu into the associated variable.
409 * This will cause any other menu posted via that variable to
410 * unpost itself and will cause this menu to post itself.
411 */
412
413 Tcl_SetVar(interp, mbPtr->varName, Tk_PathName(mbPtr->tkwin),
414 TCL_GLOBAL_ONLY);
415 } else if ((c == 'u') && (strncmp(argv[1], "unpost", length) == 0)) {
416 if (argc > 2) {
417 Tcl_AppendResult(interp, "wrong # args: should be \"",
418 argv[0], " unpost\"", (char *) NULL);
419 goto error;
420 }
421
422 /*
423 * The one-liner below looks simple, but it isn't. This code
424 * does the right thing even if this menu isn't posted anymore,
425 * but some other variable associated with the same variable
426 * is posted instead: it unposts whatever is posted. This
427 * approach is necessary because at present ButtonRelease
428 * events go to the menu button where the mouse button was
429 * first pressed; this may not be the same menu button that's
430 * currently active.
431 */
432 Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
433 } else {
434 Tcl_AppendResult(interp, "bad option \"", argv[1],
435 "\": must be activate, configure, deactivate, ",
436 "post, or unpost", (char *) NULL);
437 goto error;
438 }
439 done:
440 Tk_Release((ClientData) mbPtr);
441 return result;
442
443 redisplay:
444 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
445 Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
446 mbPtr->flags |= REDRAW_PENDING;
447 }
448 goto done;
449
450 error:
451 Tk_Release((ClientData) mbPtr);
452 return TCL_ERROR;
453 }
454 \f
455 /*
456 *----------------------------------------------------------------------
457 *
458 * DestroyMenuButton --
459 *
460 * This procedure is invoked to recycle all of the resources
461 * associated with a button widget. It is invoked as a
462 * when-idle handler in order to make sure that there is no
463 * other use of the button pending at the time of the deletion.
464 *
465 * Results:
466 * None.
467 *
468 * Side effects:
469 * Everything associated with the widget is freed up.
470 *
471 *----------------------------------------------------------------------
472 */
473
474 static void
475 DestroyMenuButton (
476 ClientData clientData /* Info about button widget. */
477 )
478 {
479 register MenuButton *mbPtr = (MenuButton *) clientData;
480 if (mbPtr->menuName != NULL) {
481 ckfree(mbPtr->menuName);
482 }
483 if (mbPtr->varName != NULL) {
484 Tcl_UntraceVar(mbPtr->interp, mbPtr->varName,
485 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
486 MenuButtonVarProc, (ClientData) mbPtr);
487 }
488 if (mbPtr->text != NULL) {
489 ckfree(mbPtr->text);
490 }
491 if (mbPtr->textVarName != NULL) {
492 Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName,
493 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
494 MenuButtonTextVarProc, (ClientData) mbPtr);
495 ckfree(mbPtr->textVarName);
496 }
497 if (mbPtr->bitmap != None) {
498 #if defined(USE_XPM3)
499 Tk_FreePixmap(mbPtr->bitmap);
500 #else
501 Tk_FreeBitmap(mbPtr->bitmap);
502 #endif
503 }
504 if (mbPtr->normalBorder != NULL) {
505 Tk_Free3DBorder(mbPtr->normalBorder);
506 }
507 if (mbPtr->activeBorder != NULL) {
508 Tk_Free3DBorder(mbPtr->activeBorder);
509 }
510 if (mbPtr->fontPtr != NULL) {
511 Tk_FreeFontStruct(mbPtr->fontPtr);
512 }
513 if (mbPtr->normalFg != NULL) {
514 Tk_FreeColor(mbPtr->normalFg);
515 }
516 if (mbPtr->activeFg != NULL) {
517 Tk_FreeColor(mbPtr->activeFg);
518 }
519 if (mbPtr->disabledFg != NULL) {
520 Tk_FreeColor(mbPtr->disabledFg);
521 }
522 if (mbPtr->normalTextGC != None) {
523 Tk_FreeGC(mbPtr->normalTextGC);
524 }
525 if (mbPtr->activeTextGC != None) {
526 Tk_FreeGC(mbPtr->activeTextGC);
527 }
528 if (mbPtr->gray != None) {
529 Tk_FreeBitmap(mbPtr->gray);
530 }
531 if (mbPtr->disabledGC != None) {
532 Tk_FreeGC(mbPtr->disabledGC);
533 }
534 if (mbPtr->cursor != None) {
535 Tk_FreeCursor(mbPtr->cursor);
536 }
537 ckfree((char *) mbPtr);
538 }
539 \f
540 /*
541 *----------------------------------------------------------------------
542 *
543 * ConfigureMenuButton --
544 *
545 * This procedure is called to process an argv/argc list, plus
546 * the Tk option database, in order to configure (or
547 * reconfigure) a menubutton widget.
548 *
549 * Results:
550 * The return value is a standard Tcl result. If TCL_ERROR is
551 * returned, then interp->result contains an error message.
552 *
553 * Side effects:
554 * Configuration information, such as text string, colors, font,
555 * etc. get set for mbPtr; old resources get freed, if there
556 * were any. The menubutton is redisplayed.
557 *
558 *----------------------------------------------------------------------
559 */
560
561 static int
562 ConfigureMenuButton (
563 Tcl_Interp *interp, /* Used for error reporting. */
564 register MenuButton *mbPtr, /* Information about widget; may or may
565 * not already have values for some fields. */
566 int argc, /* Number of valid entries in argv. */
567 char **argv, /* Arguments. */
568 int flags /* Flags to pass to Tk_ConfigureWidget. */
569 )
570 {
571 XGCValues gcValues;
572 GC newGC;
573 unsigned long mask;
574 int result;
575 Tk_Uid oldGroup;
576 char *value;
577
578 /*
579 * Eliminate any existing traces on variables monitored by the button.
580 */
581
582 if (mbPtr->varName != NULL) {
583 Tcl_UntraceVar(interp, mbPtr->varName,
584 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
585 MenuButtonVarProc, (ClientData) mbPtr);
586 }
587 if (mbPtr->textVarName != NULL) {
588 Tcl_UntraceVar(interp, mbPtr->textVarName,
589 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
590 MenuButtonTextVarProc, (ClientData) mbPtr);
591 }
592
593 oldGroup = mbPtr->varName;
594 result = Tk_ConfigureWidget(interp, mbPtr->tkwin, configSpecs,
595 argc, argv, (char *) mbPtr, flags);
596 if (oldGroup != mbPtr->varName) {
597 Tk_UnshareEvents(mbPtr->tkwin, oldGroup);
598 Tk_ShareEvents(mbPtr->tkwin, mbPtr->varName);
599 }
600 if (result != TCL_OK) {
601 return TCL_ERROR;
602 }
603
604 /*
605 * A few options need special processing, such as setting the
606 * background from a 3-D border, or filling in complicated
607 * defaults that couldn't be specified to Tk_ConfigureWidget.
608 */
609
610 if (mbPtr->state == tkActiveUid) {
611 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder);
612 } else {
613 Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder);
614 if ((mbPtr->state != tkNormalUid) && (mbPtr->state != tkDisabledUid)) {
615 Tcl_AppendResult(interp, "bad state value \"", mbPtr->state,
616 "\": must be normal, active, or disabled", (char *) NULL);
617 mbPtr->state = tkNormalUid;
618 return TCL_ERROR;
619 }
620 }
621
622 gcValues.font = mbPtr->fontPtr->fid;
623 gcValues.foreground = mbPtr->normalFg->pixel;
624 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
625
626 /*
627 * Note: GraphicsExpose events are disabled in GC's because they're
628 * used to copy stuff from an off-screen pixmap onto the screen (we know
629 * that there's no problem with obscured areas).
630 */
631
632 gcValues.graphics_exposures = False;
633 newGC = Tk_GetGC(mbPtr->tkwin,
634 GCForeground|GCBackground|GCFont|GCGraphicsExposures, &gcValues);
635 if (mbPtr->normalTextGC != None) {
636 Tk_FreeGC(mbPtr->normalTextGC);
637 }
638 mbPtr->normalTextGC = newGC;
639
640 gcValues.font = mbPtr->fontPtr->fid;
641 gcValues.foreground = mbPtr->activeFg->pixel;
642 gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel;
643 newGC = Tk_GetGC(mbPtr->tkwin, GCForeground|GCBackground|GCFont,
644 &gcValues);
645 if (mbPtr->activeTextGC != None) {
646 Tk_FreeGC(mbPtr->activeTextGC);
647 }
648 mbPtr->activeTextGC = newGC;
649
650 gcValues.font = mbPtr->fontPtr->fid;
651 gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel;
652 if (mbPtr->disabledFg != NULL) {
653 gcValues.foreground = mbPtr->disabledFg->pixel;
654 mask = GCForeground|GCBackground|GCFont;
655 } else {
656 gcValues.foreground = gcValues.background;
657 if (mbPtr->gray == None) {
658 mbPtr->gray = Tk_GetBitmap(interp, mbPtr->tkwin,
659 Tk_GetUid("gray50"));
660 if (mbPtr->gray == None) {
661 return TCL_ERROR;
662 }
663 }
664 gcValues.fill_style = FillStippled;
665 gcValues.stipple = mbPtr->gray;
666 mask = GCForeground|GCFillStyle|GCStipple;
667 }
668 newGC = Tk_GetGC(mbPtr->tkwin, mask, &gcValues);
669 if (mbPtr->disabledGC != None) {
670 Tk_FreeGC(mbPtr->disabledGC);
671 }
672 mbPtr->disabledGC = newGC;
673
674 if (mbPtr->padX < 0) {
675 mbPtr->padX = 0;
676 }
677 if (mbPtr->padY < 0) {
678 mbPtr->padY = 0;
679 }
680
681 /*
682 * Set up a trace on the menu button's variable, then initialize
683 * the variable if it doesn't already exist, so that it can be
684 * accessed immediately from Tcl code without fear of
685 * "nonexistent variable" errors.
686 */
687
688 value = Tcl_GetVar(interp, mbPtr->varName, TCL_GLOBAL_ONLY);
689 if (value == NULL) {
690 Tcl_SetVar(interp, mbPtr->varName, "", TCL_GLOBAL_ONLY);
691 }
692 Tcl_TraceVar(interp, mbPtr->varName,
693 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
694 MenuButtonVarProc, (ClientData) mbPtr);
695
696 /*
697 * Set up a trace on the variable that determines what's displayed
698 * in the menu button, if such a trace has been requested.
699 */
700
701 if ((mbPtr->bitmap == None) && (mbPtr->textVarName != NULL)) {
702 char *value;
703
704 value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY);
705 if (value == NULL) {
706 Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text,
707 TCL_GLOBAL_ONLY);
708 } else {
709 if (mbPtr->text != NULL) {
710 ckfree(mbPtr->text);
711 }
712 mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
713 strcpy(mbPtr->text, value);
714 }
715 Tcl_TraceVar(interp, mbPtr->textVarName,
716 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
717 MenuButtonTextVarProc, (ClientData) mbPtr);
718 }
719
720 /*
721 * Recompute the geometry for the button.
722 */
723
724 ComputeMenuButtonGeometry(mbPtr);
725
726 /*
727 * Lastly, arrange for the button to be redisplayed.
728 */
729
730 if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) {
731 Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
732 mbPtr->flags |= REDRAW_PENDING;
733 }
734
735 return TCL_OK;
736 }
737 \f
738 /*
739 *----------------------------------------------------------------------
740 *
741 * DisplayMenuButton --
742 *
743 * This procedure is invoked to display a menubutton widget.
744 *
745 * Results:
746 * None.
747 *
748 * Side effects:
749 * Commands are output to X to display the menubutton in its
750 * current mode.
751 *
752 *----------------------------------------------------------------------
753 */
754
755 static void
756 DisplayMenuButton (
757 ClientData clientData /* Information about widget. */
758 )
759 {
760 register MenuButton *mbPtr = (MenuButton *) clientData;
761 GC gc;
762 Tk_3DBorder border;
763 Pixmap pixmap;
764 int x = 0; /* Initialization needed only to stop
765 * compiler warning. */
766 int y;
767 register Tk_Window tkwin = mbPtr->tkwin;
768
769 mbPtr->flags &= ~REDRAW_PENDING;
770 if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
771 return;
772 }
773
774 if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
775 gc = mbPtr->disabledGC;
776 border = mbPtr->normalBorder;
777 } else if (mbPtr->state == tkActiveUid) {
778 gc = mbPtr->activeTextGC;
779 border = mbPtr->activeBorder;
780 } else {
781 gc = mbPtr->normalTextGC;
782 border = mbPtr->normalBorder;
783 }
784
785 /*
786 * In order to avoid screen flashes, this procedure redraws
787 * the menu button in a pixmap, then copies the pixmap to the
788 * screen in a single operation. This means that there's no
789 * point in time where the on-sreen image has been cleared.
790 */
791
792 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
793 Tk_Width(tkwin), Tk_Height(tkwin),
794 Tk_DefaultDepth(Tk_Screen(tkwin)));
795 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, border,
796 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
797
798 /*
799 * Display bitmap or text for button.
800 */
801
802 if (mbPtr->bitmap != None) {
803 unsigned int width, height;
804
805 #if defined(USE_XPM3)
806 Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height);
807 #else
808 Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
809 #endif
810 switch (mbPtr->anchor) {
811 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
812 x += mbPtr->borderWidth + mbPtr->padX;
813 break;
814 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
815 x += (Tk_Width(tkwin) - width)/2;
816 break;
817 default:
818 x += Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
819 - width;
820 break;
821 }
822 switch (mbPtr->anchor) {
823 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
824 y = mbPtr->borderWidth + mbPtr->padY;
825 break;
826 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
827 y = (Tk_Height(tkwin) - height)/2;
828 break;
829 default:
830 y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
831 - height;
832 break;
833 }
834 #if defined(USE_XPM3)
835 XCopyArea(Tk_Display(tkwin), mbPtr->bitmap, pixmap,
836 gc, 0, 0, width, height, x, y);
837 #else
838 XCopyPlane(Tk_Display(tkwin), mbPtr->bitmap, pixmap,
839 gc, 0, 0, width, height, x, y, 1);
840 #endif
841 } else {
842 switch (mbPtr->anchor) {
843 case TK_ANCHOR_NW: case TK_ANCHOR_W: case TK_ANCHOR_SW:
844 x = mbPtr->borderWidth + mbPtr->padX + mbPtr->leftBearing;
845 break;
846 case TK_ANCHOR_N: case TK_ANCHOR_CENTER: case TK_ANCHOR_S:
847 x = (Tk_Width(tkwin) + mbPtr->leftBearing
848 - mbPtr->rightBearing)/2;
849 break;
850 default:
851 x = Tk_Width(tkwin) - mbPtr->borderWidth - mbPtr->padX
852 - mbPtr->rightBearing;
853 break;
854 }
855 switch (mbPtr->anchor) {
856 case TK_ANCHOR_NW: case TK_ANCHOR_N: case TK_ANCHOR_NE:
857 y = mbPtr->borderWidth + mbPtr->fontPtr->ascent
858 + mbPtr->padY;
859 break;
860 case TK_ANCHOR_W: case TK_ANCHOR_CENTER: case TK_ANCHOR_E:
861 y = (Tk_Height(tkwin) + mbPtr->fontPtr->ascent
862 - mbPtr->fontPtr->descent)/2;
863 break;
864 default:
865 y = Tk_Height(tkwin) - mbPtr->borderWidth - mbPtr->padY
866 - mbPtr->fontPtr->descent;
867 break;
868 }
869 XDrawString(Tk_Display(tkwin), pixmap, gc, x, y, mbPtr->text,
870 mbPtr->textLength);
871 if (mbPtr->underline >= 0) {
872 TkUnderlineChars(Tk_Display(tkwin), pixmap, gc, mbPtr->fontPtr,
873 mbPtr->text, x, y, TK_NEWLINES_NOT_SPECIAL,
874 mbPtr->underline, mbPtr->underline);
875 }
876 }
877
878 /*
879 * If the menu button is disabled with a stipple rather than a special
880 * foreground color, generate the stippled effect.
881 */
882
883 if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg == NULL)) {
884 XFillRectangle(Tk_Display(tkwin), pixmap, mbPtr->disabledGC,
885 mbPtr->borderWidth, mbPtr->borderWidth,
886 (unsigned) (Tk_Width(tkwin) - 2*mbPtr->borderWidth),
887 (unsigned) (Tk_Height(tkwin) - 2*mbPtr->borderWidth));
888 }
889
890 /*
891 * Draw the border last. This way, if the menu button's contents
892 * overflow onto the border they'll be covered up by the border.
893 */
894
895 if (mbPtr->relief != TK_RELIEF_FLAT) {
896 Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap, border,
897 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
898 mbPtr->borderWidth, mbPtr->relief);
899 }
900
901 /*
902 * Copy the information from the off-screen pixmap onto the screen,
903 * then delete the pixmap.
904 */
905
906 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
907 mbPtr->normalTextGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
908 XFreePixmap(Tk_Display(tkwin), pixmap);
909 }
910 \f
911 /*
912 *--------------------------------------------------------------
913 *
914 * MenuButtonEventProc --
915 *
916 * This procedure is invoked by the Tk dispatcher for various
917 * events on buttons.
918 *
919 * Results:
920 * None.
921 *
922 * Side effects:
923 * When the window gets deleted, internal structures get
924 * cleaned up. When it gets exposed, it is redisplayed.
925 *
926 *--------------------------------------------------------------
927 */
928
929 static void
930 MenuButtonEventProc (
931 ClientData clientData, /* Information about window. */
932 XEvent *eventPtr /* Information about event. */
933 )
934 {
935 MenuButton *mbPtr = (MenuButton *) clientData;
936 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
937 if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) {
938 Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
939 mbPtr->flags |= REDRAW_PENDING;
940 }
941 } else if (eventPtr->type == DestroyNotify) {
942 Tcl_DeleteCommand(mbPtr->interp, Tk_PathName(mbPtr->tkwin));
943
944 /*
945 * Careful! Must delete the event-sharing information here
946 * rather than in DestroyMenuButton. By the time that procedure
947 * is called the tkwin may have been reused, resulting in some
948 * other window accidentally being cut off from shared events.
949 */
950
951 Tk_UnshareEvents(mbPtr->tkwin, mbPtr->varName);
952 mbPtr->tkwin = NULL;
953 if (mbPtr->flags & REDRAW_PENDING) {
954 Tk_CancelIdleCall(DisplayMenuButton, (ClientData) mbPtr);
955 }
956 Tk_EventuallyFree((ClientData) mbPtr, DestroyMenuButton);
957 }
958 }
959 \f
960 /*
961 *----------------------------------------------------------------------
962 *
963 * ComputeMenuButtonGeometry --
964 *
965 * After changes in a menu button's text or bitmap, this procedure
966 * recomputes the menu button's geometry and passes this information
967 * along to the geometry manager for the window.
968 *
969 * Results:
970 * None.
971 *
972 * Side effects:
973 * The menu button's window may change size.
974 *
975 *----------------------------------------------------------------------
976 */
977
978 static void
979 ComputeMenuButtonGeometry (
980 register MenuButton *mbPtr /* Widget record for menu button. */
981 )
982 {
983 XCharStruct bbox;
984 int dummy;
985 unsigned int width, height;
986
987 if (mbPtr->bitmap != None) {
988 #if defined(USE_XPM3)
989 Tk_SizeOfPixmap(mbPtr->bitmap, &width, &height);
990 #else
991 Tk_SizeOfBitmap(mbPtr->bitmap, &width, &height);
992 #endif
993 if (mbPtr->width > 0) {
994 width = mbPtr->width;
995 }
996 if (mbPtr->height > 0) {
997 height = mbPtr->height;
998 }
999 } else {
1000 mbPtr->textLength = strlen(mbPtr->text);
1001 XTextExtents(mbPtr->fontPtr, mbPtr->text, mbPtr->textLength,
1002 &dummy, &dummy, &dummy, &bbox);
1003 mbPtr->leftBearing = bbox.lbearing;
1004 mbPtr->rightBearing = bbox.rbearing;
1005 width = bbox.lbearing + bbox.rbearing;
1006 height = mbPtr->fontPtr->ascent + mbPtr->fontPtr->descent;
1007 if (mbPtr->width > 0) {
1008 width = mbPtr->width * XTextWidth(mbPtr->fontPtr, "0", 1);
1009 }
1010 if (mbPtr->height > 0) {
1011 height *= mbPtr->height;
1012 }
1013 }
1014
1015 width += 2*mbPtr->padX;
1016 height += 2*mbPtr->padY;
1017 Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->borderWidth),
1018 (int) (height + 2*mbPtr->borderWidth));
1019 Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->borderWidth);
1020 }
1021 \f
1022 /*
1023 *--------------------------------------------------------------
1024 *
1025 * MenuButtonVarProc --
1026 *
1027 * This procedure is invoked when someone changes the
1028 * state variable associated with a menubutton. This causes
1029 * the posted/unposted state of the menu to change if needed
1030 * to match the variable's new value.
1031 *
1032 * Results:
1033 * NULL is always returned.
1034 *
1035 * Side effects:
1036 * The menu may be posted or unposted.
1037 *
1038 *--------------------------------------------------------------
1039 */
1040
1041 /* ARGSUSED */
1042 static char *
1043 MenuButtonVarProc (
1044 ClientData clientData, /* Information about button. */
1045 Tcl_Interp *interp, /* Interpreter containing variable. */
1046 char *name1, /* First part of variable's name. */
1047 char *name2, /* Second part of variable's name. */
1048 int flags /* Describes what's happening to variable. */
1049 )
1050 {
1051 register MenuButton *mbPtr = (MenuButton *) clientData;
1052 char *value;
1053 int newFlags;
1054
1055 /*
1056 * If the variable is being unset, then just re-establish the
1057 * trace unless the whole interpreter is going away. Also unpost
1058 * the menu.
1059 */
1060
1061 newFlags = mbPtr->flags;
1062 if (flags & TCL_TRACE_UNSETS) {
1063 newFlags &= ~POSTED;
1064 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1065 Tcl_TraceVar2(interp, name1, name2,
1066 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1067 MenuButtonVarProc, clientData);
1068 }
1069 } else {
1070
1071 /*
1072 * Use the value of the variable to update the posted status of
1073 * the menu.
1074 */
1075
1076 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1077 if (strcmp(value, Tk_PathName(mbPtr->tkwin)) == 0) {
1078 newFlags |= POSTED;
1079 } else {
1080 newFlags &= ~POSTED;
1081 }
1082 }
1083
1084 if ((mbPtr->menuName != NULL) && (newFlags != mbPtr->flags)) {
1085 mbPtr->flags = newFlags;
1086 if (newFlags & POSTED) {
1087 int x, y;
1088 char string[50];
1089
1090 /*
1091 * Post the menu just below the menu button.
1092 */
1093
1094 Tk_GetRootCoords(mbPtr->tkwin, &x, &y);
1095 y += Tk_Height(mbPtr->tkwin);
1096 sprintf(string, "%d %d ", x, y);
1097 if (Tcl_VarEval(interp, mbPtr->menuName, " post ", string,
1098 mbPtr->varName, (char *) NULL) != TCL_OK) {
1099 TkBindError(interp);
1100 }
1101 } else {
1102 if (Tcl_VarEval(interp, mbPtr->menuName, " unpost",
1103 (char *) NULL) != TCL_OK) {
1104 TkBindError(interp);
1105 }
1106 }
1107 }
1108 return (char *) NULL;
1109 }
1110 \f
1111 /*
1112 *--------------------------------------------------------------
1113 *
1114 * MenuButtonTextVarProc --
1115 *
1116 * This procedure is invoked when someone changes the variable
1117 * whose contents are to be displayed in a menu button.
1118 *
1119 * Results:
1120 * NULL is always returned.
1121 *
1122 * Side effects:
1123 * The text displayed in the menu button will change to match the
1124 * variable.
1125 *
1126 *--------------------------------------------------------------
1127 */
1128
1129 /* ARGSUSED */
1130 static char *
1131 MenuButtonTextVarProc (
1132 ClientData clientData, /* Information about button. */
1133 Tcl_Interp *interp, /* Interpreter containing variable. */
1134 char *name1, /* Name of variable. */
1135 char *name2, /* Second part of variable name. */
1136 int flags /* Information about what happened. */
1137 )
1138 {
1139 register MenuButton *mbPtr = (MenuButton *) clientData;
1140 char *value;
1141
1142 /*
1143 * If the variable is unset, then immediately recreate it unless
1144 * the whole interpreter is going away.
1145 */
1146
1147 if (flags & TCL_TRACE_UNSETS) {
1148 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1149 Tcl_SetVar2(interp, name1, name2, mbPtr->text,
1150 flags & TCL_GLOBAL_ONLY);
1151 Tcl_TraceVar2(interp, name1, name2,
1152 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1153 MenuButtonTextVarProc, clientData);
1154 }
1155 return (char *) NULL;
1156 }
1157
1158 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1159 if (value == NULL) {
1160 value = "";
1161 }
1162 if (mbPtr->text != NULL) {
1163 ckfree(mbPtr->text);
1164 }
1165 mbPtr->text = ckalloc((unsigned) (strlen(value) + 1));
1166 strcpy(mbPtr->text, value);
1167 ComputeMenuButtonGeometry(mbPtr);
1168
1169 if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin)
1170 && !(mbPtr->flags & REDRAW_PENDING)) {
1171 Tk_DoWhenIdle(DisplayMenuButton, (ClientData) mbPtr);
1172 mbPtr->flags |= REDRAW_PENDING;
1173 }
1174 return (char *) NULL;
1175 }
Impressum, Datenschutz