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