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