1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkMenubutton.c -- |
5 |
* |
6 |
* This module implements button-like widgets that are used |
7 |
* to invoke pull-down menus. |
8 |
* |
9 |
* Copyright (c) 1990-1994 The Regents of the University of California. |
10 |
* Copyright (c) 1994-1997 Sun Microsystems, Inc. |
11 |
* |
12 |
* See the file "license.terms" for information on usage and redistribution |
13 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
14 |
* |
15 |
* RCS: @(#) $Id: tkmenubutton.c,v 1.1.1.1 2001/06/13 05:05:54 dtashley Exp $ |
16 |
*/ |
17 |
|
18 |
#include "tkMenubutton.h" |
19 |
#include "tkPort.h" |
20 |
#include "default.h" |
21 |
|
22 |
/* |
23 |
* The following table defines the legal values for the -direction |
24 |
* option. It is used together with the "enum direction" declaration |
25 |
* in tkMenubutton.h. |
26 |
*/ |
27 |
|
28 |
static char *directionStrings[] = { |
29 |
"above", "below", "flush", "left", "right", (char *) NULL |
30 |
}; |
31 |
|
32 |
/* |
33 |
* The following table defines the legal values for the -state option. |
34 |
* It is used together with the "enum state" declaration in tkMenubutton.h. |
35 |
*/ |
36 |
|
37 |
static char *stateStrings[] = { |
38 |
"active", "disabled", "normal", (char *) NULL |
39 |
}; |
40 |
|
41 |
/* |
42 |
* Information used for parsing configuration specs: |
43 |
*/ |
44 |
|
45 |
static Tk_OptionSpec optionSpecs[] = { |
46 |
{TK_OPTION_BORDER, "-activebackground", "activeBackground", "Foreground", |
47 |
DEF_MENUBUTTON_ACTIVE_BG_COLOR, -1, |
48 |
Tk_Offset(TkMenuButton, activeBorder), 0, |
49 |
(ClientData) DEF_MENUBUTTON_ACTIVE_BG_MONO, 0}, |
50 |
{TK_OPTION_COLOR, "-activeforeground", "activeForeground", "Background", |
51 |
DEF_MENUBUTTON_ACTIVE_FG_COLOR, -1, |
52 |
Tk_Offset(TkMenuButton, activeFg), |
53 |
0, (ClientData) DEF_MENUBUTTON_ACTIVE_FG_MONO, 0}, |
54 |
{TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", |
55 |
DEF_MENUBUTTON_ANCHOR, -1, |
56 |
Tk_Offset(TkMenuButton, anchor), 0, 0, 0}, |
57 |
{TK_OPTION_BORDER, "-background", "background", "Background", |
58 |
DEF_MENUBUTTON_BG_COLOR, -1, Tk_Offset(TkMenuButton, normalBorder), |
59 |
0, (ClientData) DEF_MENUBUTTON_BG_MONO, 0}, |
60 |
{TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL, |
61 |
(char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0}, |
62 |
{TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL, |
63 |
(char *) NULL, 0, -1, 0, (ClientData) "-background", 0}, |
64 |
{TK_OPTION_BITMAP, "-bitmap", "bitmap", "Bitmap", |
65 |
DEF_MENUBUTTON_BITMAP, -1, Tk_Offset(TkMenuButton, bitmap), |
66 |
TK_OPTION_NULL_OK, 0, 0}, |
67 |
{TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", |
68 |
DEF_MENUBUTTON_BORDER_WIDTH, -1, |
69 |
Tk_Offset(TkMenuButton, borderWidth), 0, 0, 0}, |
70 |
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", |
71 |
DEF_MENUBUTTON_CURSOR, -1, Tk_Offset(TkMenuButton, cursor), |
72 |
TK_OPTION_NULL_OK, 0, 0}, |
73 |
{TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction", |
74 |
DEF_MENUBUTTON_DIRECTION, -1, Tk_Offset(TkMenuButton, direction), |
75 |
0, (ClientData) directionStrings, 0}, |
76 |
{TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", |
77 |
"DisabledForeground", DEF_MENUBUTTON_DISABLED_FG_COLOR, |
78 |
-1, Tk_Offset(TkMenuButton, disabledFg), TK_OPTION_NULL_OK, |
79 |
(ClientData) DEF_MENUBUTTON_DISABLED_FG_MONO, 0}, |
80 |
{TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL, |
81 |
(char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0}, |
82 |
{TK_OPTION_FONT, "-font", "font", "Font", |
83 |
DEF_MENUBUTTON_FONT, -1, Tk_Offset(TkMenuButton, tkfont), 0, 0, 0}, |
84 |
{TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", |
85 |
DEF_MENUBUTTON_FG, -1, Tk_Offset(TkMenuButton, normalFg), 0, 0, 0}, |
86 |
{TK_OPTION_STRING, "-height", "height", "Height", |
87 |
DEF_MENUBUTTON_HEIGHT, -1, Tk_Offset(TkMenuButton, heightString), |
88 |
0, 0, 0}, |
89 |
{TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", |
90 |
"HighlightBackground", DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR, |
91 |
-1, Tk_Offset(TkMenuButton, highlightBgColorPtr), 0, 0, 0}, |
92 |
{TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", |
93 |
DEF_MENUBUTTON_HIGHLIGHT, -1, |
94 |
Tk_Offset(TkMenuButton, highlightColorPtr), 0, 0, 0}, |
95 |
{TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", |
96 |
"HighlightThickness", DEF_MENUBUTTON_HIGHLIGHT_WIDTH, |
97 |
-1, Tk_Offset(TkMenuButton, highlightWidth), 0, 0, 0}, |
98 |
{TK_OPTION_STRING, "-image", "image", "Image", |
99 |
DEF_MENUBUTTON_IMAGE, -1, Tk_Offset(TkMenuButton, imageString), |
100 |
TK_OPTION_NULL_OK, 0, 0}, |
101 |
{TK_OPTION_BOOLEAN, "-indicatoron", "indicatorOn", "IndicatorOn", |
102 |
DEF_MENUBUTTON_INDICATOR, -1, Tk_Offset(TkMenuButton, indicatorOn), |
103 |
0, 0, 0}, |
104 |
{TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", |
105 |
DEF_BUTTON_JUSTIFY, -1, Tk_Offset(TkMenuButton, justify), 0, 0, 0}, |
106 |
{TK_OPTION_STRING, "-menu", "menu", "Menu", |
107 |
DEF_MENUBUTTON_MENU, -1, Tk_Offset(TkMenuButton, menuName), |
108 |
TK_OPTION_NULL_OK, 0, 0}, |
109 |
{TK_OPTION_PIXELS, "-padx", "padX", "Pad", |
110 |
DEF_MENUBUTTON_PADX, -1, Tk_Offset(TkMenuButton, padX), |
111 |
0, 0, 0}, |
112 |
{TK_OPTION_PIXELS, "-pady", "padY", "Pad", |
113 |
DEF_MENUBUTTON_PADY, -1, Tk_Offset(TkMenuButton, padY), |
114 |
0, 0, 0}, |
115 |
{TK_OPTION_RELIEF, "-relief", "relief", "Relief", |
116 |
DEF_MENUBUTTON_RELIEF, -1, Tk_Offset(TkMenuButton, relief), |
117 |
0, 0, 0}, |
118 |
{TK_OPTION_STRING_TABLE, "-state", "state", "State", |
119 |
DEF_MENUBUTTON_STATE, -1, Tk_Offset(TkMenuButton, state), |
120 |
0, (ClientData) stateStrings, 0}, |
121 |
{TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", |
122 |
DEF_MENUBUTTON_TAKE_FOCUS, -1, |
123 |
Tk_Offset(TkMenuButton, takeFocus), TK_OPTION_NULL_OK, 0, 0}, |
124 |
{TK_OPTION_STRING, "-text", "text", "Text", |
125 |
DEF_MENUBUTTON_TEXT, -1, Tk_Offset(TkMenuButton, text), 0, 0, 0}, |
126 |
{TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", |
127 |
DEF_MENUBUTTON_TEXT_VARIABLE, -1, |
128 |
Tk_Offset(TkMenuButton, textVarName), TK_OPTION_NULL_OK, 0, 0}, |
129 |
{TK_OPTION_INT, "-underline", "underline", "Underline", |
130 |
DEF_MENUBUTTON_UNDERLINE, -1, Tk_Offset(TkMenuButton, underline), |
131 |
0, 0, 0}, |
132 |
{TK_OPTION_STRING, "-width", "width", "Width", |
133 |
DEF_MENUBUTTON_WIDTH, -1, Tk_Offset(TkMenuButton, widthString), |
134 |
0, 0, 0}, |
135 |
{TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength", |
136 |
DEF_MENUBUTTON_WRAP_LENGTH, -1, Tk_Offset(TkMenuButton, wrapLength), |
137 |
0, 0, 0}, |
138 |
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, |
139 |
(char *) NULL, 0, 0} |
140 |
}; |
141 |
|
142 |
/* |
143 |
* The following tables define the menubutton widget commands and map the |
144 |
* indexes into the string tables into a single enumerated type used |
145 |
* to dispatch the scale widget command. |
146 |
*/ |
147 |
|
148 |
static char *commandNames[] = { |
149 |
"cget", "configure", (char *) NULL |
150 |
}; |
151 |
|
152 |
enum command { |
153 |
COMMAND_CGET, COMMAND_CONFIGURE |
154 |
}; |
155 |
|
156 |
/* |
157 |
* Forward declarations for procedures defined later in this file: |
158 |
*/ |
159 |
|
160 |
static void MenuButtonCmdDeletedProc _ANSI_ARGS_(( |
161 |
ClientData clientData)); |
162 |
static void MenuButtonEventProc _ANSI_ARGS_((ClientData clientData, |
163 |
XEvent *eventPtr)); |
164 |
static void MenuButtonImageProc _ANSI_ARGS_((ClientData clientData, |
165 |
int x, int y, int width, int height, int imgWidth, |
166 |
int imgHeight)); |
167 |
static char * MenuButtonTextVarProc _ANSI_ARGS_(( |
168 |
ClientData clientData, Tcl_Interp *interp, |
169 |
char *name1, char *name2, int flags)); |
170 |
static int MenuButtonWidgetObjCmd _ANSI_ARGS_(( |
171 |
ClientData clientData, Tcl_Interp *interp, |
172 |
int objc, Tcl_Obj *CONST objv[])); |
173 |
static int ConfigureMenuButton _ANSI_ARGS_((Tcl_Interp *interp, |
174 |
TkMenuButton *mbPtr, int objc, |
175 |
Tcl_Obj *CONST objv[])); |
176 |
static void DestroyMenuButton _ANSI_ARGS_((char *memPtr)); |
177 |
|
178 |
/* |
179 |
*-------------------------------------------------------------- |
180 |
* |
181 |
* Tk_MenubuttonObjCmd -- |
182 |
* |
183 |
* This procedure is invoked to process the "button", "label", |
184 |
* "radiobutton", and "checkbutton" Tcl commands. See the |
185 |
* user documentation for details on what it does. |
186 |
* |
187 |
* Results: |
188 |
* A standard Tcl result. |
189 |
* |
190 |
* Side effects: |
191 |
* See the user documentation. |
192 |
* |
193 |
*-------------------------------------------------------------- |
194 |
*/ |
195 |
|
196 |
int |
197 |
Tk_MenubuttonObjCmd(clientData, interp, objc, objv) |
198 |
ClientData clientData; /* Either NULL or pointer to |
199 |
* option table. */ |
200 |
Tcl_Interp *interp; /* Current interpreter. */ |
201 |
int objc; /* Number of arguments. */ |
202 |
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
203 |
{ |
204 |
register TkMenuButton *mbPtr; |
205 |
Tk_OptionTable optionTable; |
206 |
Tk_Window tkwin; |
207 |
|
208 |
optionTable = (Tk_OptionTable) clientData; |
209 |
if (optionTable == NULL) { |
210 |
Tcl_CmdInfo info; |
211 |
char *name; |
212 |
|
213 |
/* |
214 |
* We haven't created the option table for this widget class |
215 |
* yet. Do it now and save the table as the clientData for |
216 |
* the command, so we'll have access to it in future |
217 |
* invocations of the command. |
218 |
*/ |
219 |
|
220 |
optionTable = Tk_CreateOptionTable(interp, optionSpecs); |
221 |
name = Tcl_GetString(objv[0]); |
222 |
Tcl_GetCommandInfo(interp, name, &info); |
223 |
info.objClientData = (ClientData) optionTable; |
224 |
Tcl_SetCommandInfo(interp, name, &info); |
225 |
} |
226 |
|
227 |
if (objc < 2) { |
228 |
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); |
229 |
return TCL_ERROR; |
230 |
} |
231 |
|
232 |
/* |
233 |
* Create the new window. |
234 |
*/ |
235 |
|
236 |
tkwin = Tk_CreateWindowFromPath(interp, |
237 |
Tk_MainWindow(interp), Tcl_GetString(objv[1]), (char *) NULL); |
238 |
if (tkwin == NULL) { |
239 |
return TCL_ERROR; |
240 |
} |
241 |
|
242 |
Tk_SetClass(tkwin, "Menubutton"); |
243 |
mbPtr = TkpCreateMenuButton(tkwin); |
244 |
|
245 |
TkSetClassProcs(tkwin, &tkpMenubuttonClass, (ClientData) mbPtr); |
246 |
|
247 |
/* |
248 |
* Initialize the data structure for the button. |
249 |
*/ |
250 |
|
251 |
mbPtr->tkwin = tkwin; |
252 |
mbPtr->display = Tk_Display (tkwin); |
253 |
mbPtr->interp = interp; |
254 |
mbPtr->widgetCmd = Tcl_CreateObjCommand(interp, |
255 |
Tk_PathName(mbPtr->tkwin), MenuButtonWidgetObjCmd, |
256 |
(ClientData) mbPtr, MenuButtonCmdDeletedProc); |
257 |
mbPtr->optionTable = optionTable; |
258 |
mbPtr->menuName = NULL; |
259 |
mbPtr->text = NULL; |
260 |
mbPtr->underline = -1; |
261 |
mbPtr->textVarName = NULL; |
262 |
mbPtr->bitmap = None; |
263 |
mbPtr->imageString = NULL; |
264 |
mbPtr->image = NULL; |
265 |
mbPtr->state = STATE_NORMAL; |
266 |
mbPtr->normalBorder = NULL; |
267 |
mbPtr->activeBorder = NULL; |
268 |
mbPtr->borderWidth = 0; |
269 |
mbPtr->relief = TK_RELIEF_FLAT; |
270 |
mbPtr->highlightWidth = 0; |
271 |
mbPtr->highlightBgColorPtr = NULL; |
272 |
mbPtr->highlightColorPtr = NULL; |
273 |
mbPtr->inset = 0; |
274 |
mbPtr->tkfont = NULL; |
275 |
mbPtr->normalFg = NULL; |
276 |
mbPtr->activeFg = NULL; |
277 |
mbPtr->disabledFg = NULL; |
278 |
mbPtr->normalTextGC = None; |
279 |
mbPtr->activeTextGC = None; |
280 |
mbPtr->gray = None; |
281 |
mbPtr->disabledGC = None; |
282 |
mbPtr->leftBearing = 0; |
283 |
mbPtr->rightBearing = 0; |
284 |
mbPtr->widthString = NULL; |
285 |
mbPtr->heightString = NULL; |
286 |
mbPtr->width = 0; |
287 |
mbPtr->width = 0; |
288 |
mbPtr->wrapLength = 0; |
289 |
mbPtr->padX = 0; |
290 |
mbPtr->padY = 0; |
291 |
mbPtr->anchor = TK_ANCHOR_CENTER; |
292 |
mbPtr->justify = TK_JUSTIFY_CENTER; |
293 |
mbPtr->textLayout = NULL; |
294 |
mbPtr->indicatorOn = 0; |
295 |
mbPtr->indicatorWidth = 0; |
296 |
mbPtr->indicatorHeight = 0; |
297 |
mbPtr->direction = DIRECTION_FLUSH; |
298 |
mbPtr->cursor = None; |
299 |
mbPtr->takeFocus = NULL; |
300 |
mbPtr->flags = 0; |
301 |
|
302 |
Tk_CreateEventHandler(mbPtr->tkwin, |
303 |
ExposureMask|StructureNotifyMask|FocusChangeMask, |
304 |
MenuButtonEventProc, (ClientData) mbPtr); |
305 |
|
306 |
if (Tk_InitOptions(interp, (char *) mbPtr, optionTable, tkwin) |
307 |
!= TCL_OK) { |
308 |
Tk_DestroyWindow(mbPtr->tkwin); |
309 |
return TCL_ERROR; |
310 |
} |
311 |
|
312 |
if (ConfigureMenuButton(interp, mbPtr, objc-2, objv+2) != TCL_OK) { |
313 |
Tk_DestroyWindow(mbPtr->tkwin); |
314 |
return TCL_ERROR; |
315 |
} |
316 |
|
317 |
Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(mbPtr->tkwin), |
318 |
-1); |
319 |
return TCL_OK; |
320 |
} |
321 |
|
322 |
/* |
323 |
*-------------------------------------------------------------- |
324 |
* |
325 |
* MenuButtonWidgetObjCmd -- |
326 |
* |
327 |
* This procedure is invoked to process the Tcl command |
328 |
* that corresponds to a widget managed by this module. |
329 |
* See the user documentation for details on what it does. |
330 |
* |
331 |
* Results: |
332 |
* A standard Tcl result. |
333 |
* |
334 |
* Side effects: |
335 |
* See the user documentation. |
336 |
* |
337 |
*-------------------------------------------------------------- |
338 |
*/ |
339 |
|
340 |
static int |
341 |
MenuButtonWidgetObjCmd(clientData, interp, objc, objv) |
342 |
ClientData clientData; /* Information about button widget. */ |
343 |
Tcl_Interp *interp; /* Current interpreter. */ |
344 |
int objc; /* Number of arguments. */ |
345 |
Tcl_Obj *CONST objv[]; /* Argument objects. */ |
346 |
{ |
347 |
register TkMenuButton *mbPtr = (TkMenuButton *) clientData; |
348 |
int result, index; |
349 |
Tcl_Obj *objPtr; |
350 |
|
351 |
if (objc < 2) { |
352 |
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); |
353 |
return TCL_ERROR; |
354 |
} |
355 |
result = Tcl_GetIndexFromObj(interp, objv[1], |
356 |
commandNames, "option", 0, &index); |
357 |
if (result != TCL_OK) { |
358 |
return result; |
359 |
} |
360 |
Tcl_Preserve((ClientData) mbPtr); |
361 |
|
362 |
switch (index) { |
363 |
case COMMAND_CGET: { |
364 |
if (objc != 3) { |
365 |
Tcl_WrongNumArgs(interp, 1, objv, "cget option"); |
366 |
goto error; |
367 |
} |
368 |
|
369 |
objPtr = Tk_GetOptionValue(interp, (char *) mbPtr, |
370 |
mbPtr->optionTable, objv[2], mbPtr->tkwin); |
371 |
if (objPtr == NULL) { |
372 |
goto error; |
373 |
} else { |
374 |
Tcl_SetObjResult(interp, objPtr); |
375 |
} |
376 |
break; |
377 |
} |
378 |
|
379 |
case COMMAND_CONFIGURE: { |
380 |
if (objc <= 3) { |
381 |
objPtr = Tk_GetOptionInfo(interp, (char *) mbPtr, |
382 |
mbPtr->optionTable, |
383 |
(objc == 3) ? objv[2] : (Tcl_Obj *) NULL, |
384 |
mbPtr->tkwin); |
385 |
if (objPtr == NULL) { |
386 |
goto error; |
387 |
} else { |
388 |
Tcl_SetObjResult(interp, objPtr); |
389 |
} |
390 |
} else { |
391 |
result = ConfigureMenuButton(interp, mbPtr, objc-2, |
392 |
objv+2); |
393 |
} |
394 |
break; |
395 |
} |
396 |
} |
397 |
Tcl_Release((ClientData) mbPtr); |
398 |
return result; |
399 |
|
400 |
error: |
401 |
Tcl_Release((ClientData) mbPtr); |
402 |
return TCL_ERROR; |
403 |
} |
404 |
|
405 |
/* |
406 |
*---------------------------------------------------------------------- |
407 |
* |
408 |
* DestroyMenuButton -- |
409 |
* |
410 |
* This procedure is invoked to recycle all of the resources |
411 |
* associated with a menubutton widget. It is invoked as a |
412 |
* when-idle handler in order to make sure that there is no |
413 |
* other use of the menubutton pending at the time of the deletion. |
414 |
* |
415 |
* Results: |
416 |
* None. |
417 |
* |
418 |
* Side effects: |
419 |
* Everything associated with the widget is freed up. |
420 |
* |
421 |
*---------------------------------------------------------------------- |
422 |
*/ |
423 |
|
424 |
static void |
425 |
DestroyMenuButton(memPtr) |
426 |
char *memPtr; /* Info about button widget. */ |
427 |
{ |
428 |
register TkMenuButton *mbPtr = (TkMenuButton *) memPtr; |
429 |
TkpDestroyMenuButton(mbPtr); |
430 |
|
431 |
if (mbPtr->flags & REDRAW_PENDING) { |
432 |
Tcl_CancelIdleCall(TkpDisplayMenuButton, (ClientData) mbPtr); |
433 |
} |
434 |
|
435 |
/* |
436 |
* Free up all the stuff that requires special handling, then |
437 |
* let Tk_FreeOptions handle all the standard option-related |
438 |
* stuff. |
439 |
*/ |
440 |
|
441 |
Tcl_DeleteCommandFromToken(mbPtr->interp, mbPtr->widgetCmd); |
442 |
if (mbPtr->textVarName != NULL) { |
443 |
Tcl_UntraceVar(mbPtr->interp, mbPtr->textVarName, |
444 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
445 |
MenuButtonTextVarProc, (ClientData) mbPtr); |
446 |
} |
447 |
if (mbPtr->image != NULL) { |
448 |
Tk_FreeImage(mbPtr->image); |
449 |
} |
450 |
if (mbPtr->normalTextGC != None) { |
451 |
Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC); |
452 |
} |
453 |
if (mbPtr->activeTextGC != None) { |
454 |
Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC); |
455 |
} |
456 |
if (mbPtr->disabledGC != None) { |
457 |
Tk_FreeGC(mbPtr->display, mbPtr->disabledGC); |
458 |
} |
459 |
if (mbPtr->gray != None) { |
460 |
Tk_FreeBitmap(mbPtr->display, mbPtr->gray); |
461 |
} |
462 |
if (mbPtr->textLayout != NULL) { |
463 |
Tk_FreeTextLayout(mbPtr->textLayout); |
464 |
} |
465 |
Tk_FreeConfigOptions((char *) mbPtr, mbPtr->optionTable, |
466 |
mbPtr->tkwin); |
467 |
mbPtr->tkwin = NULL; |
468 |
Tcl_EventuallyFree((ClientData) mbPtr, TCL_DYNAMIC); |
469 |
} |
470 |
|
471 |
/* |
472 |
*---------------------------------------------------------------------- |
473 |
* |
474 |
* ConfigureMenuButton -- |
475 |
* |
476 |
* This procedure is called to process an argv/argc list, plus |
477 |
* the Tk option database, in order to configure (or |
478 |
* reconfigure) a menubutton widget. |
479 |
* |
480 |
* Results: |
481 |
* The return value is a standard Tcl result. If TCL_ERROR is |
482 |
* returned, then the interp's result contains an error message. |
483 |
* |
484 |
* Side effects: |
485 |
* Configuration information, such as text string, colors, font, |
486 |
* etc. get set for mbPtr; old resources get freed, if there |
487 |
* were any. The menubutton is redisplayed. |
488 |
* |
489 |
*---------------------------------------------------------------------- |
490 |
*/ |
491 |
|
492 |
static int |
493 |
ConfigureMenuButton(interp, mbPtr, objc, objv) |
494 |
Tcl_Interp *interp; /* Used for error reporting. */ |
495 |
register TkMenuButton *mbPtr; |
496 |
/* Information about widget; may or may |
497 |
* not already have values for some |
498 |
* fields. */ |
499 |
int objc; /* Number of valid entries in objv. */ |
500 |
Tcl_Obj *CONST objv[]; /* Arguments. */ |
501 |
{ |
502 |
Tk_SavedOptions savedOptions; |
503 |
Tcl_Obj *errorResult = NULL; |
504 |
int error; |
505 |
Tk_Image image; |
506 |
|
507 |
/* |
508 |
* Eliminate any existing trace on variables monitored by the |
509 |
* menubutton. |
510 |
*/ |
511 |
|
512 |
if (mbPtr->textVarName != NULL) { |
513 |
Tcl_UntraceVar(interp, mbPtr->textVarName, |
514 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
515 |
MenuButtonTextVarProc, (ClientData) mbPtr); |
516 |
} |
517 |
|
518 |
/* |
519 |
* The following loop is potentially executed twice. During the |
520 |
* first pass configuration options get set to their new values. |
521 |
* If there is an error in this pass, we execute a second pass |
522 |
* to restore all the options to their previous values. |
523 |
*/ |
524 |
|
525 |
for (error = 0; error <= 1; error++) { |
526 |
if (!error) { |
527 |
/* |
528 |
* First pass: set options to new values. |
529 |
*/ |
530 |
|
531 |
if (Tk_SetOptions(interp, (char *) mbPtr, |
532 |
mbPtr->optionTable, objc, objv, |
533 |
mbPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) { |
534 |
continue; |
535 |
} |
536 |
} else { |
537 |
/* |
538 |
* Second pass: restore options to old values. |
539 |
*/ |
540 |
|
541 |
errorResult = Tcl_GetObjResult(interp); |
542 |
Tcl_IncrRefCount(errorResult); |
543 |
Tk_RestoreSavedOptions(&savedOptions); |
544 |
} |
545 |
|
546 |
/* |
547 |
* A few options need special processing, such as setting the |
548 |
* background from a 3-D border, or filling in complicated |
549 |
* defaults that couldn't be specified to Tk_SetOptions. |
550 |
*/ |
551 |
|
552 |
if ((mbPtr->state == STATE_ACTIVE) |
553 |
&& !Tk_StrictMotif(mbPtr->tkwin)) { |
554 |
Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->activeBorder); |
555 |
} else { |
556 |
Tk_SetBackgroundFromBorder(mbPtr->tkwin, mbPtr->normalBorder); |
557 |
} |
558 |
|
559 |
if (mbPtr->highlightWidth < 0) { |
560 |
mbPtr->highlightWidth = 0; |
561 |
} |
562 |
|
563 |
if (mbPtr->padX < 0) { |
564 |
mbPtr->padX = 0; |
565 |
} |
566 |
if (mbPtr->padY < 0) { |
567 |
mbPtr->padY = 0; |
568 |
} |
569 |
|
570 |
/* |
571 |
* Get the image for the widget, if there is one. Allocate the |
572 |
* new image before freeing the old one, so that the reference |
573 |
* count doesn't go to zero and cause image data to be discarded. |
574 |
*/ |
575 |
|
576 |
if (mbPtr->imageString != NULL) { |
577 |
image = Tk_GetImage(mbPtr->interp, mbPtr->tkwin, |
578 |
mbPtr->imageString, MenuButtonImageProc, |
579 |
(ClientData) mbPtr); |
580 |
if (image == NULL) { |
581 |
return TCL_ERROR; |
582 |
} |
583 |
} else { |
584 |
image = NULL; |
585 |
} |
586 |
if (mbPtr->image != NULL) { |
587 |
Tk_FreeImage(mbPtr->image); |
588 |
} |
589 |
mbPtr->image = image; |
590 |
|
591 |
/* |
592 |
* Recompute the geometry for the button. |
593 |
*/ |
594 |
|
595 |
if ((mbPtr->bitmap != None) || (mbPtr->image != NULL)) { |
596 |
if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->widthString, |
597 |
&mbPtr->width) != TCL_OK) { |
598 |
widthError: |
599 |
Tcl_AddErrorInfo(interp, "\n (processing -width option)"); |
600 |
continue; |
601 |
} |
602 |
if (Tk_GetPixels(interp, mbPtr->tkwin, mbPtr->heightString, |
603 |
&mbPtr->height) != TCL_OK) { |
604 |
heightError: |
605 |
Tcl_AddErrorInfo(interp, "\n (processing -height option)"); |
606 |
continue; |
607 |
} |
608 |
} else { |
609 |
if (Tcl_GetInt(interp, mbPtr->widthString, &mbPtr->width) |
610 |
!= TCL_OK) { |
611 |
goto widthError; |
612 |
} |
613 |
if (Tcl_GetInt(interp, mbPtr->heightString, &mbPtr->height) |
614 |
!= TCL_OK) { |
615 |
goto heightError; |
616 |
} |
617 |
} |
618 |
break; |
619 |
} |
620 |
|
621 |
if (!error) { |
622 |
Tk_FreeSavedOptions(&savedOptions); |
623 |
} |
624 |
|
625 |
if ((mbPtr->image == NULL) && (mbPtr->bitmap == None) |
626 |
&& (mbPtr->textVarName != NULL)) { |
627 |
|
628 |
/* |
629 |
* The menubutton displays the value of a variable. |
630 |
* Set up a trace to watch for any changes in it, create |
631 |
* the variable if it doesn't exist, and fetch its |
632 |
* current value. |
633 |
*/ |
634 |
|
635 |
char *value; |
636 |
|
637 |
value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); |
638 |
if (value == NULL) { |
639 |
Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, |
640 |
TCL_GLOBAL_ONLY); |
641 |
} else { |
642 |
if (mbPtr->text != NULL) { |
643 |
ckfree(mbPtr->text); |
644 |
} |
645 |
mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1)); |
646 |
strcpy(mbPtr->text, value); |
647 |
} |
648 |
Tcl_TraceVar(interp, mbPtr->textVarName, |
649 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
650 |
MenuButtonTextVarProc, (ClientData) mbPtr); |
651 |
} |
652 |
|
653 |
TkMenuButtonWorldChanged((ClientData) mbPtr); |
654 |
if (error) { |
655 |
Tcl_SetObjResult(interp, errorResult); |
656 |
Tcl_DecrRefCount(errorResult); |
657 |
return TCL_ERROR; |
658 |
} else { |
659 |
return TCL_OK; |
660 |
} |
661 |
} |
662 |
|
663 |
/* |
664 |
*--------------------------------------------------------------------------- |
665 |
* |
666 |
* TkMenuButtonWorldChanged -- |
667 |
* |
668 |
* This procedure is called when the world has changed in some |
669 |
* way and the widget needs to recompute all its graphics contexts |
670 |
* and determine its new geometry. |
671 |
* |
672 |
* Results: |
673 |
* None. |
674 |
* |
675 |
* Side effects: |
676 |
* TkMenuButton will be relayed out and redisplayed. |
677 |
* |
678 |
*--------------------------------------------------------------------------- |
679 |
*/ |
680 |
|
681 |
void |
682 |
TkMenuButtonWorldChanged(instanceData) |
683 |
ClientData instanceData; /* Information about widget. */ |
684 |
{ |
685 |
XGCValues gcValues; |
686 |
GC gc; |
687 |
unsigned long mask; |
688 |
TkMenuButton *mbPtr; |
689 |
|
690 |
mbPtr = (TkMenuButton *) instanceData; |
691 |
|
692 |
gcValues.font = Tk_FontId(mbPtr->tkfont); |
693 |
gcValues.foreground = mbPtr->normalFg->pixel; |
694 |
gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; |
695 |
|
696 |
/* |
697 |
* Note: GraphicsExpose events are disabled in GC's because they're |
698 |
* used to copy stuff from an off-screen pixmap onto the screen (we know |
699 |
* that there's no problem with obscured areas). |
700 |
*/ |
701 |
|
702 |
gcValues.graphics_exposures = False; |
703 |
mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures; |
704 |
gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); |
705 |
if (mbPtr->normalTextGC != None) { |
706 |
Tk_FreeGC(mbPtr->display, mbPtr->normalTextGC); |
707 |
} |
708 |
mbPtr->normalTextGC = gc; |
709 |
|
710 |
gcValues.font = Tk_FontId(mbPtr->tkfont); |
711 |
gcValues.foreground = mbPtr->activeFg->pixel; |
712 |
gcValues.background = Tk_3DBorderColor(mbPtr->activeBorder)->pixel; |
713 |
mask = GCForeground | GCBackground | GCFont; |
714 |
gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); |
715 |
if (mbPtr->activeTextGC != None) { |
716 |
Tk_FreeGC(mbPtr->display, mbPtr->activeTextGC); |
717 |
} |
718 |
mbPtr->activeTextGC = gc; |
719 |
|
720 |
gcValues.font = Tk_FontId(mbPtr->tkfont); |
721 |
gcValues.background = Tk_3DBorderColor(mbPtr->normalBorder)->pixel; |
722 |
if ((mbPtr->disabledFg != NULL) && (mbPtr->imageString == NULL)) { |
723 |
gcValues.foreground = mbPtr->disabledFg->pixel; |
724 |
mask = GCForeground | GCBackground | GCFont; |
725 |
} else { |
726 |
gcValues.foreground = gcValues.background; |
727 |
mask = GCForeground; |
728 |
if (mbPtr->gray == None) { |
729 |
mbPtr->gray = Tk_GetBitmap(NULL, mbPtr->tkwin, |
730 |
Tk_GetUid("gray50")); |
731 |
} |
732 |
if (mbPtr->gray != None) { |
733 |
gcValues.fill_style = FillStippled; |
734 |
gcValues.stipple = mbPtr->gray; |
735 |
mask |= GCFillStyle | GCStipple; |
736 |
} |
737 |
} |
738 |
gc = Tk_GetGC(mbPtr->tkwin, mask, &gcValues); |
739 |
if (mbPtr->disabledGC != None) { |
740 |
Tk_FreeGC(mbPtr->display, mbPtr->disabledGC); |
741 |
} |
742 |
mbPtr->disabledGC = gc; |
743 |
|
744 |
TkpComputeMenuButtonGeometry(mbPtr); |
745 |
|
746 |
/* |
747 |
* Lastly, arrange for the button to be redisplayed. |
748 |
*/ |
749 |
|
750 |
if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { |
751 |
Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); |
752 |
mbPtr->flags |= REDRAW_PENDING; |
753 |
} |
754 |
} |
755 |
|
756 |
/* |
757 |
*-------------------------------------------------------------- |
758 |
* |
759 |
* MenuButtonEventProc -- |
760 |
* |
761 |
* This procedure is invoked by the Tk dispatcher for various |
762 |
* events on buttons. |
763 |
* |
764 |
* Results: |
765 |
* None. |
766 |
* |
767 |
* Side effects: |
768 |
* When the window gets deleted, internal structures get |
769 |
* cleaned up. When it gets exposed, it is redisplayed. |
770 |
* |
771 |
*-------------------------------------------------------------- |
772 |
*/ |
773 |
|
774 |
static void |
775 |
MenuButtonEventProc(clientData, eventPtr) |
776 |
ClientData clientData; /* Information about window. */ |
777 |
XEvent *eventPtr; /* Information about event. */ |
778 |
{ |
779 |
TkMenuButton *mbPtr = (TkMenuButton *) clientData; |
780 |
if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { |
781 |
goto redraw; |
782 |
} else if (eventPtr->type == ConfigureNotify) { |
783 |
/* |
784 |
* Must redraw after size changes, since layout could have changed |
785 |
* and borders will need to be redrawn. |
786 |
*/ |
787 |
|
788 |
goto redraw; |
789 |
} else if (eventPtr->type == DestroyNotify) { |
790 |
DestroyMenuButton((char *) mbPtr); |
791 |
} else if (eventPtr->type == FocusIn) { |
792 |
if (eventPtr->xfocus.detail != NotifyInferior) { |
793 |
mbPtr->flags |= GOT_FOCUS; |
794 |
if (mbPtr->highlightWidth > 0) { |
795 |
goto redraw; |
796 |
} |
797 |
} |
798 |
} else if (eventPtr->type == FocusOut) { |
799 |
if (eventPtr->xfocus.detail != NotifyInferior) { |
800 |
mbPtr->flags &= ~GOT_FOCUS; |
801 |
if (mbPtr->highlightWidth > 0) { |
802 |
goto redraw; |
803 |
} |
804 |
} |
805 |
} |
806 |
return; |
807 |
|
808 |
redraw: |
809 |
if ((mbPtr->tkwin != NULL) && !(mbPtr->flags & REDRAW_PENDING)) { |
810 |
Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); |
811 |
mbPtr->flags |= REDRAW_PENDING; |
812 |
} |
813 |
} |
814 |
|
815 |
/* |
816 |
*---------------------------------------------------------------------- |
817 |
* |
818 |
* MenuButtonCmdDeletedProc -- |
819 |
* |
820 |
* This procedure is invoked when a widget command is deleted. If |
821 |
* the widget isn't already in the process of being destroyed, |
822 |
* this command destroys it. |
823 |
* |
824 |
* Results: |
825 |
* None. |
826 |
* |
827 |
* Side effects: |
828 |
* The widget is destroyed. |
829 |
* |
830 |
*---------------------------------------------------------------------- |
831 |
*/ |
832 |
|
833 |
static void |
834 |
MenuButtonCmdDeletedProc(clientData) |
835 |
ClientData clientData; /* Pointer to widget record for widget. */ |
836 |
{ |
837 |
TkMenuButton *mbPtr = (TkMenuButton *) clientData; |
838 |
Tk_Window tkwin = mbPtr->tkwin; |
839 |
|
840 |
/* |
841 |
* This procedure could be invoked either because the window was |
842 |
* destroyed and the command was then deleted (in which case tkwin |
843 |
* is NULL) or because the command was deleted, and then this procedure |
844 |
* destroys the widget. |
845 |
*/ |
846 |
|
847 |
if (tkwin != NULL) { |
848 |
Tk_DestroyWindow(tkwin); |
849 |
} |
850 |
} |
851 |
|
852 |
/* |
853 |
*-------------------------------------------------------------- |
854 |
* |
855 |
* MenuButtonTextVarProc -- |
856 |
* |
857 |
* This procedure is invoked when someone changes the variable |
858 |
* whose contents are to be displayed in a menu button. |
859 |
* |
860 |
* Results: |
861 |
* NULL is always returned. |
862 |
* |
863 |
* Side effects: |
864 |
* The text displayed in the menu button will change to match the |
865 |
* variable. |
866 |
* |
867 |
*-------------------------------------------------------------- |
868 |
*/ |
869 |
|
870 |
/* ARGSUSED */ |
871 |
static char * |
872 |
MenuButtonTextVarProc(clientData, interp, name1, name2, flags) |
873 |
ClientData clientData; /* Information about button. */ |
874 |
Tcl_Interp *interp; /* Interpreter containing variable. */ |
875 |
char *name1; /* Name of variable. */ |
876 |
char *name2; /* Second part of variable name. */ |
877 |
int flags; /* Information about what happened. */ |
878 |
{ |
879 |
register TkMenuButton *mbPtr = (TkMenuButton *) clientData; |
880 |
char *value; |
881 |
|
882 |
/* |
883 |
* If the variable is unset, then immediately recreate it unless |
884 |
* the whole interpreter is going away. |
885 |
*/ |
886 |
|
887 |
if (flags & TCL_TRACE_UNSETS) { |
888 |
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { |
889 |
Tcl_SetVar(interp, mbPtr->textVarName, mbPtr->text, |
890 |
TCL_GLOBAL_ONLY); |
891 |
Tcl_TraceVar(interp, mbPtr->textVarName, |
892 |
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, |
893 |
MenuButtonTextVarProc, clientData); |
894 |
} |
895 |
return (char *) NULL; |
896 |
} |
897 |
|
898 |
value = Tcl_GetVar(interp, mbPtr->textVarName, TCL_GLOBAL_ONLY); |
899 |
if (value == NULL) { |
900 |
value = ""; |
901 |
} |
902 |
if (mbPtr->text != NULL) { |
903 |
ckfree(mbPtr->text); |
904 |
} |
905 |
mbPtr->text = (char *) ckalloc((unsigned) (strlen(value) + 1)); |
906 |
strcpy(mbPtr->text, value); |
907 |
TkpComputeMenuButtonGeometry(mbPtr); |
908 |
|
909 |
if ((mbPtr->tkwin != NULL) && Tk_IsMapped(mbPtr->tkwin) |
910 |
&& !(mbPtr->flags & REDRAW_PENDING)) { |
911 |
Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); |
912 |
mbPtr->flags |= REDRAW_PENDING; |
913 |
} |
914 |
return (char *) NULL; |
915 |
} |
916 |
|
917 |
/* |
918 |
*---------------------------------------------------------------------- |
919 |
* |
920 |
* MenuButtonImageProc -- |
921 |
* |
922 |
* This procedure is invoked by the image code whenever the manager |
923 |
* for an image does something that affects the size of contents |
924 |
* of an image displayed in a button. |
925 |
* |
926 |
* Results: |
927 |
* None. |
928 |
* |
929 |
* Side effects: |
930 |
* Arranges for the button to get redisplayed. |
931 |
* |
932 |
*---------------------------------------------------------------------- |
933 |
*/ |
934 |
|
935 |
static void |
936 |
MenuButtonImageProc(clientData, x, y, width, height, imgWidth, imgHeight) |
937 |
ClientData clientData; /* Pointer to widget record. */ |
938 |
int x, y; /* Upper left pixel (within image) |
939 |
* that must be redisplayed. */ |
940 |
int width, height; /* Dimensions of area to redisplay |
941 |
* (may be <= 0). */ |
942 |
int imgWidth, imgHeight; /* New dimensions of image. */ |
943 |
{ |
944 |
register TkMenuButton *mbPtr = (TkMenuButton *) clientData; |
945 |
|
946 |
if (mbPtr->tkwin != NULL) { |
947 |
TkpComputeMenuButtonGeometry(mbPtr); |
948 |
if (Tk_IsMapped(mbPtr->tkwin) && !(mbPtr->flags & REDRAW_PENDING)) { |
949 |
Tcl_DoWhenIdle(TkpDisplayMenuButton, (ClientData) mbPtr); |
950 |
mbPtr->flags |= REDRAW_PENDING; |
951 |
} |
952 |
} |
953 |
} |
954 |
|
955 |
/* End of tkmenubutton.c */ |