1 /*
2 * tkFrame.c --
3 *
4 * This module implements "frame", "labelframe" and "toplevel" widgets
5 * for the Tk toolkit. Frames are windows with a background color and
6 * possibly a 3-D effect, but not much else in the way of attributes.
7 *
8 * Copyright © 1990-1994 The Regents of the University of California.
9 * Copyright © 1994-1997 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution of
12 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 */
14
15 #include "tkInt.h"
16 #include "default.h"
17
18 /*
19 * The following enum is used to define the type of the frame.
20 */
21
22 enum FrameType {
23 TYPE_FRAME, TYPE_TOPLEVEL, TYPE_LABELFRAME
24 };
25
26 /*
27 * A data structure of the following type is kept for each
28 * frame that currently exists for this process:
29 */
30
31 typedef struct {
32 Tk_Window tkwin; /* Window that embodies the frame. NULL means
33 * that the window has been destroyed but the
34 * data structures haven't yet been cleaned
35 * up. */
36 Display *display; /* Display containing widget. Used, among
37 * other things, so that resources can be
38 * freed even after tkwin has gone away. */
39 Tcl_Interp *interp; /* Interpreter associated with widget. Used to
40 * delete widget command. */
41 Tcl_Command widgetCmd; /* Token for frame's widget command. */
42 Tk_OptionTable optionTable; /* Table that defines configuration options
43 * available for this widget. */
44 char *className; /* Class name for widget (from configuration
45 * option). Malloc-ed. */
46 enum FrameType type; /* Type of widget, such as TYPE_FRAME. */
47 char *screenName; /* Screen on which widget is created. Non-null
48 * only for top-levels. Malloc-ed, may be
49 * NULL. */
50 char *visualName; /* Textual description of visual for window,
51 * from -visual option. Malloc-ed, may be
52 * NULL. */
53 char *colormapName; /* Textual description of colormap for window,
54 * from -colormap option. Malloc-ed, may be
55 * NULL. */
56 char *menuName; /* Textual description of menu to use for
57 * menubar. Malloc-ed, may be NULL. */
58 Colormap colormap; /* If not None, identifies a colormap
59 * allocated for this window, which must be
60 * freed when the window is deleted. */
61 Tk_3DBorder border; /* Structure used to draw 3-D border and
62 * background. NULL means no background or
63 * border. */
64 int borderWidth; /* Width of 3-D border (if any). */
65 int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
66 int highlightWidth; /* Width in pixels of highlight to draw around
67 * widget when it has the focus. 0 means don't
68 * draw a highlight. */
69 XColor *highlightBgColorPtr;
70 /* Color for drawing traversal highlight area
71 * when highlight is off. */
72 XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
73 int width; /* Width to request for window. <= 0 means
74 * don't request any size. */
75 int height; /* Height to request for window. <= 0 means
76 * don't request any size. */
77 Tk_Cursor cursor; /* Current cursor for window, or None. */
78 char *takeFocus; /* Value of -takefocus option; not used in the
79 * C code, but used by keyboard traversal
80 * scripts. Malloc'ed, but may be NULL. */
81 int isContainer; /* 1 means this window is a container, 0 means
82 * that it isn't. */
83 char *useThis; /* If the window is embedded, this points to
84 * the name of the window in which it is
85 * embedded (malloc'ed). For non-embedded
86 * windows this is NULL. */
87 int flags; /* Various flags; see below for
88 * definitions. */
89 Tcl_Obj *padXPtr; /* Value of -padx option: specifies how many
90 * pixels of extra space to leave on left and
91 * right of child area. */
92 int padX; /* Integer value corresponding to padXPtr. */
93 Tcl_Obj *padYPtr; /* Value of -padx option: specifies how many
94 * pixels of extra space to leave above and
95 * below child area. */
96 int padY; /* Integer value corresponding to padYPtr. */
97 Tcl_Obj *bgimgPtr; /* Value of -backgroundimage option: specifies
98 * image to display on window's background, or
99 * NULL if none. */
100 Tk_Image bgimg; /* Derived from bgimgPtr by calling
101 * Tk_GetImage, or NULL if bgimgPtr is
102 * NULL. */
103 int tile; /* Whether to tile the bgimg. */
104 #ifndef TK_NO_DOUBLE_BUFFERING
105 GC copyGC; /* GC for copying when double-buffering. */
106 #endif /* TK_NO_DOUBLE_BUFFERING */
107 } Frame;
108
109 /*
110 * A data structure of the following type is kept for each labelframe widget
111 * managed by this file:
112 */
113
114 typedef struct {
115 Frame frame; /* A pointer to the generic frame structure.
116 * This must be the first element of the
117 * Labelframe. */
118 /*
119 * Labelframe specific configuration settings.
120 */
121 Tcl_Obj *textPtr; /* Value of -text option: specifies text to
122 * display in button. */
123 Tk_Font tkfont; /* Value of -font option: specifies font to
124 * use for display text. */
125 XColor *textColorPtr; /* Value of -fg option: specifies foreground
126 * color in normal mode. */
127 int labelAnchor; /* Value of -labelanchor option: specifies
128 * where to place the label. */
129 Tk_Window labelWin; /* Value of -labelwidget option: Window to use
130 * as label for the frame. */
131 /*
132 * Labelframe specific fields for use with configuration settings above.
133 */
134 GC textGC; /* GC for drawing text in normal mode. */
135 Tk_TextLayout textLayout; /* Stored text layout information. */
136 XRectangle labelBox; /* The label's actual size and position. */
137 int labelReqWidth; /* The label's requested width. */
138 int labelReqHeight; /* The label's requested height. */
139 int labelTextX, labelTextY; /* Position of the text to be drawn. */
140 } Labelframe;
141
142 /*
143 * The following macros define how many extra pixels to leave around a label's
144 * text.
145 */
146
147 #define LABELSPACING 1
148 #define LABELMARGIN 4
149
150 /*
151 * Flag bits for frames:
152 *
153 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has
154 * already been queued to redraw this window.
155 * GOT_FOCUS: Non-zero means this widget currently has the
156 * input focus.
157 */
158
159 #define REDRAW_PENDING 1
160 #define GOT_FOCUS 4
161
162 /*
163 * The following enum is used to define a type for the -labelanchor option of
164 * the Labelframe widget. These values are used as indices into the string
165 * table below.
166 */
167
168 enum labelanchor {
169 LABELANCHOR_E, LABELANCHOR_EN, LABELANCHOR_ES,
170 LABELANCHOR_N, LABELANCHOR_NE, LABELANCHOR_NW,
171 LABELANCHOR_S, LABELANCHOR_SE, LABELANCHOR_SW,
172 LABELANCHOR_W, LABELANCHOR_WN, LABELANCHOR_WS
173 };
174
175 static const char *const labelAnchorStrings[] = {
176 "e", "en", "es", "n", "ne", "nw", "s", "se", "sw", "w", "wn", "ws",
177 NULL
178 };
179
180 /*
181 * Information used for parsing configuration options. There are one common
182 * table used by all and one table for each widget class.
183 */
184
185 static const Tk_OptionSpec commonOptSpec[] = {
186 {TK_OPTION_BORDER, "-background", "background", "Background",
187 DEF_FRAME_BG_COLOR, TCL_INDEX_NONE, offsetof(Frame, border),
188 TK_OPTION_NULL_OK, DEF_FRAME_BG_MONO, 0},
189 {TK_OPTION_SYNONYM, "-bg", NULL, NULL,
190 NULL, 0, TCL_INDEX_NONE, 0, "-background", 0},
191 {TK_OPTION_STRING, "-colormap", "colormap", "Colormap",
192 DEF_FRAME_COLORMAP, TCL_INDEX_NONE, offsetof(Frame, colormapName),
193 TK_OPTION_NULL_OK, 0, 0},
194 /*
195 * Having -container is useless in a labelframe since a container has
196 * no border. It should be deprecated.
197 */
198 {TK_OPTION_BOOLEAN, "-container", "container", "Container",
199 DEF_FRAME_CONTAINER, TCL_INDEX_NONE, offsetof(Frame, isContainer), 0, 0, 0},
200 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
201 DEF_FRAME_CURSOR, TCL_INDEX_NONE, offsetof(Frame, cursor),
202 TK_OPTION_NULL_OK, 0, 0},
203 {TK_OPTION_PIXELS, "-height", "height", "Height",
204 DEF_FRAME_HEIGHT, TCL_INDEX_NONE, offsetof(Frame, height), 0, 0, 0},
205 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
206 "HighlightBackground", DEF_FRAME_HIGHLIGHT_BG, TCL_INDEX_NONE,
207 offsetof(Frame, highlightBgColorPtr), 0, 0, 0},
208 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
209 DEF_FRAME_HIGHLIGHT, TCL_INDEX_NONE, offsetof(Frame, highlightColorPtr),
210 0, 0, 0},
211 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
212 "HighlightThickness", DEF_FRAME_HIGHLIGHT_WIDTH, TCL_INDEX_NONE,
213 offsetof(Frame, highlightWidth), 0, 0, 0},
214 {TK_OPTION_PIXELS, "-padx", "padX", "Pad",
215 DEF_FRAME_PADX, offsetof(Frame, padXPtr),
216 offsetof(Frame, padX), 0, 0, 0},
217 {TK_OPTION_PIXELS, "-pady", "padY", "Pad",
218 DEF_FRAME_PADY, offsetof(Frame, padYPtr),
219 offsetof(Frame, padY), 0, 0, 0},
220 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
221 DEF_FRAME_TAKE_FOCUS, TCL_INDEX_NONE, offsetof(Frame, takeFocus),
222 TK_OPTION_NULL_OK, 0, 0},
223 {TK_OPTION_STRING, "-visual", "visual", "Visual",
224 DEF_FRAME_VISUAL, TCL_INDEX_NONE, offsetof(Frame, visualName),
225 TK_OPTION_NULL_OK, 0, 0},
226 {TK_OPTION_PIXELS, "-width", "width", "Width",
227 DEF_FRAME_WIDTH, TCL_INDEX_NONE, offsetof(Frame, width), 0, 0, 0},
228 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0}
229 };
230
231 static const Tk_OptionSpec frameOptSpec[] = {
232 {TK_OPTION_STRING, "-backgroundimage", "backgroundImage", "BackgroundImage",
233 DEF_FRAME_BG_IMAGE, offsetof(Frame, bgimgPtr), TCL_INDEX_NONE,
234 TK_OPTION_NULL_OK, 0, 0},
235 {TK_OPTION_SYNONYM, "-bd", NULL, NULL,
236 NULL, 0, TCL_INDEX_NONE, 0, "-borderwidth", 0},
237 {TK_OPTION_SYNONYM, "-bgimg", NULL, NULL,
238 NULL, 0, TCL_INDEX_NONE, 0, "-backgroundimage", 0},
239 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
240 DEF_FRAME_BORDER_WIDTH, TCL_INDEX_NONE, offsetof(Frame, borderWidth), 0, 0, 0},
241 {TK_OPTION_STRING, "-class", "class", "Class",
242 DEF_FRAME_CLASS, TCL_INDEX_NONE, offsetof(Frame, className), 0, 0, 0},
243 {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
244 DEF_FRAME_RELIEF, TCL_INDEX_NONE, offsetof(Frame, relief), 0, 0, 0},
245 {TK_OPTION_BOOLEAN, "-tile", "tile", "Tile",
246 DEF_FRAME_BG_TILE, TCL_INDEX_NONE, offsetof(Frame, tile), 0, 0, 0},
247 {TK_OPTION_END, NULL, NULL, NULL,
248 NULL, 0, 0, 0, commonOptSpec, 0}
249 };
250
251 static const Tk_OptionSpec toplevelOptSpec[] = {
252 {TK_OPTION_STRING, "-backgroundimage", "backgroundImage", "BackgroundImage",
253 DEF_FRAME_BG_IMAGE, offsetof(Frame, bgimgPtr), TCL_INDEX_NONE,
254 TK_OPTION_NULL_OK, 0, 0},
255 {TK_OPTION_SYNONYM, "-bd", NULL, NULL,
256 NULL, 0, TCL_INDEX_NONE, 0, "-borderwidth", 0},
257 {TK_OPTION_SYNONYM, "-bgimg", NULL, NULL,
258 NULL, 0, TCL_INDEX_NONE, 0, "-backgroundimage", 0},
259 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
260 DEF_FRAME_BORDER_WIDTH, TCL_INDEX_NONE, offsetof(Frame, borderWidth), 0, 0, 0},
261 {TK_OPTION_STRING, "-class", "class", "Class",
262 DEF_TOPLEVEL_CLASS, TCL_INDEX_NONE, offsetof(Frame, className), 0, 0, 0},
263 {TK_OPTION_STRING, "-menu", "menu", "Menu",
264 DEF_TOPLEVEL_MENU, TCL_INDEX_NONE, offsetof(Frame, menuName),
265 TK_OPTION_NULL_OK, 0, 0},
266 {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
267 DEF_FRAME_RELIEF, TCL_INDEX_NONE, offsetof(Frame, relief), 0, 0, 0},
268 {TK_OPTION_STRING, "-screen", "screen", "Screen",
269 DEF_TOPLEVEL_SCREEN, TCL_INDEX_NONE, offsetof(Frame, screenName),
270 TK_OPTION_NULL_OK, 0, 0},
271 {TK_OPTION_BOOLEAN, "-tile", "tile", "Tile",
272 DEF_FRAME_BG_TILE, TCL_INDEX_NONE, offsetof(Frame, tile), 0, 0, 0},
273 {TK_OPTION_STRING, "-use", "use", "Use",
274 DEF_TOPLEVEL_USE, TCL_INDEX_NONE, offsetof(Frame, useThis),
275 TK_OPTION_NULL_OK, 0, 0},
276 {TK_OPTION_END, NULL, NULL, NULL,
277 NULL, 0, 0, 0, commonOptSpec, 0}
278 };
279
280 static const Tk_OptionSpec labelframeOptSpec[] = {
281 {TK_OPTION_SYNONYM, "-bd", NULL, NULL,
282 NULL, 0, TCL_INDEX_NONE, 0, "-borderwidth", 0},
283 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
284 DEF_LABELFRAME_BORDER_WIDTH, TCL_INDEX_NONE, offsetof(Frame, borderWidth),
285 0, 0, 0},
286 {TK_OPTION_STRING, "-class", "class", "Class",
287 DEF_LABELFRAME_CLASS, TCL_INDEX_NONE, offsetof(Frame, className), 0, 0, 0},
288 {TK_OPTION_SYNONYM, "-fg", "foreground", NULL,
289 NULL, 0, TCL_INDEX_NONE, 0, "-foreground", 0},
290 {TK_OPTION_FONT, "-font", "font", "Font",
291 DEF_LABELFRAME_FONT, TCL_INDEX_NONE, offsetof(Labelframe, tkfont), 0, 0, 0},
292 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
293 DEF_LABELFRAME_FG, TCL_INDEX_NONE, offsetof(Labelframe, textColorPtr), 0, 0, 0},
294 {TK_OPTION_STRING_TABLE, "-labelanchor", "labelAnchor", "LabelAnchor",
295 DEF_LABELFRAME_LABELANCHOR, TCL_INDEX_NONE, offsetof(Labelframe, labelAnchor),
296 0, labelAnchorStrings, 0},
297 {TK_OPTION_WINDOW, "-labelwidget", "labelWidget", "LabelWidget",
298 NULL, TCL_INDEX_NONE, offsetof(Labelframe, labelWin), TK_OPTION_NULL_OK, 0, 0},
299 {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
300 DEF_LABELFRAME_RELIEF, TCL_INDEX_NONE, offsetof(Frame, relief), 0, 0, 0},
301 {TK_OPTION_STRING, "-text", "text", "Text",
302 DEF_LABELFRAME_TEXT, offsetof(Labelframe, textPtr), TCL_INDEX_NONE,
303 TK_OPTION_NULL_OK, 0, 0},
304 {TK_OPTION_END, NULL, NULL, NULL,
305 NULL, 0, 0, 0, commonOptSpec, 0}
306 };
307
308 /*
309 * Class names for widgets, indexed by FrameType.
310 */
311
312 static const char *const classNames[] = {"Frame", "Toplevel", "Labelframe"};
313
314 /*
315 * The following table maps from FrameType to the option template for that
316 * class of widgets.
317 */
318
319 static const Tk_OptionSpec *const optionSpecs[] = {
320 frameOptSpec,
321 toplevelOptSpec,
322 labelframeOptSpec,
323 };
324
325 /*
326 * Forward declarations for functions defined later in this file:
327 */
328
329 static void ComputeFrameGeometry(Frame *framePtr);
330 static int ConfigureFrame(Tcl_Interp *interp, Frame *framePtr,
331 int objc, Tcl_Obj *const objv[]);
332 static int CreateFrame(ClientData clientData, Tcl_Interp *interp,
333 int objc, Tcl_Obj *const argv[],
334 enum FrameType type, const char *appName);
335 static void DestroyFrame(void *memPtr);
336 static void DestroyFramePartly(Frame *framePtr);
337 static void DisplayFrame(ClientData clientData);
338 static void DrawFrameBackground(Tk_Window tkwin, Pixmap pixmap,
339 int highlightWidth, int borderWidth,
340 Tk_Image bgimg, int bgtile);
341 static void FrameBgImageProc(ClientData clientData,
342 int x, int y, int width, int height,
343 int imgWidth, int imgHeight);
344 static void FrameCmdDeletedProc(ClientData clientData);
345 static void FrameEventProc(ClientData clientData,
346 XEvent *eventPtr);
347 static void FrameLostContentProc(ClientData clientData,
348 Tk_Window tkwin);
349 static void FrameRequestProc(ClientData clientData,
350 Tk_Window tkwin);
351 static void FrameStructureProc(ClientData clientData,
352 XEvent *eventPtr);
353 static int FrameWidgetObjCmd(ClientData clientData,
354 Tcl_Interp *interp, int objc,
355 Tcl_Obj *const objv[]);
356 static void FrameWorldChanged(ClientData instanceData);
357 static void MapFrame(ClientData clientData);
358
359 /*
360 * The structure below defines frame class behavior by means of functions that
361 * can be invoked from generic window code.
362 */
363
364 static const Tk_ClassProcs frameClass = {
365 sizeof(Tk_ClassProcs), /* size */
366 FrameWorldChanged, /* worldChangedProc */
367 NULL, /* createProc */
368 NULL /* modalProc */
369 };
370
371 /*
372 * The structure below defines the official type record for the labelframe's
373 * geometry manager:
374 */
375
376 static const Tk_GeomMgr frameGeomType = {
377 "labelframe", /* name */
378 FrameRequestProc, /* requestProc */
379 FrameLostContentProc /* lostContentProc */
380 };
381
382 /*
383 *--------------------------------------------------------------
384 *
385 * Tk_FrameObjCmd, Tk_ToplevelObjCmd, Tk_LabelframeObjCmd --
386 *
387 * These functions are invoked to process the "frame", "toplevel" and
388 * "labelframe" Tcl commands. See the user documentation for details on
389 * what they do.
390 *
391 * Results:
392 * A standard Tcl result.
393 *
394 * Side effects:
395 * See the user documentation. These functions are just wrappers; they
396 * call CreateFrame to do all of the real work.
397 *
398 *--------------------------------------------------------------
399 */
400
401 int
Tk_FrameObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])402 Tk_FrameObjCmd(
403 ClientData clientData, /* Either NULL or pointer to option table. */
404 Tcl_Interp *interp, /* Current interpreter. */
405 int objc, /* Number of arguments. */
406 Tcl_Obj *const objv[]) /* Argument objects. */
407 {
408 return CreateFrame(clientData, interp, objc, objv, TYPE_FRAME, NULL);
409 }
410
411 int
Tk_ToplevelObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])412 Tk_ToplevelObjCmd(
413 ClientData clientData, /* Either NULL or pointer to option table. */
414 Tcl_Interp *interp, /* Current interpreter. */
415 int objc, /* Number of arguments. */
416 Tcl_Obj *const objv[]) /* Argument objects. */
417 {
418 return CreateFrame(clientData, interp, objc, objv, TYPE_TOPLEVEL, NULL);
419 }
420
421 int
Tk_LabelframeObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])422 Tk_LabelframeObjCmd(
423 ClientData clientData, /* Either NULL or pointer to option table. */
424 Tcl_Interp *interp, /* Current interpreter. */
425 int objc, /* Number of arguments. */
426 Tcl_Obj *const objv[]) /* Argument objects. */
427 {
428 return CreateFrame(clientData, interp, objc, objv, TYPE_LABELFRAME, NULL);
429 }
430
431 /*
432 *--------------------------------------------------------------
433 *
434 * TkCreateFrame --
435 *
436 * This function is the old command function for the "frame" and
437 * "toplevel" commands. Now it is used directly by Tk_Init to create a
438 * new main window. See the user documentation for the "frame" and
439 * "toplevel" commands for details on what it does.
440 *
441 * Results:
442 * A standard Tcl result.
443 *
444 * Side effects:
445 * See the user documentation.
446 *
447 *--------------------------------------------------------------
448 */
449
450 int
TkCreateFrame(ClientData clientData,Tcl_Interp * interp,int argc,const char * const * argv,int toplevel,const char * appName)451 TkCreateFrame(
452 ClientData clientData, /* Either NULL or pointer to option table. */
453 Tcl_Interp *interp, /* Current interpreter. */
454 int argc, /* Number of arguments. */
455 const char *const *argv, /* Argument strings. */
456 int toplevel, /* Non-zero means create a toplevel window,
457 * zero means create a frame. */
458 const char *appName) /* Should only be non-NULL if there is no main
459 * window associated with the interpreter.
460 * Gives the base name to use for the new
461 * application. */
462 {
463 int result, i;
464 Tcl_Obj **objv = (Tcl_Obj **)ckalloc((argc+1) * sizeof(Tcl_Obj **));
465
466 for (i=0; i<argc; i++) {
467 objv[i] = Tcl_NewStringObj(argv[i], -1);
468 Tcl_IncrRefCount(objv[i]);
469 }
470 objv[argc] = NULL;
471 result = CreateFrame(clientData, interp, argc, objv,
472 toplevel ? TYPE_TOPLEVEL : TYPE_FRAME, appName);
473 for (i=0; i<argc; i++) {
474 Tcl_DecrRefCount(objv[i]);
475 }
476 ckfree(objv);
477 return result;
478 }
479
480 int
TkListCreateFrame(ClientData clientData,Tcl_Interp * interp,Tcl_Obj * listObj,int toplevel,Tcl_Obj * nameObj)481 TkListCreateFrame(
482 ClientData clientData, /* Either NULL or pointer to option table. */
483 Tcl_Interp *interp, /* Current interpreter. */
484 Tcl_Obj *listObj, /* List of arguments. */
485 int toplevel, /* Non-zero means create a toplevel window,
486 * zero means create a frame. */
487 Tcl_Obj *nameObj) /* Should only be non-NULL if there is no main
488 * window associated with the interpreter.
489 * Gives the base name to use for the new
490 * application. */
491 {
492 int objc;
493 Tcl_Obj **objv;
494
495 if (TCL_OK != Tcl_ListObjGetElements(interp, listObj, &objc, &objv)) {
496 return TCL_ERROR;
497 }
498 return CreateFrame(clientData, interp, objc, objv,
499 toplevel ? TYPE_TOPLEVEL : TYPE_FRAME,
500 nameObj ? Tcl_GetString(nameObj) : NULL);
501 }
502
503 static int
CreateFrame(ClientData dummy,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[],enum FrameType type,const char * appName)504 CreateFrame(
505 ClientData dummy, /* NULL. */
506 Tcl_Interp *interp, /* Current interpreter. */
507 int objc, /* Number of arguments. */
508 Tcl_Obj *const objv[], /* Argument objects. */
509 enum FrameType type, /* What widget type to create. */
510 const char *appName) /* Should only be non-NULL if there are no
511 * Main window associated with the
512 * interpreter. Gives the base name to use for
513 * the new application. */
514 {
515 Tk_Window tkwin;
516 Frame *framePtr;
517 Tk_OptionTable optionTable;
518 Tk_Window newWin;
519 const char *className, *screenName, *visualName, *colormapName;
520 const char *arg, *useOption;
521 int i, depth;
522 TkSizeT length;
523 unsigned int mask;
524 Colormap colormap;
525 Visual *visual;
526 (void)dummy;
527
528 if (objc < 2) {
529 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?");
530 return TCL_ERROR;
531 }
532
533 /*
534 * Create the option table for this widget class. If it has already been
535 * created, the cached pointer will be returned.
536 */
537
538 optionTable = Tk_CreateOptionTable(interp, optionSpecs[type]);
539
540 /*
541 * Pre-process the argument list. Scan through it to find any "-class",
542 * "-screen", "-visual", and "-colormap" options. These arguments need to
543 * be processed specially, before the window is configured using the usual
544 * Tk mechanisms.
545 */
546
547 className = colormapName = screenName = visualName = useOption = NULL;
548 colormap = None;
549 for (i = 2; i < objc; i += 2) {
550 arg = Tcl_GetStringFromObj(objv[i], &length);
551 if (length < 2) {
552 continue;
553 }
554 if ((arg[1] == 'c') && (length >= 3)
555 && (strncmp(arg, "-class", length) == 0)) {
556 className = Tcl_GetString(objv[i+1]);
557 } else if ((arg[1] == 'c') && (length >= 3)
558 && (strncmp(arg, "-colormap", length) == 0)) {
559 colormapName = Tcl_GetString(objv[i+1]);
560 } else if ((arg[1] == 's') && (type == TYPE_TOPLEVEL)
561 && (strncmp(arg, "-screen", length) == 0)) {
562 screenName = Tcl_GetString(objv[i+1]);
563 } else if ((arg[1] == 'u') && (type == TYPE_TOPLEVEL)
564 && (strncmp(arg, "-use", length) == 0)) {
565 useOption = Tcl_GetString(objv[i+1]);
566 } else if ((arg[1] == 'v')
567 && (strncmp(arg, "-visual", length) == 0)) {
568 visualName = Tcl_GetString(objv[i+1]);
569 }
570 }
571
572 /*
573 * Create the window, and deal with the special options -use, -classname,
574 * -colormap, -screenname, and -visual. These options must be handle
575 * before calling ConfigureFrame below, and they must also be processed in
576 * a particular order, for the following reasons:
577 * 1. Must set the window's class before calling ConfigureFrame, so that
578 * unspecified options are looked up in the option database using the
579 * correct class.
580 * 2. Must set visual information before calling ConfigureFrame so that
581 * colors are allocated in a proper colormap.
582 * 3. Must call TkpUseWindow before setting non-default visual
583 * information, since TkpUseWindow changes the defaults.
584 */
585
586 if (screenName == NULL) {
587 screenName = (type == TYPE_TOPLEVEL) ? "" : NULL;
588 }
589
590 /*
591 * Main window associated with interpreter. If we're called by Tk_Init to
592 * create a new application, then this is NULL.
593 */
594
595 tkwin = Tk_MainWindow(interp);
596 if (tkwin != NULL) {
597 newWin = Tk_CreateWindowFromPath(interp, tkwin, Tcl_GetString(objv[1]),
598 screenName);
599 } else if (appName == NULL) {
600 /*
601 * This occurs when someone tried to create a frame/toplevel while we
602 * are being destroyed. Let an error be thrown.
603 */
604
605 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
606 "unable to create widget \"%s\"", Tcl_GetString(objv[1])));
607 Tcl_SetErrorCode(interp, "TK", "APPLICATION_GONE", NULL);
608 return TCL_ERROR;
609 } else {
610 /*
611 * We were called from Tk_Init; create a new application.
612 */
613
614 newWin = TkCreateMainWindow(interp, screenName, appName);
615 }
616 if (newWin == NULL) {
617 goto error;
618 }
619
620 /*
621 * Mark Tk frames as suitable candidates for [wm manage].
622 */
623
624 ((TkWindow *) newWin)->flags |= TK_WM_MANAGEABLE;
625
626 if (className == NULL) {
627 className = Tk_GetOption(newWin, "class", "Class");
628 if (className == NULL) {
629 className = classNames[type];
630 }
631 }
632 Tk_SetClass(newWin, className);
633 if (useOption == NULL) {
634 useOption = Tk_GetOption(newWin, "use", "Use");
635 }
636 if ((useOption != NULL) && (*useOption != 0)
637 && (TkpUseWindow(interp, newWin, useOption) != TCL_OK)) {
638 goto error;
639 }
640 if (visualName == NULL) {
641 visualName = Tk_GetOption(newWin, "visual", "Visual");
642 }
643 if (colormapName == NULL) {
644 colormapName = Tk_GetOption(newWin, "colormap", "Colormap");
645 }
646 if ((colormapName != NULL) && (*colormapName == 0)) {
647 colormapName = NULL;
648 }
649 if (visualName != NULL) {
650 visual = Tk_GetVisual(interp, newWin, visualName, &depth,
651 (colormapName == NULL) ? &colormap : NULL);
652 if (visual == NULL) {
653 goto error;
654 }
655 Tk_SetWindowVisual(newWin, visual, depth, colormap);
656 }
657 if (colormapName != NULL) {
658 colormap = Tk_GetColormap(interp, newWin, colormapName);
659 if (colormap == None) {
660 goto error;
661 }
662 Tk_SetWindowColormap(newWin, colormap);
663 }
664
665 /*
666 * For top-level windows, provide an initial geometry request of 200x200,
667 * just so the window looks nicer on the screen if it doesn't request a
668 * size for itself.
669 */
670
671 if (type == TYPE_TOPLEVEL) {
672 Tk_GeometryRequest(newWin, 200, 200);
673 }
674
675 /*
676 * Create the widget record, process configuration options, and create
677 * event handlers. Then fill in a few additional fields in the widget
678 * record from the special options.
679 */
680
681 if (type == TYPE_LABELFRAME) {
682 framePtr = (Frame *)ckalloc(sizeof(Labelframe));
683 memset(framePtr, 0, sizeof(Labelframe));
684 } else {
685 framePtr = (Frame *)ckalloc(sizeof(Frame));
686 memset(framePtr, 0, sizeof(Frame));
687 }
688 framePtr->tkwin = newWin;
689 framePtr->display = Tk_Display(newWin);
690 framePtr->interp = interp;
691 framePtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(newWin),
692 FrameWidgetObjCmd, framePtr, FrameCmdDeletedProc);
693 framePtr->optionTable = optionTable;
694 framePtr->type = type;
695 framePtr->colormap = colormap;
696 framePtr->relief = TK_RELIEF_FLAT;
697 framePtr->cursor = NULL;
698
699 if (framePtr->type == TYPE_LABELFRAME) {
700 Labelframe *labelframePtr = (Labelframe *) framePtr;
701
702 labelframePtr->labelAnchor = LABELANCHOR_NW;
703 labelframePtr->textGC = NULL;
704 }
705
706 /*
707 * Store backreference to frame widget in window structure.
708 */
709
710 Tk_SetClassProcs(newWin, &frameClass, framePtr);
711
712 mask = ExposureMask | StructureNotifyMask | FocusChangeMask;
713 if (type == TYPE_TOPLEVEL) {
714 mask |= ActivateMask;
715 }
716 Tk_CreateEventHandler(newWin, mask, FrameEventProc, framePtr);
717 if ((Tk_InitOptions(interp, framePtr, optionTable, newWin)
718 != TCL_OK) ||
719 (ConfigureFrame(interp, framePtr, objc-2, objv+2) != TCL_OK)) {
720 goto error;
721 }
722 if (framePtr->isContainer) {
723 if (framePtr->useThis != NULL) {
724 Tcl_SetObjResult(interp, Tcl_NewStringObj(
725 "windows cannot have both the -use and the -container"
726 " option set", -1));
727 Tcl_SetErrorCode(interp, "TK", "FRAME", "CONTAINMENT", NULL);
728 goto error;
729 }
730 TkpMakeContainer(framePtr->tkwin);
731 }
732 if (type == TYPE_TOPLEVEL) {
733 Tcl_DoWhenIdle(MapFrame, framePtr);
734 }
735 Tcl_SetObjResult(interp, Tk_NewWindowObj(newWin));
736 return TCL_OK;
737
738 error:
739 if (newWin != NULL) {
740 Tk_DestroyWindow(newWin);
741 }
742 return TCL_ERROR;
743 }
744
745 /*
746 *--------------------------------------------------------------
747 *
748 * FrameWidgetObjCmd --
749 *
750 * This function is invoked to process the Tcl command that corresponds
751 * to a frame widget. See the user documentation for details on what it
752 * does.
753 *
754 * Results:
755 * A standard Tcl result.
756 *
757 * Side effects:
758 * See the user documentation.
759 *
760 *--------------------------------------------------------------
761 */
762
763 static int
FrameWidgetObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])764 FrameWidgetObjCmd(
765 ClientData clientData, /* Information about frame widget. */
766 Tcl_Interp *interp, /* Current interpreter. */
767 int objc, /* Number of arguments. */
768 Tcl_Obj *const objv[]) /* Argument objects. */
769 {
770 static const char *const frameOptions[] = {
771 "cget", "configure", NULL
772 };
773 enum options {
774 FRAME_CGET, FRAME_CONFIGURE
775 };
776 Frame *framePtr = (Frame *)clientData;
777 int result = TCL_OK, index;
778 int c, i;
779 TkSizeT length;
780 Tcl_Obj *objPtr;
781
782 if (objc < 2) {
783 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
784 return TCL_ERROR;
785 }
786 if (Tcl_GetIndexFromObjStruct(interp, objv[1], frameOptions,
787 sizeof(char *), "option", 0, &index) != TCL_OK) {
788 return TCL_ERROR;
789 }
790 Tcl_Preserve(framePtr);
791 switch ((enum options) index) {
792 case FRAME_CGET:
793 if (objc != 3) {
794 Tcl_WrongNumArgs(interp, 2, objv, "option");
795 result = TCL_ERROR;
796 goto done;
797 }
798 objPtr = Tk_GetOptionValue(interp, framePtr,
799 framePtr->optionTable, objv[2], framePtr->tkwin);
800 if (objPtr == NULL) {
801 result = TCL_ERROR;
802 goto done;
803 }
804 Tcl_SetObjResult(interp, objPtr);
805 break;
806 case FRAME_CONFIGURE:
807 if (objc <= 3) {
808 objPtr = Tk_GetOptionInfo(interp, framePtr,
809 framePtr->optionTable, (objc == 3) ? objv[2] : NULL,
810 framePtr->tkwin);
811 if (objPtr == NULL) {
812 result = TCL_ERROR;
813 goto done;
814 }
815 Tcl_SetObjResult(interp, objPtr);
816 } else {
817 /*
818 * Don't allow the options -class, -colormap, -container, -screen,
819 * -use, or -visual to be changed.
820 */
821
822 for (i = 2; i < objc; i++) {
823 const char *arg = Tcl_GetStringFromObj(objv[i], &length);
824
825 if (length < 2) {
826 continue;
827 }
828 c = arg[1];
829 if (((c == 'c') && (length >= 2)
830 && (strncmp(arg, "-class", length) == 0))
831 || ((c == 'c') && (length >= 3)
832 && (strncmp(arg, "-colormap", length) == 0))
833 || ((c == 'c') && (length >= 3)
834 && (strncmp(arg, "-container", length) == 0))
835 || ((c == 's') && (framePtr->type == TYPE_TOPLEVEL)
836 && (strncmp(arg, "-screen", length) == 0))
837 || ((c == 'u') && (framePtr->type == TYPE_TOPLEVEL)
838 && (strncmp(arg, "-use", length) == 0))
839 || ((c == 'v')
840 && (strncmp(arg, "-visual", length) == 0))) {
841
842 #ifdef _WIN32
843 if (c == 'u') {
844 const char *string = Tcl_GetString(objv[i+1]);
845
846 if (TkpUseWindow(interp, framePtr->tkwin,
847 string) != TCL_OK) {
848 result = TCL_ERROR;
849 goto done;
850 }
851 continue;
852 }
853 #endif
854 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
855 "can't modify %s option after widget is created",
856 arg));
857 Tcl_SetErrorCode(interp, "TK", "FRAME", "CREATE_ONLY",
858 NULL);
859 result = TCL_ERROR;
860 goto done;
861 }
862 }
863 result = ConfigureFrame(interp, framePtr, objc-2, objv+2);
864 }
865 break;
866 }
867
868 done:
869 Tcl_Release(framePtr);
870 return result;
871 }
872
873 /*
874 *----------------------------------------------------------------------
875 *
876 * DestroyFrame --
877 *
878 * This function is invoked by Tcl_EventuallyFree or Tcl_Release to clean
879 * up the internal structure of a frame at a safe time (when no-one is
880 * using it anymore).
881 *
882 * Results:
883 * None.
884 *
885 * Side effects:
886 * Everything associated with the frame is freed up.
887 *
888 *----------------------------------------------------------------------
889 */
890
891 static void
DestroyFrame(void * memPtr)892 DestroyFrame(
893 void *memPtr) /* Info about frame widget. */
894 {
895 Frame *framePtr = (Frame *)memPtr;
896 Labelframe *labelframePtr = (Labelframe *)memPtr;
897
898 if (framePtr->type == TYPE_LABELFRAME) {
899 Tk_FreeTextLayout(labelframePtr->textLayout);
900 if (labelframePtr->textGC != NULL) {
901 Tk_FreeGC(framePtr->display, labelframePtr->textGC);
902 }
903 }
904 #ifndef TK_NO_DOUBLE_BUFFERING
905 if (framePtr->copyGC != NULL) {
906 Tk_FreeGC(framePtr->display, framePtr->copyGC);
907 }
908 #endif /* TK_NO_DOUBLE_BUFFERING */
909 if (framePtr->colormap != None) {
910 Tk_FreeColormap(framePtr->display, framePtr->colormap);
911 }
912 if (framePtr->bgimg) {
913 Tk_FreeImage(framePtr->bgimg);
914 }
915 ckfree(framePtr);
916 }
917
918 /*
919 *----------------------------------------------------------------------
920 *
921 * DestroyFramePartly --
922 *
923 * This function is invoked to clean up everything that needs tkwin to be
924 * defined when deleted. During the destruction process tkwin is always
925 * set to NULL and this function must be called before that happens.
926 *
927 * Results:
928 * None.
929 *
930 * Side effects:
931 * Some things associated with the frame are freed up.
932 *
933 *----------------------------------------------------------------------
934 */
935
936 static void
DestroyFramePartly(Frame * framePtr)937 DestroyFramePartly(
938 Frame *framePtr) /* Info about frame widget. */
939 {
940 Labelframe *labelframePtr = (Labelframe *) framePtr;
941
942 if (framePtr->type == TYPE_LABELFRAME && labelframePtr->labelWin != NULL) {
943 Tk_DeleteEventHandler(labelframePtr->labelWin, StructureNotifyMask,
944 FrameStructureProc, framePtr);
945 Tk_ManageGeometry(labelframePtr->labelWin, NULL, NULL);
946 if (framePtr->tkwin != Tk_Parent(labelframePtr->labelWin)) {
947 Tk_UnmaintainGeometry(labelframePtr->labelWin, framePtr->tkwin);
948 }
949 Tk_UnmapWindow(labelframePtr->labelWin);
950 labelframePtr->labelWin = NULL;
951 }
952
953 Tk_FreeConfigOptions((char *) framePtr, framePtr->optionTable,
954 framePtr->tkwin);
955 }
956
957 /*
958 *----------------------------------------------------------------------
959 *
960 * ConfigureFrame --
961 *
962 * This function is called to process an objv/objc list, plus the Tk
963 * option database, in order to configure (or reconfigure) a frame
964 * widget.
965 *
966 * Results:
967 * The return value is a standard Tcl result. If TCL_ERROR is returned,
968 * then the interp's result contains an error message.
969 *
970 * Side effects:
971 * Configuration information, such as text string, colors, font, etc. get
972 * set for framePtr; old resources get freed, if there were any.
973 *
974 *----------------------------------------------------------------------
975 */
976
977 static int
ConfigureFrame(Tcl_Interp * interp,Frame * framePtr,int objc,Tcl_Obj * const objv[])978 ConfigureFrame(
979 Tcl_Interp *interp, /* Used for error reporting. */
980 Frame *framePtr, /* Information about widget; may or may not
981 * already have values for some fields. */
982 int objc, /* Number of valid entries in objv. */
983 Tcl_Obj *const objv[]) /* Arguments. */
984 {
985 Tk_SavedOptions savedOptions;
986 char *oldMenuName;
987 Tk_Window oldWindow = NULL;
988 Labelframe *labelframePtr = (Labelframe *) framePtr;
989 Tk_Image image = NULL;
990
991 /*
992 * Need the old menubar name for the menu code to delete it.
993 */
994
995 if (framePtr->menuName == NULL) {
996 oldMenuName = NULL;
997 } else {
998 oldMenuName = (char *)ckalloc(strlen(framePtr->menuName) + 1);
999 strcpy(oldMenuName, framePtr->menuName);
1000 }
1001
1002 if (framePtr->type == TYPE_LABELFRAME) {
1003 oldWindow = labelframePtr->labelWin;
1004 }
1005 if (Tk_SetOptions(interp, framePtr,
1006 framePtr->optionTable, objc, objv,
1007 framePtr->tkwin, &savedOptions, NULL) != TCL_OK) {
1008 if (oldMenuName != NULL) {
1009 ckfree(oldMenuName);
1010 }
1011 return TCL_ERROR;
1012 }
1013
1014 if (framePtr->bgimgPtr) {
1015 image = Tk_GetImage(interp, framePtr->tkwin,
1016 Tcl_GetString(framePtr->bgimgPtr), FrameBgImageProc, framePtr);
1017 if (image == NULL) {
1018 Tk_RestoreSavedOptions(&savedOptions);
1019 return TCL_ERROR;
1020 }
1021 }
1022 if (framePtr->bgimg) {
1023 Tk_FreeImage(framePtr->bgimg);
1024 }
1025 framePtr->bgimg = image;
1026
1027 Tk_FreeSavedOptions(&savedOptions);
1028
1029 /*
1030 * A few of the options require additional processing.
1031 */
1032
1033 if ((((oldMenuName == NULL) && (framePtr->menuName != NULL))
1034 || ((oldMenuName != NULL) && (framePtr->menuName == NULL))
1035 || ((oldMenuName != NULL) && (framePtr->menuName != NULL)
1036 && strcmp(oldMenuName, framePtr->menuName) != 0))
1037 && framePtr->type == TYPE_TOPLEVEL) {
1038 TkSetWindowMenuBar(interp, framePtr->tkwin, oldMenuName,
1039 framePtr->menuName);
1040 }
1041
1042 if (oldMenuName != NULL) {
1043 ckfree(oldMenuName);
1044 }
1045
1046 if (framePtr->border != NULL) {
1047 Tk_SetBackgroundFromBorder(framePtr->tkwin, framePtr->border);
1048 } else {
1049 Tk_SetWindowBackgroundPixmap(framePtr->tkwin, None);
1050 }
1051
1052 if (framePtr->highlightWidth < 0) {
1053 framePtr->highlightWidth = 0;
1054 }
1055 if (framePtr->padX < 0) {
1056 framePtr->padX = 0;
1057 }
1058 if (framePtr->padY < 0) {
1059 framePtr->padY = 0;
1060 }
1061
1062 /*
1063 * If a -labelwidget is specified, check that it is valid and set up
1064 * geometry management for it.
1065 */
1066
1067 if (framePtr->type == TYPE_LABELFRAME) {
1068 if (oldWindow != labelframePtr->labelWin) {
1069 if (oldWindow != NULL) {
1070 Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
1071 FrameStructureProc, framePtr);
1072 Tk_ManageGeometry(oldWindow, NULL, NULL);
1073 Tk_UnmaintainGeometry(oldWindow, framePtr->tkwin);
1074 Tk_UnmapWindow(oldWindow);
1075 }
1076 if (labelframePtr->labelWin != NULL) {
1077 Tk_Window ancestor, parent, sibling = NULL;
1078
1079 /*
1080 * Make sure that the frame is either the parent of the window
1081 * used as label or a descendant of that parent. Also, don't
1082 * allow a top-level window to be managed inside the frame.
1083 */
1084
1085 parent = Tk_Parent(labelframePtr->labelWin);
1086 for (ancestor = framePtr->tkwin; ;
1087 ancestor = Tk_Parent(ancestor)) {
1088 if (ancestor == parent) {
1089 break;
1090 }
1091 sibling = ancestor;
1092 if (Tk_IsTopLevel(ancestor)) {
1093 goto badLabelWindow;
1094 }
1095 }
1096 if (Tk_IsTopLevel(labelframePtr->labelWin)) {
1097 goto badLabelWindow;
1098 }
1099 if (labelframePtr->labelWin == framePtr->tkwin) {
1100 goto badLabelWindow;
1101 }
1102 Tk_CreateEventHandler(labelframePtr->labelWin,
1103 StructureNotifyMask, FrameStructureProc, framePtr);
1104 Tk_ManageGeometry(labelframePtr->labelWin, &frameGeomType,
1105 framePtr);
1106
1107 /*
1108 * If the frame is not parent to the label, make sure the
1109 * label is above its sibling in the stacking order.
1110 */
1111
1112 if (sibling != NULL) {
1113 Tk_RestackWindow(labelframePtr->labelWin, Above, sibling);
1114 }
1115 }
1116 }
1117 }
1118
1119 FrameWorldChanged(framePtr);
1120 return TCL_OK;
1121
1122 badLabelWindow:
1123 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1124 "can't use %s as label in this frame",
1125 Tk_PathName(labelframePtr->labelWin)));
1126 Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "HIERARCHY", NULL);
1127 labelframePtr->labelWin = NULL;
1128 return TCL_ERROR;
1129 }
1130
1131 /*
1132 *---------------------------------------------------------------------------
1133 *
1134 * FrameWorldChanged --
1135 *
1136 * This function is called when the world has changed in some way and the
1137 * widget needs to recompute all its graphics contexts and determine its
1138 * new geometry.
1139 *
1140 * Results:
1141 * None.
1142 *
1143 * Side effects:
1144 * Frame will be relayed out and redisplayed.
1145 *
1146 *---------------------------------------------------------------------------
1147 */
1148
1149 static void
FrameWorldChanged(ClientData instanceData)1150 FrameWorldChanged(
1151 ClientData instanceData) /* Information about widget. */
1152 {
1153 Frame *framePtr = (Frame *)instanceData;
1154 Labelframe *labelframePtr = (Labelframe *)instanceData;
1155 Tk_Window tkwin = framePtr->tkwin;
1156 XGCValues gcValues;
1157 GC gc;
1158 int anyTextLabel, anyWindowLabel;
1159 int bWidthLeft, bWidthRight, bWidthTop, bWidthBottom;
1160 const char *labelText;
1161
1162 anyTextLabel = (framePtr->type == TYPE_LABELFRAME) &&
1163 (labelframePtr->textPtr != NULL) &&
1164 (labelframePtr->labelWin == NULL);
1165 anyWindowLabel = (framePtr->type == TYPE_LABELFRAME) &&
1166 (labelframePtr->labelWin != NULL);
1167
1168 #ifndef TK_NO_DOUBLE_BUFFERING
1169 gcValues.graphics_exposures = False;
1170 gc = Tk_GetGC(tkwin, GCGraphicsExposures, &gcValues);
1171 if (framePtr->copyGC != NULL) {
1172 Tk_FreeGC(framePtr->display, framePtr->copyGC);
1173 }
1174 framePtr->copyGC = gc;
1175 #endif /* TK_NO_DOUBLE_BUFFERING */
1176
1177 if (framePtr->type == TYPE_LABELFRAME) {
1178 /*
1179 * The textGC is needed even in the labelWin case, so it's always
1180 * created for a labelframe.
1181 */
1182
1183 gcValues.font = Tk_FontId(labelframePtr->tkfont);
1184 gcValues.foreground = labelframePtr->textColorPtr->pixel;
1185 gcValues.graphics_exposures = False;
1186 gc = Tk_GetGC(tkwin, GCForeground | GCFont | GCGraphicsExposures,
1187 &gcValues);
1188 if (labelframePtr->textGC != NULL) {
1189 Tk_FreeGC(framePtr->display, labelframePtr->textGC);
1190 }
1191 labelframePtr->textGC = gc;
1192
1193 /*
1194 * Calculate label size.
1195 */
1196
1197 labelframePtr->labelReqWidth = labelframePtr->labelReqHeight = 0;
1198
1199 if (anyTextLabel) {
1200 labelText = Tcl_GetString(labelframePtr->textPtr);
1201 Tk_FreeTextLayout(labelframePtr->textLayout);
1202 labelframePtr->textLayout =
1203 Tk_ComputeTextLayout(labelframePtr->tkfont,
1204 labelText, -1, 0, TK_JUSTIFY_CENTER, 0,
1205 &labelframePtr->labelReqWidth,
1206 &labelframePtr->labelReqHeight);
1207 labelframePtr->labelReqWidth += 2 * LABELSPACING;
1208 labelframePtr->labelReqHeight += 2 * LABELSPACING;
1209 } else if (anyWindowLabel) {
1210 labelframePtr->labelReqWidth = Tk_ReqWidth(labelframePtr->labelWin);
1211 labelframePtr->labelReqHeight =
1212 Tk_ReqHeight(labelframePtr->labelWin);
1213 }
1214
1215 /*
1216 * Make sure label size is at least as big as the border. This
1217 * simplifies later calculations and gives a better appearance with
1218 * thick borders.
1219 */
1220
1221 if ((labelframePtr->labelAnchor >= LABELANCHOR_N) &&
1222 (labelframePtr->labelAnchor <= LABELANCHOR_SW)) {
1223 if (labelframePtr->labelReqHeight < framePtr->borderWidth) {
1224 labelframePtr->labelReqHeight = framePtr->borderWidth;
1225 }
1226 } else {
1227 if (labelframePtr->labelReqWidth < framePtr->borderWidth) {
1228 labelframePtr->labelReqWidth = framePtr->borderWidth;
1229 }
1230 }
1231 }
1232
1233 /*
1234 * Calculate individual border widths.
1235 */
1236
1237 bWidthBottom = bWidthTop = bWidthRight = bWidthLeft =
1238 framePtr->borderWidth + framePtr->highlightWidth;
1239
1240 bWidthLeft += framePtr->padX;
1241 bWidthRight += framePtr->padX;
1242 bWidthTop += framePtr->padY;
1243 bWidthBottom += framePtr->padY;
1244
1245 if (anyTextLabel || anyWindowLabel) {
1246 switch (labelframePtr->labelAnchor) {
1247 case LABELANCHOR_E:
1248 case LABELANCHOR_EN:
1249 case LABELANCHOR_ES:
1250 bWidthRight += labelframePtr->labelReqWidth -
1251 framePtr->borderWidth;
1252 break;
1253 case LABELANCHOR_N:
1254 case LABELANCHOR_NE:
1255 case LABELANCHOR_NW:
1256 bWidthTop += labelframePtr->labelReqHeight - framePtr->borderWidth;
1257 break;
1258 case LABELANCHOR_S:
1259 case LABELANCHOR_SE:
1260 case LABELANCHOR_SW:
1261 bWidthBottom += labelframePtr->labelReqHeight -
1262 framePtr->borderWidth;
1263 break;
1264 default:
1265 bWidthLeft += labelframePtr->labelReqWidth - framePtr->borderWidth;
1266 break;
1267 }
1268 }
1269
1270 Tk_SetInternalBorderEx(tkwin, bWidthLeft, bWidthRight, bWidthTop,
1271 bWidthBottom);
1272
1273 ComputeFrameGeometry(framePtr);
1274
1275 /*
1276 * A labelframe should request size for its label.
1277 */
1278
1279 if (framePtr->type == TYPE_LABELFRAME) {
1280 int minwidth = labelframePtr->labelReqWidth;
1281 int minheight = labelframePtr->labelReqHeight;
1282 int padding = framePtr->highlightWidth;
1283
1284 if (framePtr->borderWidth > 0) {
1285 padding += framePtr->borderWidth + LABELMARGIN;
1286 }
1287 padding *= 2;
1288 if ((labelframePtr->labelAnchor >= LABELANCHOR_N) &&
1289 (labelframePtr->labelAnchor <= LABELANCHOR_SW)) {
1290 minwidth += padding;
1291 minheight += framePtr->borderWidth + framePtr->highlightWidth;
1292 } else {
1293 minheight += padding;
1294 minwidth += framePtr->borderWidth + framePtr->highlightWidth;
1295 }
1296 Tk_SetMinimumRequestSize(tkwin, minwidth, minheight);
1297 }
1298
1299 if ((framePtr->width > 0) || (framePtr->height > 0)) {
1300 Tk_GeometryRequest(tkwin, framePtr->width, framePtr->height);
1301 }
1302
1303 if (Tk_IsMapped(tkwin)) {
1304 if (!(framePtr->flags & REDRAW_PENDING)) {
1305 Tcl_DoWhenIdle(DisplayFrame, framePtr);
1306 }
1307 framePtr->flags |= REDRAW_PENDING;
1308 }
1309 }
1310
1311 /*
1312 *----------------------------------------------------------------------
1313 *
1314 * ComputeFrameGeometry --
1315 *
1316 * This function is called to compute various geometrical information for
1317 * a frame, such as where various things get displayed. It's called when
1318 * the window is reconfigured.
1319 *
1320 * Results:
1321 * None.
1322 *
1323 * Side effects:
1324 * Display-related numbers get changed in *framePtr.
1325 *
1326 *----------------------------------------------------------------------
1327 */
1328
1329 static void
ComputeFrameGeometry(Frame * framePtr)1330 ComputeFrameGeometry(
1331 Frame *framePtr) /* Information about widget. */
1332 {
1333 int otherWidth, otherHeight, otherWidthT, otherHeightT, padding;
1334 int maxWidth, maxHeight;
1335 Tk_Window tkwin;
1336 Labelframe *labelframePtr = (Labelframe *) framePtr;
1337
1338 /*
1339 * We have nothing to do here unless there is a label.
1340 */
1341
1342 if (framePtr->type != TYPE_LABELFRAME) {
1343 return;
1344 }
1345 if (labelframePtr->textPtr == NULL && labelframePtr->labelWin == NULL) {
1346 return;
1347 }
1348
1349 tkwin = framePtr->tkwin;
1350
1351 /*
1352 * Calculate the available size for the label
1353 */
1354
1355 labelframePtr->labelBox.width = labelframePtr->labelReqWidth;
1356 labelframePtr->labelBox.height = labelframePtr->labelReqHeight;
1357
1358 padding = framePtr->highlightWidth;
1359 if (framePtr->borderWidth > 0) {
1360 padding += framePtr->borderWidth + LABELMARGIN;
1361 }
1362 padding *= 2;
1363
1364 maxHeight = Tk_Height(tkwin);
1365 maxWidth = Tk_Width(tkwin);
1366
1367 if ((labelframePtr->labelAnchor >= LABELANCHOR_N) &&
1368 (labelframePtr->labelAnchor <= LABELANCHOR_SW)) {
1369 maxWidth -= padding;
1370 if (maxWidth < 1) {
1371 maxWidth = 1;
1372 }
1373 } else {
1374 maxHeight -= padding;
1375 if (maxHeight < 1) {
1376 maxHeight = 1;
1377 }
1378 }
1379 if (labelframePtr->labelBox.width > maxWidth) {
1380 labelframePtr->labelBox.width = maxWidth;
1381 }
1382 if (labelframePtr->labelBox.height > maxHeight) {
1383 labelframePtr->labelBox.height = maxHeight;
1384 }
1385
1386 /*
1387 * Calculate label and text position. The text's position is based on the
1388 * requested size (= the text's real size) to get proper alignment if the
1389 * text does not fit.
1390 */
1391
1392 otherWidth = Tk_Width(tkwin) - labelframePtr->labelBox.width;
1393 otherHeight = Tk_Height(tkwin) - labelframePtr->labelBox.height;
1394 otherWidthT = Tk_Width(tkwin) - labelframePtr->labelReqWidth;
1395 otherHeightT = Tk_Height(tkwin) - labelframePtr->labelReqHeight;
1396 padding = framePtr->highlightWidth;
1397
1398 switch (labelframePtr->labelAnchor) {
1399 case LABELANCHOR_E:
1400 case LABELANCHOR_EN:
1401 case LABELANCHOR_ES:
1402 labelframePtr->labelTextX = otherWidthT - padding;
1403 labelframePtr->labelBox.x = otherWidth - padding;
1404 break;
1405 case LABELANCHOR_N:
1406 case LABELANCHOR_NE:
1407 case LABELANCHOR_NW:
1408 labelframePtr->labelTextY = padding;
1409 labelframePtr->labelBox.y = padding;
1410 break;
1411 case LABELANCHOR_S:
1412 case LABELANCHOR_SE:
1413 case LABELANCHOR_SW:
1414 labelframePtr->labelTextY = otherHeightT - padding;
1415 labelframePtr->labelBox.y = otherHeight - padding;
1416 break;
1417 default:
1418 labelframePtr->labelTextX = padding;
1419 labelframePtr->labelBox.x = padding;
1420 break;
1421 }
1422
1423 if (framePtr->borderWidth > 0) {
1424 padding += framePtr->borderWidth + LABELMARGIN;
1425 }
1426
1427 switch (labelframePtr->labelAnchor) {
1428 case LABELANCHOR_NW:
1429 case LABELANCHOR_SW:
1430 labelframePtr->labelTextX = padding;
1431 labelframePtr->labelBox.x = padding;
1432 break;
1433 case LABELANCHOR_N:
1434 case LABELANCHOR_S:
1435 labelframePtr->labelTextX = otherWidthT / 2;
1436 labelframePtr->labelBox.x = otherWidth / 2;
1437 break;
1438 case LABELANCHOR_NE:
1439 case LABELANCHOR_SE:
1440 labelframePtr->labelTextX = otherWidthT - padding;
1441 labelframePtr->labelBox.x = otherWidth - padding;
1442 break;
1443 case LABELANCHOR_EN:
1444 case LABELANCHOR_WN:
1445 labelframePtr->labelTextY = padding;
1446 labelframePtr->labelBox.y = padding;
1447 break;
1448 case LABELANCHOR_E:
1449 case LABELANCHOR_W:
1450 labelframePtr->labelTextY = otherHeightT / 2;
1451 labelframePtr->labelBox.y = otherHeight / 2;
1452 break;
1453 default:
1454 labelframePtr->labelTextY = otherHeightT - padding;
1455 labelframePtr->labelBox.y = otherHeight - padding;
1456 break;
1457 }
1458 }
1459
1460 /*
1461 *----------------------------------------------------------------------
1462 *
1463 * DisplayFrame --
1464 *
1465 * This function is invoked to display a frame widget.
1466 *
1467 * Results:
1468 * None.
1469 *
1470 * Side effects:
1471 * Commands are output to X to display the frame in its current mode.
1472 *
1473 *----------------------------------------------------------------------
1474 */
1475
1476 static void
DisplayFrame(ClientData clientData)1477 DisplayFrame(
1478 ClientData clientData) /* Information about widget. */
1479 {
1480 Frame *framePtr = (Frame *)clientData;
1481 Tk_Window tkwin = framePtr->tkwin;
1482 int bdX1, bdY1, bdX2, bdY2, hlWidth;
1483 Pixmap pixmap;
1484 TkRegion clipRegion = NULL;
1485
1486 framePtr->flags &= ~REDRAW_PENDING;
1487 if ((framePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1488 return;
1489 }
1490
1491 /*
1492 * Highlight shall always be drawn if it exists, so do that first.
1493 */
1494
1495 hlWidth = framePtr->highlightWidth;
1496
1497 if (hlWidth != 0) {
1498 GC fgGC, bgGC;
1499
1500 bgGC = Tk_GCForColor(framePtr->highlightBgColorPtr,
1501 Tk_WindowId(tkwin));
1502 if (framePtr->flags & GOT_FOCUS) {
1503 fgGC = Tk_GCForColor(framePtr->highlightColorPtr,
1504 Tk_WindowId(tkwin));
1505 TkpDrawHighlightBorder(tkwin, fgGC, bgGC, hlWidth,
1506 Tk_WindowId(tkwin));
1507 } else {
1508 TkpDrawHighlightBorder(tkwin, bgGC, bgGC, hlWidth,
1509 Tk_WindowId(tkwin));
1510 }
1511 }
1512
1513 /*
1514 * If -background is set to "", no interior is drawn.
1515 */
1516
1517 if (framePtr->border == NULL) {
1518 return;
1519 }
1520
1521 #ifndef TK_NO_DOUBLE_BUFFERING
1522 /*
1523 * In order to avoid screen flashes, this function redraws the frame into
1524 * off-screen memory, then copies it back on-screen in a single operation.
1525 * This means there's no point in time where the on-screen image has been
1526 * cleared.
1527 */
1528
1529 pixmap = Tk_GetPixmap(framePtr->display, Tk_WindowId(tkwin),
1530 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1531 #else
1532 pixmap = Tk_WindowId(tkwin);
1533 #endif /* TK_NO_DOUBLE_BUFFERING */
1534
1535 if (framePtr->type != TYPE_LABELFRAME) {
1536 /*
1537 * Pass to platform specific draw function. In general, it just draws
1538 * a simple rectangle, but it may "theme" the background.
1539 */
1540
1541 noLabel:
1542 TkpDrawFrameEx(tkwin, pixmap, framePtr->border, hlWidth,
1543 framePtr->borderWidth, framePtr->relief);
1544 if (framePtr->bgimg) {
1545 DrawFrameBackground(tkwin, pixmap, hlWidth, framePtr->borderWidth,
1546 framePtr->bgimg, framePtr->tile);
1547 }
1548 } else {
1549 Labelframe *labelframePtr = (Labelframe *) framePtr;
1550
1551 if ((labelframePtr->textPtr == NULL) &&
1552 (labelframePtr->labelWin == NULL)) {
1553 goto noLabel;
1554 }
1555
1556 /*
1557 * Clear the pixmap.
1558 */
1559
1560 Tk_Fill3DRectangle(tkwin, pixmap, framePtr->border, 0, 0,
1561 Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1562
1563 /*
1564 * Calculate how the label affects the border's position.
1565 */
1566
1567 bdX1 = bdY1 = hlWidth;
1568 bdX2 = Tk_Width(tkwin) - hlWidth;
1569 bdY2 = Tk_Height(tkwin) - hlWidth;
1570
1571 switch (labelframePtr->labelAnchor) {
1572 case LABELANCHOR_E:
1573 case LABELANCHOR_EN:
1574 case LABELANCHOR_ES:
1575 bdX2 -= (labelframePtr->labelBox.width-framePtr->borderWidth) / 2;
1576 break;
1577 case LABELANCHOR_N:
1578 case LABELANCHOR_NE:
1579 case LABELANCHOR_NW:
1580 /*
1581 * Since the glyphs of the text tend to be in the lower part we
1582 * favor a lower border position by rounding up.
1583 */
1584
1585 bdY1 += (labelframePtr->labelBox.height-framePtr->borderWidth+1)/2;
1586 break;
1587 case LABELANCHOR_S:
1588 case LABELANCHOR_SE:
1589 case LABELANCHOR_SW:
1590 bdY2 -= (labelframePtr->labelBox.height-framePtr->borderWidth) / 2;
1591 break;
1592 default:
1593 bdX1 += (labelframePtr->labelBox.width-framePtr->borderWidth) / 2;
1594 break;
1595 }
1596
1597 /*
1598 * Draw border
1599 */
1600
1601 Tk_Draw3DRectangle(tkwin, pixmap, framePtr->border, bdX1, bdY1,
1602 bdX2 - bdX1, bdY2 - bdY1, framePtr->borderWidth,
1603 framePtr->relief);
1604
1605 if (labelframePtr->labelWin == NULL) {
1606 /*
1607 * Clear behind the label
1608 */
1609
1610 Tk_Fill3DRectangle(tkwin, pixmap,
1611 framePtr->border, labelframePtr->labelBox.x,
1612 labelframePtr->labelBox.y, labelframePtr->labelBox.width,
1613 labelframePtr->labelBox.height, 0, TK_RELIEF_FLAT);
1614
1615 /*
1616 * Draw label. If there is not room for the entire label, use
1617 * clipping to get a nice appearance.
1618 */
1619
1620 if ((labelframePtr->labelBox.width < labelframePtr->labelReqWidth)
1621 || (labelframePtr->labelBox.height <
1622 labelframePtr->labelReqHeight)) {
1623 clipRegion = TkCreateRegion();
1624 TkUnionRectWithRegion(&labelframePtr->labelBox, clipRegion,
1625 clipRegion);
1626 TkSetRegion(framePtr->display, labelframePtr->textGC,
1627 clipRegion);
1628 }
1629
1630 Tk_DrawTextLayout(framePtr->display, pixmap,
1631 labelframePtr->textGC, labelframePtr->textLayout,
1632 labelframePtr->labelTextX + LABELSPACING,
1633 labelframePtr->labelTextY + LABELSPACING, 0, -1);
1634
1635 if (clipRegion != NULL) {
1636 XSetClipMask(framePtr->display, labelframePtr->textGC, None);
1637 TkDestroyRegion(clipRegion);
1638 }
1639 } else {
1640 /*
1641 * Reposition and map the window (but in different ways depending
1642 * on whether the frame is the window's parent).
1643 */
1644
1645 if (framePtr->tkwin == Tk_Parent(labelframePtr->labelWin)) {
1646 if ((labelframePtr->labelBox.x != Tk_X(labelframePtr->labelWin))
1647 || (labelframePtr->labelBox.y !=
1648 Tk_Y(labelframePtr->labelWin))
1649 || (labelframePtr->labelBox.width !=
1650 Tk_Width(labelframePtr->labelWin))
1651 || (labelframePtr->labelBox.height !=
1652 Tk_Height(labelframePtr->labelWin))) {
1653 Tk_MoveResizeWindow(labelframePtr->labelWin,
1654 labelframePtr->labelBox.x,
1655 labelframePtr->labelBox.y,
1656 labelframePtr->labelBox.width,
1657 labelframePtr->labelBox.height);
1658 }
1659 Tk_MapWindow(labelframePtr->labelWin);
1660 } else {
1661 Tk_MaintainGeometry(labelframePtr->labelWin, framePtr->tkwin,
1662 labelframePtr->labelBox.x, labelframePtr->labelBox.y,
1663 labelframePtr->labelBox.width,
1664 labelframePtr->labelBox.height);
1665 }
1666 }
1667 }
1668
1669 #ifndef TK_NO_DOUBLE_BUFFERING
1670 /*
1671 * Everything's been redisplayed; now copy the pixmap onto the screen and
1672 * free up the pixmap.
1673 */
1674
1675 XCopyArea(framePtr->display, pixmap, Tk_WindowId(tkwin),
1676 framePtr->copyGC, hlWidth, hlWidth,
1677 (unsigned) (Tk_Width(tkwin) - 2 * hlWidth),
1678 (unsigned) (Tk_Height(tkwin) - 2 * hlWidth),
1679 hlWidth, hlWidth);
1680 Tk_FreePixmap(framePtr->display, pixmap);
1681 #endif /* TK_NO_DOUBLE_BUFFERING */
1682 }
1683
1684 /*
1685 *----------------------------------------------------------------------
1686 *
1687 * TkpDrawFrame --
1688 *
1689 * This procedure draws the rectangular frame area.
1690 *
1691 * Results:
1692 * None.
1693 *
1694 * Side effects:
1695 * Draws inside the tkwin area.
1696 *
1697 *----------------------------------------------------------------------
1698 */
1699
1700 void
TkpDrawFrame(Tk_Window tkwin,Tk_3DBorder border,int highlightWidth,int borderWidth,int relief)1701 TkpDrawFrame(
1702 Tk_Window tkwin,
1703 Tk_3DBorder border,
1704 int highlightWidth,
1705 int borderWidth,
1706 int relief)
1707 {
1708 /*
1709 * Legacy shim to allow for external callers. Internal ones use
1710 * non-exposed TkpDrawFrameEx directly so they can use double-buffering.
1711 */
1712
1713 TkpDrawFrameEx(tkwin, Tk_WindowId(tkwin), border,
1714 highlightWidth, borderWidth, relief);
1715 }
1716
1717 /*
1718 *--------------------------------------------------------------
1719 *
1720 * FrameEventProc --
1721 *
1722 * This function is invoked by the Tk dispatcher on structure changes to
1723 * a frame. For frames with 3D borders, this function is also invoked for
1724 * exposures.
1725 *
1726 * Results:
1727 * None.
1728 *
1729 * Side effects:
1730 * When the window gets deleted, internal structures get cleaned up.
1731 * When it gets exposed, it is redisplayed.
1732 *
1733 *--------------------------------------------------------------
1734 */
1735
1736 static void
FrameEventProc(ClientData clientData,XEvent * eventPtr)1737 FrameEventProc(
1738 ClientData clientData, /* Information about window. */
1739 XEvent *eventPtr) /* Information about event. */
1740 {
1741 Frame *framePtr = (Frame *)clientData;
1742
1743 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1744 goto redraw;
1745 } else if (eventPtr->type == ConfigureNotify) {
1746 ComputeFrameGeometry(framePtr);
1747 goto redraw;
1748 } else if (eventPtr->type == DestroyNotify) {
1749 if (framePtr->menuName != NULL) {
1750 TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin,
1751 framePtr->menuName, NULL);
1752 ckfree(framePtr->menuName);
1753 framePtr->menuName = NULL;
1754 }
1755 if (framePtr->tkwin != NULL) {
1756 /*
1757 * If this window is a container, then this event could be coming
1758 * from the embedded application, in which case Tk_DestroyWindow
1759 * hasn't been called yet. When Tk_DestroyWindow is called later,
1760 * then another destroy event will be generated. We need to be
1761 * sure we ignore the second event, since the frame could be gone
1762 * by then. To do so, delete the event handler explicitly
1763 * (normally it's done implicitly by Tk_DestroyWindow).
1764 */
1765
1766 /*
1767 * Since the tkwin pointer will be gone when we reach
1768 * DestroyFrame, we must free all options now.
1769 */
1770
1771 DestroyFramePartly(framePtr);
1772
1773 Tk_DeleteEventHandler(framePtr->tkwin,
1774 ExposureMask|StructureNotifyMask|FocusChangeMask,
1775 FrameEventProc, framePtr);
1776 framePtr->tkwin = NULL;
1777 Tcl_DeleteCommandFromToken(framePtr->interp, framePtr->widgetCmd);
1778 }
1779 if (framePtr->flags & REDRAW_PENDING) {
1780 Tcl_CancelIdleCall(DisplayFrame, framePtr);
1781 }
1782 Tcl_CancelIdleCall(MapFrame, framePtr);
1783 Tcl_EventuallyFree(framePtr, (Tcl_FreeProc *) DestroyFrame);
1784 } else if (eventPtr->type == FocusIn) {
1785 if (eventPtr->xfocus.detail != NotifyInferior) {
1786 framePtr->flags |= GOT_FOCUS;
1787 if (framePtr->highlightWidth > 0) {
1788 goto redraw;
1789 }
1790 }
1791 } else if (eventPtr->type == FocusOut) {
1792 if (eventPtr->xfocus.detail != NotifyInferior) {
1793 framePtr->flags &= ~GOT_FOCUS;
1794 if (framePtr->highlightWidth > 0) {
1795 goto redraw;
1796 }
1797 }
1798 } else if (eventPtr->type == ActivateNotify) {
1799 TkpSetMainMenubar(framePtr->interp, framePtr->tkwin,
1800 framePtr->menuName);
1801 }
1802 return;
1803
1804 redraw:
1805 if ((framePtr->tkwin != NULL) && !(framePtr->flags & REDRAW_PENDING)) {
1806 Tcl_DoWhenIdle(DisplayFrame, framePtr);
1807 framePtr->flags |= REDRAW_PENDING;
1808 }
1809 }
1810
1811 /*
1812 *----------------------------------------------------------------------
1813 *
1814 * FrameCmdDeletedProc --
1815 *
1816 * This function is invoked when a widget command is deleted. If the
1817 * widget isn't already in the process of being destroyed, this command
1818 * destroys it.
1819 *
1820 * Results:
1821 * None.
1822 *
1823 * Side effects:
1824 * The widget is destroyed.
1825 *
1826 *----------------------------------------------------------------------
1827 */
1828
1829 static void
FrameCmdDeletedProc(ClientData clientData)1830 FrameCmdDeletedProc(
1831 ClientData clientData) /* Pointer to widget record for widget. */
1832 {
1833 Frame *framePtr = (Frame *)clientData;
1834 Tk_Window tkwin = framePtr->tkwin;
1835
1836 if (framePtr->menuName != NULL) {
1837 TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin,
1838 framePtr->menuName, NULL);
1839 ckfree(framePtr->menuName);
1840 framePtr->menuName = NULL;
1841 }
1842
1843 /*
1844 * This function could be invoked either because the window was destroyed
1845 * and the command was then deleted (in which case tkwin is NULL) or
1846 * because the command was deleted, and then this function destroys the
1847 * widget.
1848 */
1849
1850 if (tkwin != NULL) {
1851 /*
1852 * Some options need tkwin to be freed, so we free them here, before
1853 * setting tkwin to NULL.
1854 */
1855
1856 DestroyFramePartly(framePtr);
1857
1858 framePtr->tkwin = NULL;
1859 Tk_DestroyWindow(tkwin);
1860 }
1861 }
1862
1863 /*
1864 *----------------------------------------------------------------------
1865 *
1866 * MapFrame --
1867 *
1868 * This function is invoked as a when-idle handler to map a newly-created
1869 * top-level frame.
1870 *
1871 * Results:
1872 * None.
1873 *
1874 * Side effects:
1875 * The frame given by the clientData argument is mapped.
1876 *
1877 *----------------------------------------------------------------------
1878 */
1879
1880 static void
MapFrame(ClientData clientData)1881 MapFrame(
1882 ClientData clientData) /* Pointer to frame structure. */
1883 {
1884 Frame *framePtr = (Frame *)clientData;
1885
1886 /*
1887 * Wait for all other background events to be processed before mapping
1888 * window. This ensures that the window's correct geometry will have been
1889 * determined before it is first mapped, so that the window manager
1890 * doesn't get a false idea of its desired geometry.
1891 */
1892
1893 Tcl_Preserve(framePtr);
1894 while (1) {
1895 if (Tcl_DoOneEvent(TCL_IDLE_EVENTS) == 0) {
1896 break;
1897 }
1898
1899 /*
1900 * After each event, make sure that the window still exists and quit
1901 * if the window has been destroyed.
1902 */
1903
1904 if (framePtr->tkwin == NULL) {
1905 Tcl_Release(framePtr);
1906 return;
1907 }
1908 }
1909 Tk_MapWindow(framePtr->tkwin);
1910 Tcl_Release(framePtr);
1911 }
1912
1913 /*
1914 *--------------------------------------------------------------
1915 *
1916 * TkInstallFrameMenu --
1917 *
1918 * This function is needed when a Windows HWND is created and a menubar
1919 * has been set to the window with a system menu. It notifies the menu
1920 * package so that the system menu can be rebuilt.
1921 *
1922 * Results:
1923 * None.
1924 *
1925 * Side effects:
1926 * The system menu (if any) is created for the menubar associated with
1927 * this frame.
1928 *
1929 *--------------------------------------------------------------
1930 */
1931
1932 void
TkInstallFrameMenu(Tk_Window tkwin)1933 TkInstallFrameMenu(
1934 Tk_Window tkwin) /* The window that was just created. */
1935 {
1936 TkWindow *winPtr = (TkWindow *) tkwin;
1937
1938 if (winPtr->mainPtr != NULL) {
1939 Frame *framePtr = (Frame *)winPtr->instanceData;
1940
1941 if (framePtr == NULL) {
1942 Tcl_Panic("TkInstallFrameMenu couldn't get frame pointer");
1943 }
1944 TkpMenuNotifyToplevelCreate(winPtr->mainPtr->interp,
1945 framePtr->menuName);
1946 }
1947 }
1948
1949 /*
1950 *--------------------------------------------------------------
1951 *
1952 * FrameStructureProc --
1953 *
1954 * This function is invoked whenever StructureNotify events occur for a
1955 * window that's managed as label for the frame. This procudure's only
1956 * purpose is to clean up when windows are deleted.
1957 *
1958 * Results:
1959 * None.
1960 *
1961 * Side effects:
1962 * The window is disassociated from the frame when it is deleted.
1963 *
1964 *--------------------------------------------------------------
1965 */
1966
1967 static void
FrameStructureProc(ClientData clientData,XEvent * eventPtr)1968 FrameStructureProc(
1969 ClientData clientData, /* Pointer to record describing frame. */
1970 XEvent *eventPtr) /* Describes what just happened. */
1971 {
1972 Labelframe *labelframePtr = (Labelframe *)clientData;
1973
1974 if (eventPtr->type == DestroyNotify) {
1975 /*
1976 * This should only happen in a labelframe but it doesn't hurt to be
1977 * careful.
1978 */
1979
1980 if (labelframePtr->frame.type == TYPE_LABELFRAME) {
1981 labelframePtr->labelWin = NULL;
1982 FrameWorldChanged(labelframePtr);
1983 }
1984 }
1985 }
1986
1987 /*
1988 *--------------------------------------------------------------
1989 *
1990 * FrameRequestProc --
1991 *
1992 * This function is invoked whenever a window that's associated with a
1993 * frame changes its requested dimensions.
1994 *
1995 * Results:
1996 * None.
1997 *
1998 * Side effects:
1999 * The size and location on the screen of the window may change depending
2000 * on the options specified for the frame.
2001 *
2002 *--------------------------------------------------------------
2003 */
2004
2005 static void
FrameRequestProc(ClientData clientData,Tk_Window tkwin)2006 FrameRequestProc(
2007 ClientData clientData, /* Pointer to record for frame. */
2008 Tk_Window tkwin) /* Window that changed its desired size. */
2009 {
2010 Frame *framePtr = (Frame *)clientData;
2011 (void)tkwin;
2012
2013 FrameWorldChanged(framePtr);
2014 }
2015
2016 /*
2017 *--------------------------------------------------------------
2018 *
2019 * FrameLostContentProc --
2020 *
2021 * This function is invoked by Tk whenever some other geometry claims
2022 * control over a content window that used to be managed by us.
2023 *
2024 * Results:
2025 * None.
2026 *
2027 * Side effects:
2028 * Forgets all frame-related information about the content window.
2029 *
2030 *--------------------------------------------------------------
2031 */
2032
2033 static void
FrameLostContentProc(ClientData clientData,Tk_Window tkwin)2034 FrameLostContentProc(
2035 ClientData clientData, /* Frame structure for content window window that was
2036 * stolen away. */
2037 Tk_Window tkwin) /* Tk's handle for the content window window. */
2038 {
2039 Frame *framePtr = (Frame *)clientData;
2040 Labelframe *labelframePtr = (Labelframe *)clientData;
2041 (void)tkwin;
2042
2043 /*
2044 * This should only happen in a labelframe but it doesn't hurt to be
2045 * careful.
2046 */
2047
2048 if (labelframePtr->frame.type == TYPE_LABELFRAME) {
2049 Tk_DeleteEventHandler(labelframePtr->labelWin, StructureNotifyMask,
2050 FrameStructureProc, labelframePtr);
2051 if (framePtr->tkwin != Tk_Parent(labelframePtr->labelWin)) {
2052 Tk_UnmaintainGeometry(labelframePtr->labelWin, framePtr->tkwin);
2053 }
2054 Tk_UnmapWindow(labelframePtr->labelWin);
2055 labelframePtr->labelWin = NULL;
2056 }
2057 FrameWorldChanged(framePtr);
2058 }
2059
2060 void
TkMapTopFrame(Tk_Window tkwin)2061 TkMapTopFrame(
2062 Tk_Window tkwin)
2063 {
2064 Frame *framePtr = (Frame *)((TkWindow *) tkwin)->instanceData;
2065 Tk_OptionTable optionTable;
2066
2067 if (Tk_IsTopLevel(tkwin) && framePtr->type == TYPE_FRAME) {
2068 framePtr->type = TYPE_TOPLEVEL;
2069 Tcl_DoWhenIdle(MapFrame, framePtr);
2070 if (framePtr->menuName != NULL) {
2071 TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin, NULL,
2072 framePtr->menuName);
2073 }
2074 } else if (!Tk_IsTopLevel(tkwin) && framePtr->type == TYPE_TOPLEVEL) {
2075 framePtr->type = TYPE_FRAME;
2076 } else {
2077 /*
2078 * Not a frame or toplevel, skip it.
2079 */
2080
2081 return;
2082 }
2083
2084 /*
2085 * The option table has already been created so the cached pointer will be
2086 * returned.
2087 */
2088
2089 optionTable = Tk_CreateOptionTable(framePtr->interp,
2090 optionSpecs[framePtr->type]);
2091 framePtr->optionTable = optionTable;
2092 }
2093
2094 /*
2095 *--------------------------------------------------------------
2096 *
2097 * TkToplevelWindowFromCommandToken --
2098 *
2099 * If the given command name to the command for a toplevel window in the
2100 * given interpreter, return the tkwin for that toplevel window. Note
2101 * that this lookup can't be done using the standard tkwin internal table
2102 * because the command might have been renamed.
2103 *
2104 * Results:
2105 * A Tk_Window token, or NULL if the name does not refer to a toplevel
2106 * window.
2107 *
2108 * Side effects:
2109 * None.
2110 *
2111 *--------------------------------------------------------------
2112 */
2113
2114 Tk_Window
TkToplevelWindowForCommand(Tcl_Interp * interp,const char * cmdName)2115 TkToplevelWindowForCommand(
2116 Tcl_Interp *interp,
2117 const char *cmdName)
2118 {
2119 Tcl_CmdInfo cmdInfo;
2120 Frame *framePtr;
2121
2122 if (Tcl_GetCommandInfo(interp, cmdName, &cmdInfo) == 0) {
2123 return NULL;
2124 }
2125 if (cmdInfo.objProc != FrameWidgetObjCmd) {
2126 return NULL;
2127 }
2128 framePtr = (Frame *)cmdInfo.objClientData;
2129 if (framePtr->type != TYPE_TOPLEVEL) {
2130 return NULL;
2131 }
2132 return framePtr->tkwin;
2133 }
2134
2135 /*
2136 *----------------------------------------------------------------------
2137 *
2138 * FrameBgImageProc --
2139 *
2140 * This function is invoked by the image code whenever the manager for an
2141 * image does something that affects the size or contents of an image
2142 * displayed on a frame's background.
2143 *
2144 * Results:
2145 * None.
2146 *
2147 * Side effects:
2148 * Arranges for the button to get redisplayed.
2149 *
2150 *----------------------------------------------------------------------
2151 */
2152
2153 static void
FrameBgImageProc(ClientData clientData,int x,int y,int width,int height,int imgWidth,int imgHeight)2154 FrameBgImageProc(
2155 ClientData clientData, /* Pointer to widget record. */
2156 int x, int y, /* Upper left pixel (within image) that must
2157 * be redisplayed. */
2158 int width, int height, /* Dimensions of area to redisplay (might be
2159 * <= 0). */
2160 int imgWidth, int imgHeight)/* New dimensions of image. */
2161 {
2162 Frame *framePtr = (Frame *)clientData;
2163 (void)x;
2164 (void)y;
2165 (void)width;
2166 (void)height;
2167 (void)imgWidth;
2168 (void)imgHeight;
2169
2170
2171 /*
2172 * Changing the background image never alters the dimensions of the frame.
2173 */
2174
2175 if (framePtr->tkwin && Tk_IsMapped(framePtr->tkwin) &&
2176 !(framePtr->flags & REDRAW_PENDING)) {
2177 Tcl_DoWhenIdle(DisplayFrame, framePtr);
2178 framePtr->flags |= REDRAW_PENDING;
2179 }
2180 }
2181
2182 /*
2183 *----------------------------------------------------------------------
2184 *
2185 * DrawFrameBackground --
2186 *
2187 * This function draws the background image of a rectangular frame area.
2188 *
2189 * Results:
2190 * None.
2191 *
2192 * Side effects:
2193 * Draws inside the tkwin area.
2194 *
2195 *----------------------------------------------------------------------
2196 */
2197
2198 static void
DrawFrameBackground(Tk_Window tkwin,Pixmap pixmap,int highlightWidth,int borderWidth,Tk_Image bgimg,int bgtile)2199 DrawFrameBackground(
2200 Tk_Window tkwin,
2201 Pixmap pixmap,
2202 int highlightWidth,
2203 int borderWidth,
2204 Tk_Image bgimg,
2205 int bgtile)
2206 {
2207 int width, height; /* Area to paint on. */
2208 int imageWidth, imageHeight; /* Dimensions of image. */
2209 const int bw = highlightWidth + borderWidth;
2210
2211 Tk_SizeOfImage(bgimg, &imageWidth, &imageHeight);
2212 width = Tk_Width(tkwin) - 2*bw;
2213 height = Tk_Height(tkwin) - 2*bw;
2214
2215 if (bgtile) {
2216 /*
2217 * Draw the image tiled in the widget (inside the border).
2218 */
2219
2220 int x, y;
2221
2222 for (x = bw; x - bw < width; x += imageWidth) {
2223 int w = imageWidth;
2224 if (x - bw + imageWidth > width) {
2225 w = (width + bw) - x;
2226 }
2227 for (y = bw; y < height + bw; y += imageHeight) {
2228 int h = imageHeight;
2229 if (y - bw + imageHeight > height) {
2230 h = (height + bw) - y;
2231 }
2232 Tk_RedrawImage(bgimg, 0, 0, w, h, pixmap, x, y);
2233 }
2234 }
2235 } else {
2236 /*
2237 * Draw the image centred in the widget (inside the border).
2238 */
2239
2240 int x, y, xOff, yOff, w, h;
2241
2242 if (width > imageWidth) {
2243 x = 0;
2244 xOff = (Tk_Width(tkwin) - imageWidth) / 2;
2245 w = imageWidth;
2246 } else {
2247 x = (imageWidth - width) / 2;
2248 xOff = bw;
2249 w = width;
2250 }
2251 if (height > imageHeight) {
2252 y = 0;
2253 yOff = (Tk_Height(tkwin) - imageHeight) / 2;
2254 h = imageHeight;
2255 } else {
2256 y = (imageHeight - height) / 2;
2257 yOff = bw;
2258 h = height;
2259 }
2260 Tk_RedrawImage(bgimg, x, y, w, h, pixmap, xOff, yOff);
2261 }
2262 }
2263
2264 /*
2265 * Local Variables:
2266 * mode: c
2267 * c-basic-offset: 4
2268 * fill-column: 78
2269 * End:
2270 */
2271