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