diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-18 17:31:55 (GMT) |
commit | 39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb (patch) | |
tree | 8e5374666c7f0b3017176ec9d6e6b6eae0dcabac /tk8.6/generic/tkFrame.c | |
parent | 066971b1e6e77991d9161bb0216a63ba94ea04f9 (diff) | |
parent | 6b095f3c8521ca7215e6ff5dcbada52b197ef7d0 (diff) | |
download | blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.zip blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.gz blt-39e34335fb6eb6eaf2b7ee51ccf172006dd46fbb.tar.bz2 |
Merge commit '6b095f3c8521ca7215e6ff5dcbada52b197ef7d0' as 'tk8.6'
Diffstat (limited to 'tk8.6/generic/tkFrame.c')
-rw-r--r-- | tk8.6/generic/tkFrame.c | 2039 |
1 files changed, 2039 insertions, 0 deletions
diff --git a/tk8.6/generic/tkFrame.c b/tk8.6/generic/tkFrame.c new file mode 100644 index 0000000..f6edfb0 --- /dev/null +++ b/tk8.6/generic/tkFrame.c @@ -0,0 +1,2039 @@ +/* + * tkFrame.c -- + * + * This module implements "frame", "labelframe" and "toplevel" widgets + * for the Tk toolkit. Frames are windows with a background color and + * possibly a 3-D effect, but not much else in the way of attributes. + * + * Copyright (c) 1990-1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "default.h" +#include "tkInt.h" + +/* + * The following enum is used to define the type of the frame. + */ + +enum FrameType { + TYPE_FRAME, TYPE_TOPLEVEL, TYPE_LABELFRAME +}; + +/* + * A data structure of the following type is kept for each + * frame that currently exists for this process: + */ + +typedef struct { + Tk_Window tkwin; /* Window that embodies the frame. NULL means + * that the window has been destroyed but the + * data structures haven't yet been cleaned + * up. */ + Display *display; /* Display containing widget. Used, among + * other things, so that resources can be + * freed even after tkwin has gone away. */ + Tcl_Interp *interp; /* Interpreter associated with widget. Used to + * delete widget command. */ + Tcl_Command widgetCmd; /* Token for frame's widget command. */ + Tk_OptionTable optionTable; /* Table that defines configuration options + * available for this widget. */ + char *className; /* Class name for widget (from configuration + * option). Malloc-ed. */ + enum FrameType type; /* Type of widget, such as TYPE_FRAME. */ + char *screenName; /* Screen on which widget is created. Non-null + * only for top-levels. Malloc-ed, may be + * NULL. */ + char *visualName; /* Textual description of visual for window, + * from -visual option. Malloc-ed, may be + * NULL. */ + char *colormapName; /* Textual description of colormap for window, + * from -colormap option. Malloc-ed, may be + * NULL. */ + char *menuName; /* Textual description of menu to use for + * menubar. Malloc-ed, may be NULL. */ + Colormap colormap; /* If not None, identifies a colormap + * allocated for this window, which must be + * freed when the window is deleted. */ + Tk_3DBorder border; /* Structure used to draw 3-D border and + * background. NULL means no background or + * border. */ + int borderWidth; /* Width of 3-D border (if any). */ + int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */ + int highlightWidth; /* Width in pixels of highlight to draw around + * widget when it has the focus. 0 means don't + * draw a highlight. */ + XColor *highlightBgColorPtr; + /* Color for drawing traversal highlight area + * when highlight is off. */ + XColor *highlightColorPtr; /* Color for drawing traversal highlight. */ + int width; /* Width to request for window. <= 0 means + * don't request any size. */ + int height; /* Height to request for window. <= 0 means + * don't request any size. */ + Tk_Cursor cursor; /* Current cursor for window, or None. */ + char *takeFocus; /* Value of -takefocus option; not used in the + * C code, but used by keyboard traversal + * scripts. Malloc'ed, but may be NULL. */ + int isContainer; /* 1 means this window is a container, 0 means + * that it isn't. */ + char *useThis; /* If the window is embedded, this points to + * the name of the window in which it is + * embedded (malloc'ed). For non-embedded + * windows this is NULL. */ + int flags; /* Various flags; see below for + * definitions. */ + Tcl_Obj *padXPtr; /* Value of -padx option: specifies how many + * pixels of extra space to leave on left and + * right of child area. */ + int padX; /* Integer value corresponding to padXPtr. */ + Tcl_Obj *padYPtr; /* Value of -padx option: specifies how many + * pixels of extra space to leave above and + * below child area. */ + int padY; /* Integer value corresponding to padYPtr. */ +} Frame; + +/* + * A data structure of the following type is kept for each labelframe widget + * managed by this file: + */ + +typedef struct { + Frame frame; /* A pointer to the generic frame structure. + * This must be the first element of the + * Labelframe. */ + /* + * Labelframe specific configuration settings. + */ + Tcl_Obj *textPtr; /* Value of -text option: specifies text to + * display in button. */ + Tk_Font tkfont; /* Value of -font option: specifies font to + * use for display text. */ + XColor *textColorPtr; /* Value of -fg option: specifies foreground + * color in normal mode. */ + int labelAnchor; /* Value of -labelanchor option: specifies + * where to place the label. */ + Tk_Window labelWin; /* Value of -labelwidget option: Window to use + * as label for the frame. */ + /* + * Labelframe specific fields for use with configuration settings above. + */ + GC textGC; /* GC for drawing text in normal mode. */ + Tk_TextLayout textLayout; /* Stored text layout information. */ + XRectangle labelBox; /* The label's actual size and position. */ + int labelReqWidth; /* The label's requested width. */ + int labelReqHeight; /* The label's requested height. */ + int labelTextX, labelTextY; /* Position of the text to be drawn. */ +} Labelframe; + +/* + * The following macros define how many extra pixels to leave around a label's + * text. + */ + +#define LABELSPACING 1 +#define LABELMARGIN 4 + +/* + * Flag bits for frames: + * + * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has + * already been queued to redraw this window. + * GOT_FOCUS: Non-zero means this widget currently has the + * input focus. + */ + +#define REDRAW_PENDING 1 +#define GOT_FOCUS 4 + +/* + * The following enum is used to define a type for the -labelanchor option of + * the Labelframe widget. These values are used as indices into the string + * table below. + */ + +enum labelanchor { + LABELANCHOR_E, LABELANCHOR_EN, LABELANCHOR_ES, + LABELANCHOR_N, LABELANCHOR_NE, LABELANCHOR_NW, + LABELANCHOR_S, LABELANCHOR_SE, LABELANCHOR_SW, + LABELANCHOR_W, LABELANCHOR_WN, LABELANCHOR_WS +}; + +static const char *const labelAnchorStrings[] = { + "e", "en", "es", "n", "ne", "nw", "s", "se", "sw", "w", "wn", "ws", + NULL +}; + +/* + * Information used for parsing configuration options. There are one common + * table used by all and one table for each widget class. + */ + +static const Tk_OptionSpec commonOptSpec[] = { + {TK_OPTION_BORDER, "-background", "background", "Background", + DEF_FRAME_BG_COLOR, -1, Tk_Offset(Frame, border), + TK_OPTION_NULL_OK, DEF_FRAME_BG_MONO, 0}, + {TK_OPTION_SYNONYM, "-bg", NULL, NULL, + NULL, 0, -1, 0, "-background", 0}, + {TK_OPTION_STRING, "-colormap", "colormap", "Colormap", + DEF_FRAME_COLORMAP, -1, Tk_Offset(Frame, colormapName), + TK_OPTION_NULL_OK, 0, 0}, + /* + * Having -container is useless in a labelframe since a container has + * no border. It should be deprecated. + */ + {TK_OPTION_BOOLEAN, "-container", "container", "Container", + DEF_FRAME_CONTAINER, -1, Tk_Offset(Frame, isContainer), 0, 0, 0}, + {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", + DEF_FRAME_CURSOR, -1, Tk_Offset(Frame, cursor), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_PIXELS, "-height", "height", "Height", + DEF_FRAME_HEIGHT, -1, Tk_Offset(Frame, height), 0, 0, 0}, + {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", + "HighlightBackground", DEF_FRAME_HIGHLIGHT_BG, -1, + Tk_Offset(Frame, highlightBgColorPtr), 0, 0, 0}, + {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", + DEF_FRAME_HIGHLIGHT, -1, Tk_Offset(Frame, highlightColorPtr), + 0, 0, 0}, + {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", + "HighlightThickness", DEF_FRAME_HIGHLIGHT_WIDTH, -1, + Tk_Offset(Frame, highlightWidth), 0, 0, 0}, + {TK_OPTION_PIXELS, "-padx", "padX", "Pad", + DEF_FRAME_PADX, Tk_Offset(Frame, padXPtr), + Tk_Offset(Frame, padX), 0, 0, 0}, + {TK_OPTION_PIXELS, "-pady", "padY", "Pad", + DEF_FRAME_PADY, Tk_Offset(Frame, padYPtr), + Tk_Offset(Frame, padY), 0, 0, 0}, + {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", + DEF_FRAME_TAKE_FOCUS, -1, Tk_Offset(Frame, takeFocus), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_STRING, "-visual", "visual", "Visual", + DEF_FRAME_VISUAL, -1, Tk_Offset(Frame, visualName), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_PIXELS, "-width", "width", "Width", + DEF_FRAME_WIDTH, -1, Tk_Offset(Frame, width), 0, 0, 0}, + {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0} +}; + +static const Tk_OptionSpec frameOptSpec[] = { + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, "-borderwidth", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + DEF_FRAME_BORDER_WIDTH, -1, Tk_Offset(Frame, borderWidth), 0, 0, 0}, + {TK_OPTION_STRING, "-class", "class", "Class", + DEF_FRAME_CLASS, -1, Tk_Offset(Frame, className), 0, 0, 0}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + DEF_FRAME_RELIEF, -1, Tk_Offset(Frame, relief), 0, 0, 0}, + {TK_OPTION_END, NULL, NULL, NULL, + NULL, 0, 0, 0, commonOptSpec, 0} +}; + +static const Tk_OptionSpec toplevelOptSpec[] = { + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, "-borderwidth", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + DEF_FRAME_BORDER_WIDTH, -1, Tk_Offset(Frame, borderWidth), 0, 0, 0}, + {TK_OPTION_STRING, "-class", "class", "Class", + DEF_TOPLEVEL_CLASS, -1, Tk_Offset(Frame, className), 0, 0, 0}, + {TK_OPTION_STRING, "-menu", "menu", "Menu", + DEF_TOPLEVEL_MENU, -1, Tk_Offset(Frame, menuName), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + DEF_FRAME_RELIEF, -1, Tk_Offset(Frame, relief), 0, 0, 0}, + {TK_OPTION_STRING, "-screen", "screen", "Screen", + DEF_TOPLEVEL_SCREEN, -1, Tk_Offset(Frame, screenName), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_STRING, "-use", "use", "Use", + DEF_TOPLEVEL_USE, -1, Tk_Offset(Frame, useThis), + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_END, NULL, NULL, NULL, + NULL, 0, 0, 0, commonOptSpec, 0} +}; + +static const Tk_OptionSpec labelframeOptSpec[] = { + {TK_OPTION_SYNONYM, "-bd", NULL, NULL, + NULL, 0, -1, 0, "-borderwidth", 0}, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + DEF_LABELFRAME_BORDER_WIDTH, -1, Tk_Offset(Frame, borderWidth), + 0, 0, 0}, + {TK_OPTION_STRING, "-class", "class", "Class", + DEF_LABELFRAME_CLASS, -1, Tk_Offset(Frame, className), 0, 0, 0}, + {TK_OPTION_SYNONYM, "-fg", "foreground", NULL, + NULL, 0, -1, 0, "-foreground", 0}, + {TK_OPTION_FONT, "-font", "font", "Font", + DEF_LABELFRAME_FONT, -1, Tk_Offset(Labelframe, tkfont), 0, 0, 0}, + {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", + DEF_LABELFRAME_FG, -1, Tk_Offset(Labelframe, textColorPtr), 0, 0, 0}, + {TK_OPTION_STRING_TABLE, "-labelanchor", "labelAnchor", "LabelAnchor", + DEF_LABELFRAME_LABELANCHOR, -1, Tk_Offset(Labelframe, labelAnchor), + 0, labelAnchorStrings, 0}, + {TK_OPTION_WINDOW, "-labelwidget", "labelWidget", "LabelWidget", + NULL, -1, Tk_Offset(Labelframe, labelWin), TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + DEF_LABELFRAME_RELIEF, -1, Tk_Offset(Frame, relief), 0, 0, 0}, + {TK_OPTION_STRING, "-text", "text", "Text", + DEF_LABELFRAME_TEXT, Tk_Offset(Labelframe, textPtr), -1, + TK_OPTION_NULL_OK, 0, 0}, + {TK_OPTION_END, NULL, NULL, NULL, + NULL, 0, 0, 0, commonOptSpec, 0} +}; + +/* + * Class names for widgets, indexed by FrameType. + */ + +static const char *const classNames[] = {"Frame", "Toplevel", "Labelframe"}; + +/* + * The following table maps from FrameType to the option template for that + * class of widgets. + */ + +static const Tk_OptionSpec *const optionSpecs[] = { + frameOptSpec, + toplevelOptSpec, + labelframeOptSpec, +}; + +/* + * Forward declarations for functions defined later in this file: + */ + +static void ComputeFrameGeometry(Frame *framePtr); +static int ConfigureFrame(Tcl_Interp *interp, Frame *framePtr, + int objc, Tcl_Obj *const objv[]); +static int CreateFrame(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *const argv[], + enum FrameType type, const char *appName); +static void DestroyFrame(void *memPtr); +static void DestroyFramePartly(Frame *framePtr); +static void DisplayFrame(ClientData clientData); +static void FrameCmdDeletedProc(ClientData clientData); +static void FrameEventProc(ClientData clientData, + XEvent *eventPtr); +static void FrameLostSlaveProc(ClientData clientData, + Tk_Window tkwin); +static void FrameRequestProc(ClientData clientData, + Tk_Window tkwin); +static void FrameStructureProc(ClientData clientData, + XEvent *eventPtr); +static int FrameWidgetObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static void FrameWorldChanged(ClientData instanceData); +static void MapFrame(ClientData clientData); + +/* + * The structure below defines frame class behavior by means of functions that + * can be invoked from generic window code. + */ + +static const Tk_ClassProcs frameClass = { + sizeof(Tk_ClassProcs), /* size */ + FrameWorldChanged, /* worldChangedProc */ + NULL, /* createProc */ + NULL /* modalProc */ +}; + +/* + * The structure below defines the official type record for the labelframe's + * geometry manager: + */ + +static const Tk_GeomMgr frameGeomType = { + "labelframe", /* name */ + FrameRequestProc, /* requestProc */ + FrameLostSlaveProc /* lostSlaveProc */ +}; + +/* + *-------------------------------------------------------------- + * + * Tk_FrameObjCmd, Tk_ToplevelObjCmd, Tk_LabelframeObjCmd -- + * + * These functions are invoked to process the "frame", "toplevel" and + * "labelframe" Tcl commands. See the user documentation for details on + * what they do. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. These functions are just wrappers; they + * call CreateFrame to do all of the real work. + * + *-------------------------------------------------------------- + */ + +int +Tk_FrameObjCmd( + ClientData clientData, /* Either NULL or pointer to option table. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return CreateFrame(clientData, interp, objc, objv, TYPE_FRAME, NULL); +} + +int +Tk_ToplevelObjCmd( + ClientData clientData, /* Either NULL or pointer to option table. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return CreateFrame(clientData, interp, objc, objv, TYPE_TOPLEVEL, NULL); +} + +int +Tk_LabelframeObjCmd( + ClientData clientData, /* Either NULL or pointer to option table. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return CreateFrame(clientData, interp, objc, objv, TYPE_LABELFRAME, NULL); +} + +/* + *-------------------------------------------------------------- + * + * TkCreateFrame -- + * + * This function is the old command function for the "frame" and + * "toplevel" commands. Now it is used directly by Tk_Init to create a + * new main window. See the user documentation for the "frame" and + * "toplevel" commands for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *-------------------------------------------------------------- + */ + +int +TkCreateFrame( + ClientData clientData, /* Either NULL or pointer to option table. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + const char *const *argv, /* Argument strings. */ + int toplevel, /* Non-zero means create a toplevel window, + * zero means create a frame. */ + const char *appName) /* Should only be non-NULL if there is no main + * window associated with the interpreter. + * Gives the base name to use for the new + * application. */ +{ + int result, i; + Tcl_Obj **objv = ckalloc((argc+1) * sizeof(Tcl_Obj **)); + + for (i=0; i<argc; i++) { + objv[i] = Tcl_NewStringObj(argv[i], -1); + Tcl_IncrRefCount(objv[i]); + } + objv[argc] = NULL; + result = CreateFrame(clientData, interp, argc, objv, + toplevel ? TYPE_TOPLEVEL : TYPE_FRAME, appName); + for (i=0; i<argc; i++) { + Tcl_DecrRefCount(objv[i]); + } + ckfree(objv); + return result; +} + +int +TkListCreateFrame( + ClientData clientData, /* Either NULL or pointer to option table. */ + Tcl_Interp *interp, /* Current interpreter. */ + Tcl_Obj *listObj, /* List of arguments. */ + int toplevel, /* Non-zero means create a toplevel window, + * zero means create a frame. */ + Tcl_Obj *nameObj) /* Should only be non-NULL if there is no main + * window associated with the interpreter. + * Gives the base name to use for the new + * application. */ + +{ + int objc; + Tcl_Obj **objv; + + if (TCL_OK != Tcl_ListObjGetElements(interp, listObj, &objc, &objv)) { + return TCL_ERROR; + } + return CreateFrame(clientData, interp, objc, objv, + toplevel ? TYPE_TOPLEVEL : TYPE_FRAME, + nameObj ? Tcl_GetString(nameObj) : NULL); +} + +static int +CreateFrame( + ClientData clientData, /* NULL. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument objects. */ + enum FrameType type, /* What widget type to create. */ + const char *appName) /* Should only be non-NULL if there are no + * Main window associated with the + * interpreter. Gives the base name to use for + * the new application. */ +{ + Tk_Window tkwin; + Frame *framePtr; + Tk_OptionTable optionTable; + Tk_Window newWin; + const char *className, *screenName, *visualName, *colormapName; + const char *arg, *useOption; + int i, length, depth; + unsigned int mask; + Colormap colormap; + Visual *visual; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "pathName ?-option value ...?"); + return TCL_ERROR; + } + + /* + * Create the option table for this widget class. If it has already been + * created, the cached pointer will be returned. + */ + + optionTable = Tk_CreateOptionTable(interp, optionSpecs[type]); + + /* + * Pre-process the argument list. Scan through it to find any "-class", + * "-screen", "-visual", and "-colormap" options. These arguments need to + * be processed specially, before the window is configured using the usual + * Tk mechanisms. + */ + + className = colormapName = screenName = visualName = useOption = NULL; + colormap = None; + for (i = 2; i < objc; i += 2) { + arg = Tcl_GetStringFromObj(objv[i], &length); + if (length < 2) { + continue; + } + if ((arg[1] == 'c') && (length >= 3) + && (strncmp(arg, "-class", (unsigned) length) == 0)) { + className = Tcl_GetString(objv[i+1]); + } else if ((arg[1] == 'c') && (length >= 3) + && (strncmp(arg, "-colormap", (unsigned) length) == 0)) { + colormapName = Tcl_GetString(objv[i+1]); + } else if ((arg[1] == 's') && (type == TYPE_TOPLEVEL) + && (strncmp(arg, "-screen", (unsigned) length) == 0)) { + screenName = Tcl_GetString(objv[i+1]); + } else if ((arg[1] == 'u') && (type == TYPE_TOPLEVEL) + && (strncmp(arg, "-use", (unsigned) length) == 0)) { + useOption = Tcl_GetString(objv[i+1]); + } else if ((arg[1] == 'v') + && (strncmp(arg, "-visual", (unsigned) length) == 0)) { + visualName = Tcl_GetString(objv[i+1]); + } + } + + /* + * Create the window, and deal with the special options -use, -classname, + * -colormap, -screenname, and -visual. These options must be handle + * before calling ConfigureFrame below, and they must also be processed in + * a particular order, for the following reasons: + * 1. Must set the window's class before calling ConfigureFrame, so that + * unspecified options are looked up in the option database using the + * correct class. + * 2. Must set visual information before calling ConfigureFrame so that + * colors are allocated in a proper colormap. + * 3. Must call TkpUseWindow before setting non-default visual + * information, since TkpUseWindow changes the defaults. + */ + + if (screenName == NULL) { + screenName = (type == TYPE_TOPLEVEL) ? "" : NULL; + } + + /* + * Main window associated with interpreter. If we're called by Tk_Init to + * create a new application, then this is NULL. + */ + + tkwin = Tk_MainWindow(interp); + if (tkwin != NULL) { + newWin = Tk_CreateWindowFromPath(interp, tkwin, Tcl_GetString(objv[1]), + screenName); + } else if (appName == NULL) { + /* + * This occurs when someone tried to create a frame/toplevel while we + * are being destroyed. Let an error be thrown. + */ + + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unable to create widget \"%s\"", Tcl_GetString(objv[1]))); + Tcl_SetErrorCode(interp, "TK", "APPLICATION_GONE", NULL); + return TCL_ERROR; + } else { + /* + * We were called from Tk_Init; create a new application. + */ + + newWin = TkCreateMainWindow(interp, screenName, appName); + } + if (newWin == NULL) { + goto error; + } + + /* + * Mark Tk frames as suitable candidates for [wm manage]. + */ + + ((TkWindow *) newWin)->flags |= TK_WM_MANAGEABLE; + + if (className == NULL) { + className = Tk_GetOption(newWin, "class", "Class"); + if (className == NULL) { + className = classNames[type]; + } + } + Tk_SetClass(newWin, className); + if (useOption == NULL) { + useOption = Tk_GetOption(newWin, "use", "Use"); + } + if ((useOption != NULL) && (*useOption != 0) + && (TkpUseWindow(interp, newWin, useOption) != TCL_OK)) { + goto error; + } + if (visualName == NULL) { + visualName = Tk_GetOption(newWin, "visual", "Visual"); + } + if (colormapName == NULL) { + colormapName = Tk_GetOption(newWin, "colormap", "Colormap"); + } + if ((colormapName != NULL) && (*colormapName == 0)) { + colormapName = NULL; + } + if (visualName != NULL) { + visual = Tk_GetVisual(interp, newWin, visualName, &depth, + (colormapName == NULL) ? &colormap : NULL); + if (visual == NULL) { + goto error; + } + Tk_SetWindowVisual(newWin, visual, depth, colormap); + } + if (colormapName != NULL) { + colormap = Tk_GetColormap(interp, newWin, colormapName); + if (colormap == None) { + goto error; + } + Tk_SetWindowColormap(newWin, colormap); + } + + /* + * For top-level windows, provide an initial geometry request of 200x200, + * just so the window looks nicer on the screen if it doesn't request a + * size for itself. + */ + + if (type == TYPE_TOPLEVEL) { + Tk_GeometryRequest(newWin, 200, 200); + } + + /* + * Create the widget record, process configuration options, and create + * event handlers. Then fill in a few additional fields in the widget + * record from the special options. + */ + + if (type == TYPE_LABELFRAME) { + framePtr = ckalloc(sizeof(Labelframe)); + memset(framePtr, 0, sizeof(Labelframe)); + } else { + framePtr = ckalloc(sizeof(Frame)); + memset(framePtr, 0, sizeof(Frame)); + } + framePtr->tkwin = newWin; + framePtr->display = Tk_Display(newWin); + framePtr->interp = interp; + framePtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(newWin), + FrameWidgetObjCmd, framePtr, FrameCmdDeletedProc); + framePtr->optionTable = optionTable; + framePtr->type = type; + framePtr->colormap = colormap; + framePtr->relief = TK_RELIEF_FLAT; + framePtr->cursor = None; + + if (framePtr->type == TYPE_LABELFRAME) { + Labelframe *labelframePtr = (Labelframe *) framePtr; + + labelframePtr->labelAnchor = LABELANCHOR_NW; + labelframePtr->textGC = None; + } + + /* + * Store backreference to frame widget in window structure. + */ + + Tk_SetClassProcs(newWin, &frameClass, framePtr); + + mask = ExposureMask | StructureNotifyMask | FocusChangeMask; + if (type == TYPE_TOPLEVEL) { + mask |= ActivateMask; + } + Tk_CreateEventHandler(newWin, mask, FrameEventProc, framePtr); + if ((Tk_InitOptions(interp, (char *) framePtr, optionTable, newWin) + != TCL_OK) || + (ConfigureFrame(interp, framePtr, objc-2, objv+2) != TCL_OK)) { + goto error; + } + if (framePtr->isContainer) { + if (framePtr->useThis != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "windows cannot have both the -use and the -container" + " option set", -1)); + Tcl_SetErrorCode(interp, "TK", "FRAME", "CONTAINMENT", NULL); + goto error; + } + TkpMakeContainer(framePtr->tkwin); + } + if (type == TYPE_TOPLEVEL) { + Tcl_DoWhenIdle(MapFrame, framePtr); + } + Tcl_SetObjResult(interp, TkNewWindowObj(newWin)); + return TCL_OK; + + error: + if (newWin != NULL) { + Tk_DestroyWindow(newWin); + } + return TCL_ERROR; +} + +/* + *-------------------------------------------------------------- + * + * FrameWidgetObjCmd -- + * + * This function is invoked to process the Tcl command that corresponds + * to a frame widget. See the user documentation for details on what it + * does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *-------------------------------------------------------------- + */ + +static int +FrameWidgetObjCmd( + ClientData clientData, /* Information about frame widget. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + static const char *const frameOptions[] = { + "cget", "configure", NULL + }; + enum options { + FRAME_CGET, FRAME_CONFIGURE + }; + register Frame *framePtr = clientData; + int result = TCL_OK, index; + int c, i, length; + Tcl_Obj *objPtr; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObjStruct(interp, objv[1], frameOptions, + sizeof(char *), "option", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + Tcl_Preserve(framePtr); + switch ((enum options) index) { + case FRAME_CGET: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "option"); + result = TCL_ERROR; + goto done; + } + objPtr = Tk_GetOptionValue(interp, (char *) framePtr, + framePtr->optionTable, objv[2], framePtr->tkwin); + if (objPtr == NULL) { + result = TCL_ERROR; + goto done; + } + Tcl_SetObjResult(interp, objPtr); + break; + case FRAME_CONFIGURE: + if (objc <= 3) { + objPtr = Tk_GetOptionInfo(interp, (char *) framePtr, + framePtr->optionTable, (objc == 3) ? objv[2] : NULL, + framePtr->tkwin); + if (objPtr == NULL) { + result = TCL_ERROR; + goto done; + } + Tcl_SetObjResult(interp, objPtr); + } else { + /* + * Don't allow the options -class, -colormap, -container, -screen, + * -use, or -visual to be changed. + */ + + for (i = 2; i < objc; i++) { + const char *arg = Tcl_GetStringFromObj(objv[i], &length); + + if (length < 2) { + continue; + } + c = arg[1]; + if (((c == 'c') && (length >= 2) + && (strncmp(arg, "-class", (unsigned)length) == 0)) + || ((c == 'c') && (length >= 3) + && (strncmp(arg, "-colormap", (unsigned)length) == 0)) + || ((c == 'c') && (length >= 3) + && (strncmp(arg, "-container", (unsigned)length) == 0)) + || ((c == 's') && (framePtr->type == TYPE_TOPLEVEL) + && (strncmp(arg, "-screen", (unsigned)length) == 0)) + || ((c == 'u') && (framePtr->type == TYPE_TOPLEVEL) + && (strncmp(arg, "-use", (unsigned)length) == 0)) + || ((c == 'v') + && (strncmp(arg, "-visual", (unsigned)length) == 0))) { + +#ifdef SUPPORT_CONFIG_EMBEDDED + if (c == 'u') { + const char *string = Tcl_GetString(objv[i+1]); + + if (TkpUseWindow(interp, framePtr->tkwin, + string) != TCL_OK) { + result = TCL_ERROR; + goto done; + } + continue; + } +#endif + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't modify %s option after widget is created", + arg)); + Tcl_SetErrorCode(interp, "TK", "FRAME", "CREATE_ONLY", + NULL); + result = TCL_ERROR; + goto done; + } + } + result = ConfigureFrame(interp, framePtr, objc-2, objv+2); + } + break; + } + + done: + Tcl_Release(framePtr); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * DestroyFrame -- + * + * This function is invoked by Tcl_EventuallyFree or Tcl_Release to clean + * up the internal structure of a frame at a safe time (when no-one is + * using it anymore). + * + * Results: + * None. + * + * Side effects: + * Everything associated with the frame is freed up. + * + *---------------------------------------------------------------------- + */ + +static void +DestroyFrame( + void *memPtr) /* Info about frame widget. */ +{ + register Frame *framePtr = memPtr; + register Labelframe *labelframePtr = memPtr; + + if (framePtr->type == TYPE_LABELFRAME) { + Tk_FreeTextLayout(labelframePtr->textLayout); + if (labelframePtr->textGC != None) { + Tk_FreeGC(framePtr->display, labelframePtr->textGC); + } + } + if (framePtr->colormap != None) { + Tk_FreeColormap(framePtr->display, framePtr->colormap); + } + ckfree(framePtr); +} + +/* + *---------------------------------------------------------------------- + * + * DestroyFramePartly -- + * + * This function is invoked to clean up everything that needs tkwin to be + * defined when deleted. During the destruction process tkwin is always + * set to NULL and this function must be called before that happens. + * + * Results: + * None. + * + * Side effects: + * Some things associated with the frame are freed up. + * + *---------------------------------------------------------------------- + */ + +static void +DestroyFramePartly( + Frame *framePtr) /* Info about frame widget. */ +{ + register Labelframe *labelframePtr = (Labelframe *) framePtr; + + if (framePtr->type == TYPE_LABELFRAME && labelframePtr->labelWin != NULL) { + Tk_DeleteEventHandler(labelframePtr->labelWin, StructureNotifyMask, + FrameStructureProc, framePtr); + Tk_ManageGeometry(labelframePtr->labelWin, NULL, NULL); + if (framePtr->tkwin != Tk_Parent(labelframePtr->labelWin)) { + Tk_UnmaintainGeometry(labelframePtr->labelWin, framePtr->tkwin); + } + Tk_UnmapWindow(labelframePtr->labelWin); + labelframePtr->labelWin = NULL; + } + + Tk_FreeConfigOptions((char *) framePtr, framePtr->optionTable, + framePtr->tkwin); +} + +/* + *---------------------------------------------------------------------- + * + * ConfigureFrame -- + * + * This function is called to process an objv/objc list, plus the Tk + * option database, in order to configure (or reconfigure) a frame + * widget. + * + * Results: + * The return value is a standard Tcl result. If TCL_ERROR is returned, + * then the interp's result contains an error message. + * + * Side effects: + * Configuration information, such as text string, colors, font, etc. get + * set for framePtr; old resources get freed, if there were any. + * + *---------------------------------------------------------------------- + */ + +static int +ConfigureFrame( + Tcl_Interp *interp, /* Used for error reporting. */ + register Frame *framePtr, /* Information about widget; may or may not + * already have values for some fields. */ + int objc, /* Number of valid entries in objv. */ + Tcl_Obj *const objv[]) /* Arguments. */ +{ + Tk_SavedOptions savedOptions; + char *oldMenuName; + Tk_Window oldWindow = NULL; + Labelframe *labelframePtr = (Labelframe *) framePtr; + + /* + * Need the old menubar name for the menu code to delete it. + */ + + if (framePtr->menuName == NULL) { + oldMenuName = NULL; + } else { + oldMenuName = ckalloc(strlen(framePtr->menuName) + 1); + strcpy(oldMenuName, framePtr->menuName); + } + + if (framePtr->type == TYPE_LABELFRAME) { + oldWindow = labelframePtr->labelWin; + } + if (Tk_SetOptions(interp, (char *) framePtr, + framePtr->optionTable, objc, objv, + framePtr->tkwin, &savedOptions, NULL) != TCL_OK) { + if (oldMenuName != NULL) { + ckfree(oldMenuName); + } + return TCL_ERROR; + } + Tk_FreeSavedOptions(&savedOptions); + + /* + * A few of the options require additional processing. + */ + + if ((((oldMenuName == NULL) && (framePtr->menuName != NULL)) + || ((oldMenuName != NULL) && (framePtr->menuName == NULL)) + || ((oldMenuName != NULL) && (framePtr->menuName != NULL) + && strcmp(oldMenuName, framePtr->menuName) != 0)) + && framePtr->type == TYPE_TOPLEVEL) { + TkSetWindowMenuBar(interp, framePtr->tkwin, oldMenuName, + framePtr->menuName); + } + + if (oldMenuName != NULL) { + ckfree(oldMenuName); + } + + if (framePtr->border != NULL) { + Tk_SetBackgroundFromBorder(framePtr->tkwin, framePtr->border); + } else { + Tk_SetWindowBackgroundPixmap(framePtr->tkwin, None); + } + + if (framePtr->highlightWidth < 0) { + framePtr->highlightWidth = 0; + } + if (framePtr->padX < 0) { + framePtr->padX = 0; + } + if (framePtr->padY < 0) { + framePtr->padY = 0; + } + + /* + * If a -labelwidget is specified, check that it is valid and set up + * geometry management for it. + */ + + if (framePtr->type == TYPE_LABELFRAME) { + if (oldWindow != labelframePtr->labelWin) { + if (oldWindow != NULL) { + Tk_DeleteEventHandler(oldWindow, StructureNotifyMask, + FrameStructureProc, framePtr); + Tk_ManageGeometry(oldWindow, NULL, NULL); + Tk_UnmaintainGeometry(oldWindow, framePtr->tkwin); + Tk_UnmapWindow(oldWindow); + } + if (labelframePtr->labelWin != NULL) { + Tk_Window ancestor, parent, sibling = NULL; + + /* + * Make sure that the frame is either the parent of the window + * used as label or a descendant of that parent. Also, don't + * allow a top-level window to be managed inside the frame. + */ + + parent = Tk_Parent(labelframePtr->labelWin); + for (ancestor = framePtr->tkwin; ; + ancestor = Tk_Parent(ancestor)) { + if (ancestor == parent) { + break; + } + sibling = ancestor; + if (Tk_IsTopLevel(ancestor)) { + goto badLabelWindow; + } + } + if (Tk_IsTopLevel(labelframePtr->labelWin)) { + goto badLabelWindow; + } + if (labelframePtr->labelWin == framePtr->tkwin) { + goto badLabelWindow; + } + Tk_CreateEventHandler(labelframePtr->labelWin, + StructureNotifyMask, FrameStructureProc, framePtr); + Tk_ManageGeometry(labelframePtr->labelWin, &frameGeomType, + framePtr); + + /* + * If the frame is not parent to the label, make sure the + * label is above its sibling in the stacking order. + */ + + if (sibling != NULL) { + Tk_RestackWindow(labelframePtr->labelWin, Above, sibling); + } + } + } + } + + FrameWorldChanged(framePtr); + return TCL_OK; + + badLabelWindow: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't use %s as label in this frame", + Tk_PathName(labelframePtr->labelWin))); + Tcl_SetErrorCode(interp, "TK", "GEOMETRY", "HIERARCHY", NULL); + labelframePtr->labelWin = NULL; + return TCL_ERROR; +} + +/* + *--------------------------------------------------------------------------- + * + * FrameWorldChanged -- + * + * This function is called when the world has changed in some way and the + * widget needs to recompute all its graphics contexts and determine its + * new geometry. + * + * Results: + * None. + * + * Side effects: + * Frame will be relayed out and redisplayed. + * + *--------------------------------------------------------------------------- + */ + +static void +FrameWorldChanged( + ClientData instanceData) /* Information about widget. */ +{ + Frame *framePtr = instanceData; + Labelframe *labelframePtr = instanceData; + Tk_Window tkwin = framePtr->tkwin; + XGCValues gcValues; + GC gc; + int anyTextLabel, anyWindowLabel; + int bWidthLeft, bWidthRight, bWidthTop, bWidthBottom; + const char *labelText; + + anyTextLabel = (framePtr->type == TYPE_LABELFRAME) && + (labelframePtr->textPtr != NULL) && + (labelframePtr->labelWin == NULL); + anyWindowLabel = (framePtr->type == TYPE_LABELFRAME) && + (labelframePtr->labelWin != NULL); + + if (framePtr->type == TYPE_LABELFRAME) { + /* + * The textGC is needed even in the labelWin case, so it's always + * created for a labelframe. + */ + + gcValues.font = Tk_FontId(labelframePtr->tkfont); + gcValues.foreground = labelframePtr->textColorPtr->pixel; + gcValues.graphics_exposures = False; + gc = Tk_GetGC(tkwin, GCForeground | GCFont | GCGraphicsExposures, + &gcValues); + if (labelframePtr->textGC != None) { + Tk_FreeGC(framePtr->display, labelframePtr->textGC); + } + labelframePtr->textGC = gc; + + /* + * Calculate label size. + */ + + labelframePtr->labelReqWidth = labelframePtr->labelReqHeight = 0; + + if (anyTextLabel) { + labelText = Tcl_GetString(labelframePtr->textPtr); + Tk_FreeTextLayout(labelframePtr->textLayout); + labelframePtr->textLayout = + Tk_ComputeTextLayout(labelframePtr->tkfont, + labelText, -1, 0, TK_JUSTIFY_CENTER, 0, + &labelframePtr->labelReqWidth, + &labelframePtr->labelReqHeight); + labelframePtr->labelReqWidth += 2 * LABELSPACING; + labelframePtr->labelReqHeight += 2 * LABELSPACING; + } else if (anyWindowLabel) { + labelframePtr->labelReqWidth = Tk_ReqWidth(labelframePtr->labelWin); + labelframePtr->labelReqHeight = + Tk_ReqHeight(labelframePtr->labelWin); + } + + /* + * Make sure label size is at least as big as the border. This + * simplifies later calculations and gives a better appearance with + * thick borders. + */ + + if ((labelframePtr->labelAnchor >= LABELANCHOR_N) && + (labelframePtr->labelAnchor <= LABELANCHOR_SW)) { + if (labelframePtr->labelReqHeight < framePtr->borderWidth) { + labelframePtr->labelReqHeight = framePtr->borderWidth; + } + } else { + if (labelframePtr->labelReqWidth < framePtr->borderWidth) { + labelframePtr->labelReqWidth = framePtr->borderWidth; + } + } + } + + /* + * Calculate individual border widths. + */ + + bWidthBottom = bWidthTop = bWidthRight = bWidthLeft = + framePtr->borderWidth + framePtr->highlightWidth; + + bWidthLeft += framePtr->padX; + bWidthRight += framePtr->padX; + bWidthTop += framePtr->padY; + bWidthBottom += framePtr->padY; + + if (anyTextLabel || anyWindowLabel) { + switch (labelframePtr->labelAnchor) { + case LABELANCHOR_E: + case LABELANCHOR_EN: + case LABELANCHOR_ES: + bWidthRight += labelframePtr->labelReqWidth - + framePtr->borderWidth; + break; + case LABELANCHOR_N: + case LABELANCHOR_NE: + case LABELANCHOR_NW: + bWidthTop += labelframePtr->labelReqHeight - framePtr->borderWidth; + break; + case LABELANCHOR_S: + case LABELANCHOR_SE: + case LABELANCHOR_SW: + bWidthBottom += labelframePtr->labelReqHeight - + framePtr->borderWidth; + break; + default: + bWidthLeft += labelframePtr->labelReqWidth - framePtr->borderWidth; + break; + } + } + + Tk_SetInternalBorderEx(tkwin, bWidthLeft, bWidthRight, bWidthTop, + bWidthBottom); + + ComputeFrameGeometry(framePtr); + + /* + * A labelframe should request size for its label. + */ + + if (framePtr->type == TYPE_LABELFRAME) { + int minwidth = labelframePtr->labelReqWidth; + int minheight = labelframePtr->labelReqHeight; + int padding = framePtr->highlightWidth; + + if (framePtr->borderWidth > 0) { + padding += framePtr->borderWidth + LABELMARGIN; + } + padding *= 2; + if ((labelframePtr->labelAnchor >= LABELANCHOR_N) && + (labelframePtr->labelAnchor <= LABELANCHOR_SW)) { + minwidth += padding; + minheight += framePtr->borderWidth + framePtr->highlightWidth; + } else { + minheight += padding; + minwidth += framePtr->borderWidth + framePtr->highlightWidth; + } + Tk_SetMinimumRequestSize(tkwin, minwidth, minheight); + } + + if ((framePtr->width > 0) || (framePtr->height > 0)) { + Tk_GeometryRequest(tkwin, framePtr->width, framePtr->height); + } + + if (Tk_IsMapped(tkwin)) { + if (!(framePtr->flags & REDRAW_PENDING)) { + Tcl_DoWhenIdle(DisplayFrame, framePtr); + } + framePtr->flags |= REDRAW_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * ComputeFrameGeometry -- + * + * This function is called to compute various geometrical information for + * a frame, such as where various things get displayed. It's called when + * the window is reconfigured. + * + * Results: + * None. + * + * Side effects: + * Display-related numbers get changed in *framePtr. + * + *---------------------------------------------------------------------- + */ + +static void +ComputeFrameGeometry( + register Frame *framePtr) /* Information about widget. */ +{ + int otherWidth, otherHeight, otherWidthT, otherHeightT, padding; + int maxWidth, maxHeight; + Tk_Window tkwin; + Labelframe *labelframePtr = (Labelframe *) framePtr; + + /* + * We have nothing to do here unless there is a label. + */ + + if (framePtr->type != TYPE_LABELFRAME) { + return; + } + if (labelframePtr->textPtr == NULL && labelframePtr->labelWin == NULL) { + return; + } + + tkwin = framePtr->tkwin; + + /* + * Calculate the available size for the label + */ + + labelframePtr->labelBox.width = labelframePtr->labelReqWidth; + labelframePtr->labelBox.height = labelframePtr->labelReqHeight; + + padding = framePtr->highlightWidth; + if (framePtr->borderWidth > 0) { + padding += framePtr->borderWidth + LABELMARGIN; + } + padding *= 2; + + maxHeight = Tk_Height(tkwin); + maxWidth = Tk_Width(tkwin); + + if ((labelframePtr->labelAnchor >= LABELANCHOR_N) && + (labelframePtr->labelAnchor <= LABELANCHOR_SW)) { + maxWidth -= padding; + if (maxWidth < 1) { + maxWidth = 1; + } + } else { + maxHeight -= padding; + if (maxHeight < 1) { + maxHeight = 1; + } + } + if (labelframePtr->labelBox.width > maxWidth) { + labelframePtr->labelBox.width = maxWidth; + } + if (labelframePtr->labelBox.height > maxHeight) { + labelframePtr->labelBox.height = maxHeight; + } + + /* + * Calculate label and text position. The text's position is based on the + * requested size (= the text's real size) to get proper alignment if the + * text does not fit. + */ + + otherWidth = Tk_Width(tkwin) - labelframePtr->labelBox.width; + otherHeight = Tk_Height(tkwin) - labelframePtr->labelBox.height; + otherWidthT = Tk_Width(tkwin) - labelframePtr->labelReqWidth; + otherHeightT = Tk_Height(tkwin) - labelframePtr->labelReqHeight; + padding = framePtr->highlightWidth; + + switch (labelframePtr->labelAnchor) { + case LABELANCHOR_E: + case LABELANCHOR_EN: + case LABELANCHOR_ES: + labelframePtr->labelTextX = otherWidthT - padding; + labelframePtr->labelBox.x = otherWidth - padding; + break; + case LABELANCHOR_N: + case LABELANCHOR_NE: + case LABELANCHOR_NW: + labelframePtr->labelTextY = padding; + labelframePtr->labelBox.y = padding; + break; + case LABELANCHOR_S: + case LABELANCHOR_SE: + case LABELANCHOR_SW: + labelframePtr->labelTextY = otherHeightT - padding; + labelframePtr->labelBox.y = otherHeight - padding; + break; + default: + labelframePtr->labelTextX = padding; + labelframePtr->labelBox.x = padding; + break; + } + + if (framePtr->borderWidth > 0) { + padding += framePtr->borderWidth + LABELMARGIN; + } + + switch (labelframePtr->labelAnchor) { + case LABELANCHOR_NW: + case LABELANCHOR_SW: + labelframePtr->labelTextX = padding; + labelframePtr->labelBox.x = padding; + break; + case LABELANCHOR_N: + case LABELANCHOR_S: + labelframePtr->labelTextX = otherWidthT / 2; + labelframePtr->labelBox.x = otherWidth / 2; + break; + case LABELANCHOR_NE: + case LABELANCHOR_SE: + labelframePtr->labelTextX = otherWidthT - padding; + labelframePtr->labelBox.x = otherWidth - padding; + break; + case LABELANCHOR_EN: + case LABELANCHOR_WN: + labelframePtr->labelTextY = padding; + labelframePtr->labelBox.y = padding; + break; + case LABELANCHOR_E: + case LABELANCHOR_W: + labelframePtr->labelTextY = otherHeightT / 2; + labelframePtr->labelBox.y = otherHeight / 2; + break; + default: + labelframePtr->labelTextY = otherHeightT - padding; + labelframePtr->labelBox.y = otherHeight - padding; + break; + } +} + +/* + *---------------------------------------------------------------------- + * + * DisplayFrame -- + * + * This function is invoked to display a frame widget. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the frame in its current mode. + * + *---------------------------------------------------------------------- + */ + +static void +DisplayFrame( + ClientData clientData) /* Information about widget. */ +{ + register Frame *framePtr = clientData; + register Tk_Window tkwin = framePtr->tkwin; + int bdX1, bdY1, bdX2, bdY2, hlWidth; + Pixmap pixmap; + TkRegion clipRegion = NULL; + + framePtr->flags &= ~REDRAW_PENDING; + if ((framePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { + return; + } + + /* + * Highlight shall always be drawn if it exists, so do that first. + */ + + hlWidth = framePtr->highlightWidth; + + if (hlWidth != 0) { + GC fgGC, bgGC; + + bgGC = Tk_GCForColor(framePtr->highlightBgColorPtr, + Tk_WindowId(tkwin)); + if (framePtr->flags & GOT_FOCUS) { + fgGC = Tk_GCForColor(framePtr->highlightColorPtr, + Tk_WindowId(tkwin)); + TkpDrawHighlightBorder(tkwin, fgGC, bgGC, hlWidth, + Tk_WindowId(tkwin)); + } else { + TkpDrawHighlightBorder(tkwin, bgGC, bgGC, hlWidth, + Tk_WindowId(tkwin)); + } + } + + /* + * If -background is set to "", no interior is drawn. + */ + + if (framePtr->border == NULL) { + return; + } + + if (framePtr->type != TYPE_LABELFRAME) { + /* + * Pass to platform specific draw function. In general, it just draws + * a simple rectangle, but it may "theme" the background. + */ + + noLabel: + TkpDrawFrame(tkwin, framePtr->border, hlWidth, + framePtr->borderWidth, framePtr->relief); + } else { + Labelframe *labelframePtr = (Labelframe *) framePtr; + + if ((labelframePtr->textPtr == NULL) && + (labelframePtr->labelWin == NULL)) { + goto noLabel; + } + +#ifndef TK_NO_DOUBLE_BUFFERING + /* + * In order to avoid screen flashes, this function redraws the frame + * into off-screen memory, then copies it back on-screen in a single + * operation. This means there's no point in time where the on-screen + * image has been cleared. + */ + + pixmap = Tk_GetPixmap(framePtr->display, Tk_WindowId(tkwin), + Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); +#else + pixmap = Tk_WindowId(tkwin); +#endif /* TK_NO_DOUBLE_BUFFERING */ + + /* + * Clear the pixmap. + */ + + Tk_Fill3DRectangle(tkwin, pixmap, framePtr->border, 0, 0, + Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); + + /* + * Calculate how the label affects the border's position. + */ + + bdX1 = bdY1 = hlWidth; + bdX2 = Tk_Width(tkwin) - hlWidth; + bdY2 = Tk_Height(tkwin) - hlWidth; + + switch (labelframePtr->labelAnchor) { + case LABELANCHOR_E: + case LABELANCHOR_EN: + case LABELANCHOR_ES: + bdX2 -= (labelframePtr->labelBox.width-framePtr->borderWidth) / 2; + break; + case LABELANCHOR_N: + case LABELANCHOR_NE: + case LABELANCHOR_NW: + /* + * Since the glyphs of the text tend to be in the lower part we + * favor a lower border position by rounding up. + */ + + bdY1 += (labelframePtr->labelBox.height-framePtr->borderWidth+1)/2; + break; + case LABELANCHOR_S: + case LABELANCHOR_SE: + case LABELANCHOR_SW: + bdY2 -= (labelframePtr->labelBox.height-framePtr->borderWidth) / 2; + break; + default: + bdX1 += (labelframePtr->labelBox.width-framePtr->borderWidth) / 2; + break; + } + + /* + * Draw border + */ + + Tk_Draw3DRectangle(tkwin, pixmap, framePtr->border, bdX1, bdY1, + bdX2 - bdX1, bdY2 - bdY1, framePtr->borderWidth, + framePtr->relief); + + if (labelframePtr->labelWin == NULL) { + /* + * Clear behind the label + */ + + Tk_Fill3DRectangle(tkwin, pixmap, + framePtr->border, labelframePtr->labelBox.x, + labelframePtr->labelBox.y, labelframePtr->labelBox.width, + labelframePtr->labelBox.height, 0, TK_RELIEF_FLAT); + + /* + * Draw label. If there is not room for the entire label, use + * clipping to get a nice appearance. + */ + + if ((labelframePtr->labelBox.width < labelframePtr->labelReqWidth) + || (labelframePtr->labelBox.height < + labelframePtr->labelReqHeight)) { + clipRegion = TkCreateRegion(); + TkUnionRectWithRegion(&labelframePtr->labelBox, clipRegion, + clipRegion); + TkSetRegion(framePtr->display, labelframePtr->textGC, + clipRegion); + } + + Tk_DrawTextLayout(framePtr->display, pixmap, + labelframePtr->textGC, labelframePtr->textLayout, + labelframePtr->labelTextX + LABELSPACING, + labelframePtr->labelTextY + LABELSPACING, 0, -1); + + if (clipRegion != NULL) { + XSetClipMask(framePtr->display, labelframePtr->textGC, None); + TkDestroyRegion(clipRegion); + } + } else { + /* + * Reposition and map the window (but in different ways depending + * on whether the frame is the window's parent). + */ + + if (framePtr->tkwin == Tk_Parent(labelframePtr->labelWin)) { + if ((labelframePtr->labelBox.x != Tk_X(labelframePtr->labelWin)) + || (labelframePtr->labelBox.y != + Tk_Y(labelframePtr->labelWin)) + || (labelframePtr->labelBox.width != + Tk_Width(labelframePtr->labelWin)) + || (labelframePtr->labelBox.height != + Tk_Height(labelframePtr->labelWin))) { + Tk_MoveResizeWindow(labelframePtr->labelWin, + labelframePtr->labelBox.x, + labelframePtr->labelBox.y, + labelframePtr->labelBox.width, + labelframePtr->labelBox.height); + } + Tk_MapWindow(labelframePtr->labelWin); + } else { + Tk_MaintainGeometry(labelframePtr->labelWin, framePtr->tkwin, + labelframePtr->labelBox.x, labelframePtr->labelBox.y, + labelframePtr->labelBox.width, + labelframePtr->labelBox.height); + } + } + +#ifndef TK_NO_DOUBLE_BUFFERING + /* + * Everything's been redisplayed; now copy the pixmap onto the screen + * and free up the pixmap. + */ + + XCopyArea(framePtr->display, pixmap, Tk_WindowId(tkwin), + labelframePtr->textGC, hlWidth, hlWidth, + (unsigned) (Tk_Width(tkwin) - 2 * hlWidth), + (unsigned) (Tk_Height(tkwin) - 2 * hlWidth), + hlWidth, hlWidth); + Tk_FreePixmap(framePtr->display, pixmap); +#endif /* TK_NO_DOUBLE_BUFFERING */ + } + +} + +/* + *-------------------------------------------------------------- + * + * FrameEventProc -- + * + * This function is invoked by the Tk dispatcher on structure changes to + * a frame. For frames with 3D borders, this function is also invoked for + * exposures. + * + * Results: + * None. + * + * Side effects: + * When the window gets deleted, internal structures get cleaned up. + * When it gets exposed, it is redisplayed. + * + *-------------------------------------------------------------- + */ + +static void +FrameEventProc( + ClientData clientData, /* Information about window. */ + register XEvent *eventPtr) /* Information about event. */ +{ + register Frame *framePtr = clientData; + + if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { + goto redraw; + } else if (eventPtr->type == ConfigureNotify) { + ComputeFrameGeometry(framePtr); + goto redraw; + } else if (eventPtr->type == DestroyNotify) { + if (framePtr->menuName != NULL) { + TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin, + framePtr->menuName, NULL); + ckfree(framePtr->menuName); + framePtr->menuName = NULL; + } + if (framePtr->tkwin != NULL) { + /* + * If this window is a container, then this event could be coming + * from the embedded application, in which case Tk_DestroyWindow + * hasn't been called yet. When Tk_DestroyWindow is called later, + * then another destroy event will be generated. We need to be + * sure we ignore the second event, since the frame could be gone + * by then. To do so, delete the event handler explicitly + * (normally it's done implicitly by Tk_DestroyWindow). + */ + + /* + * Since the tkwin pointer will be gone when we reach + * DestroyFrame, we must free all options now. + */ + + DestroyFramePartly(framePtr); + + Tk_DeleteEventHandler(framePtr->tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + FrameEventProc, framePtr); + framePtr->tkwin = NULL; + Tcl_DeleteCommandFromToken(framePtr->interp, framePtr->widgetCmd); + } + if (framePtr->flags & REDRAW_PENDING) { + Tcl_CancelIdleCall(DisplayFrame, framePtr); + } + Tcl_CancelIdleCall(MapFrame, framePtr); + Tcl_EventuallyFree(framePtr, (Tcl_FreeProc *) DestroyFrame); + } else if (eventPtr->type == FocusIn) { + if (eventPtr->xfocus.detail != NotifyInferior) { + framePtr->flags |= GOT_FOCUS; + if (framePtr->highlightWidth > 0) { + goto redraw; + } + } + } else if (eventPtr->type == FocusOut) { + if (eventPtr->xfocus.detail != NotifyInferior) { + framePtr->flags &= ~GOT_FOCUS; + if (framePtr->highlightWidth > 0) { + goto redraw; + } + } + } else if (eventPtr->type == ActivateNotify) { + TkpSetMainMenubar(framePtr->interp, framePtr->tkwin, + framePtr->menuName); + } + return; + + redraw: + if ((framePtr->tkwin != NULL) && !(framePtr->flags & REDRAW_PENDING)) { + Tcl_DoWhenIdle(DisplayFrame, framePtr); + framePtr->flags |= REDRAW_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * FrameCmdDeletedProc -- + * + * This function is invoked when a widget command is deleted. If the + * widget isn't already in the process of being destroyed, this command + * destroys it. + * + * Results: + * None. + * + * Side effects: + * The widget is destroyed. + * + *---------------------------------------------------------------------- + */ + +static void +FrameCmdDeletedProc( + ClientData clientData) /* Pointer to widget record for widget. */ +{ + Frame *framePtr = clientData; + Tk_Window tkwin = framePtr->tkwin; + + if (framePtr->menuName != NULL) { + TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin, + framePtr->menuName, NULL); + ckfree(framePtr->menuName); + framePtr->menuName = NULL; + } + + /* + * This function could be invoked either because the window was destroyed + * and the command was then deleted (in which case tkwin is NULL) or + * because the command was deleted, and then this function destroys the + * widget. + */ + + if (tkwin != NULL) { + /* + * Some options need tkwin to be freed, so we free them here, before + * setting tkwin to NULL. + */ + + DestroyFramePartly(framePtr); + + framePtr->tkwin = NULL; + Tk_DestroyWindow(tkwin); + } +} + +/* + *---------------------------------------------------------------------- + * + * MapFrame -- + * + * This function is invoked as a when-idle handler to map a newly-created + * top-level frame. + * + * Results: + * None. + * + * Side effects: + * The frame given by the clientData argument is mapped. + * + *---------------------------------------------------------------------- + */ + +static void +MapFrame( + ClientData clientData) /* Pointer to frame structure. */ +{ + Frame *framePtr = clientData; + + /* + * Wait for all other background events to be processed before mapping + * window. This ensures that the window's correct geometry will have been + * determined before it is first mapped, so that the window manager + * doesn't get a false idea of its desired geometry. + */ + + Tcl_Preserve(framePtr); + while (1) { + if (Tcl_DoOneEvent(TCL_IDLE_EVENTS) == 0) { + break; + } + + /* + * After each event, make sure that the window still exists and quit + * if the window has been destroyed. + */ + + if (framePtr->tkwin == NULL) { + Tcl_Release(framePtr); + return; + } + } + Tk_MapWindow(framePtr->tkwin); + Tcl_Release(framePtr); +} + +/* + *-------------------------------------------------------------- + * + * TkInstallFrameMenu -- + * + * This function is needed when a Windows HWND is created and a menubar + * has been set to the window with a system menu. It notifies the menu + * package so that the system menu can be rebuilt. + * + * Results: + * None. + * + * Side effects: + * The system menu (if any) is created for the menubar associated with + * this frame. + * + *-------------------------------------------------------------- + */ + +void +TkInstallFrameMenu( + Tk_Window tkwin) /* The window that was just created. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + + if (winPtr->mainPtr != NULL) { + Frame *framePtr = winPtr->instanceData; + + if (framePtr == NULL) { + Tcl_Panic("TkInstallFrameMenu couldn't get frame pointer"); + } + TkpMenuNotifyToplevelCreate(winPtr->mainPtr->interp, + framePtr->menuName); + } +} + +/* + *-------------------------------------------------------------- + * + * FrameStructureProc -- + * + * This function is invoked whenever StructureNotify events occur for a + * window that's managed as label for the frame. This procudure's only + * purpose is to clean up when windows are deleted. + * + * Results: + * None. + * + * Side effects: + * The window is disassociated from the frame when it is deleted. + * + *-------------------------------------------------------------- + */ + +static void +FrameStructureProc( + ClientData clientData, /* Pointer to record describing frame. */ + XEvent *eventPtr) /* Describes what just happened. */ +{ + Labelframe *labelframePtr = clientData; + + if (eventPtr->type == DestroyNotify) { + /* + * This should only happen in a labelframe but it doesn't hurt to be + * careful. + */ + + if (labelframePtr->frame.type == TYPE_LABELFRAME) { + labelframePtr->labelWin = NULL; + FrameWorldChanged(labelframePtr); + } + } +} + +/* + *-------------------------------------------------------------- + * + * FrameRequestProc -- + * + * This function is invoked whenever a window that's associated with a + * frame changes its requested dimensions. + * + * Results: + * None. + * + * Side effects: + * The size and location on the screen of the window may change depending + * on the options specified for the frame. + * + *-------------------------------------------------------------- + */ + +static void +FrameRequestProc( + ClientData clientData, /* Pointer to record for frame. */ + Tk_Window tkwin) /* Window that changed its desired size. */ +{ + Frame *framePtr = clientData; + + FrameWorldChanged(framePtr); +} + +/* + *-------------------------------------------------------------- + * + * FrameLostSlaveProc -- + * + * This function is invoked by Tk whenever some other geometry claims + * control over a slave that used to be managed by us. + * + * Results: + * None. + * + * Side effects: + * Forgets all frame-related information about the slave. + * + *-------------------------------------------------------------- + */ + +static void +FrameLostSlaveProc( + ClientData clientData, /* Frame structure for slave window that was + * stolen away. */ + Tk_Window tkwin) /* Tk's handle for the slave window. */ +{ + Frame *framePtr = clientData; + Labelframe *labelframePtr = clientData; + + /* + * This should only happen in a labelframe but it doesn't hurt to be + * careful. + */ + + if (labelframePtr->frame.type == TYPE_LABELFRAME) { + Tk_DeleteEventHandler(labelframePtr->labelWin, StructureNotifyMask, + FrameStructureProc, labelframePtr); + if (framePtr->tkwin != Tk_Parent(labelframePtr->labelWin)) { + Tk_UnmaintainGeometry(labelframePtr->labelWin, framePtr->tkwin); + } + Tk_UnmapWindow(labelframePtr->labelWin); + labelframePtr->labelWin = NULL; + } + FrameWorldChanged(framePtr); +} + +void +TkMapTopFrame( + Tk_Window tkwin) +{ + Frame *framePtr = ((TkWindow *) tkwin)->instanceData; + Tk_OptionTable optionTable; + + if (Tk_IsTopLevel(tkwin) && framePtr->type == TYPE_FRAME) { + framePtr->type = TYPE_TOPLEVEL; + Tcl_DoWhenIdle(MapFrame, framePtr); + if (framePtr->menuName != NULL) { + TkSetWindowMenuBar(framePtr->interp, framePtr->tkwin, NULL, + framePtr->menuName); + } + } else if (!Tk_IsTopLevel(tkwin) && framePtr->type == TYPE_TOPLEVEL) { + framePtr->type = TYPE_FRAME; + } else { + /* + * Not a frame or toplevel, skip it. + */ + + return; + } + + /* + * The option table has already been created so the cached pointer will be + * returned. + */ + + optionTable = Tk_CreateOptionTable(framePtr->interp, + optionSpecs[framePtr->type]); + framePtr->optionTable = optionTable; +} + +/* + *-------------------------------------------------------------- + * + * TkToplevelWindowFromCommandToken -- + * + * If the given command name to the command for a toplevel window in the + * given interpreter, return the tkwin for that toplevel window. Note + * that this lookup can't be done using the standard tkwin internal table + * because the command might have been renamed. + * + * Results: + * A Tk_Window token, or NULL if the name does not refer to a toplevel + * window. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +Tk_Window +TkToplevelWindowForCommand( + Tcl_Interp *interp, + const char *cmdName) +{ + Tcl_CmdInfo cmdInfo; + Frame *framePtr; + + if (Tcl_GetCommandInfo(interp, cmdName, &cmdInfo) == 0) { + return NULL; + } + if (cmdInfo.objProc != FrameWidgetObjCmd) { + return NULL; + } + framePtr = cmdInfo.objClientData; + if (framePtr->type != TYPE_TOPLEVEL) { + return NULL; + } + return framePtr->tkwin; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |