/* * tkFrame.c (CTk) -- * * This module implements "frame" 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-1995 Sun Microsystems, Inc. * Copyright (c) 1994-1995 Cleveland Clinic Foundation * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * @(#) $Id: ctk.shar,v 1.50 1996/01/15 14:47:16 andrewm Exp andrewm $ */ #include "default.h" #include "tkPort.h" #include "tkInt.h" /* * 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. */ Tcl_Interp *interp; /* Interpreter associated with widget. Used * to delete widget command. */ Tcl_Command widgetCmd; /* Token for frame's widget command. */ char *className; /* Class name for widget (from configuration * option). Malloc-ed. */ int mask; /* Either FRAME or TOPLEVEL; used to select * which configuration options are valid for * widget. */ char *screenName; /* Screen on which widget is created. Non-null * only for top-levels. Malloc-ed, may be * NULL. */ int borderWidth; /* Width of 3-D border (if any). */ 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. */ char *title; /* Title of window. Only valid for toplevels * Malloc-ed, may be null. */ 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 flags; /* Various flags; see below for * definitions. */ } Frame; /* * Flag bits for frames: * * REDRAW_PENDING: Non-zero means a DoWhenIdle handler * has already been queued to redraw * this window. * CLEAR_NEEDED; Need to clear the window when redrawing. */ #define REDRAW_PENDING 1 #define CLEAR_NEEDED 2 /* * The following flag bits are used so that there can be separate * defaults for some configuration options for frames and toplevels. */ #define FRAME TK_CONFIG_USER_BIT #define TOPLEVEL (TK_CONFIG_USER_BIT << 1) #define BOTH (FRAME | TOPLEVEL) static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, 0, BOTH}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_FRAME_BORDER_WIDTH, Tk_Offset(Frame, borderWidth), FRAME}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", DEF_TOPLEVEL_BORDER_WIDTH, Tk_Offset(Frame, borderWidth), TOPLEVEL}, {TK_CONFIG_STRING, "-class", "class", "Class", DEF_FRAME_CLASS, Tk_Offset(Frame, className), FRAME}, {TK_CONFIG_STRING, "-class", "class", "Class", DEF_TOPLEVEL_CLASS, Tk_Offset(Frame, className), TOPLEVEL}, {TK_CONFIG_PIXELS, "-height", "height", "Height", DEF_FRAME_HEIGHT, Tk_Offset(Frame, height), BOTH}, {TK_CONFIG_STRING, "-screen", "screen", "Screen", DEF_TOPLEVEL_SCREEN, Tk_Offset(Frame, screenName), TOPLEVEL|TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", DEF_FRAME_TAKE_FOCUS, Tk_Offset(Frame, takeFocus), BOTH|TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-title", "title", "Title", DEF_TOPLEVEL_TITLE, Tk_Offset(Frame, title), TOPLEVEL|TK_CONFIG_NULL_OK}, {TK_CONFIG_PIXELS, "-width", "width", "Width", DEF_FRAME_WIDTH, Tk_Offset(Frame, width), BOTH}, {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; /* * Forward declarations for procedures defined later in this file: */ static int ConfigureFrame _ANSI_ARGS_((Tcl_Interp *interp, Frame *framePtr, int argc, char **argv, int flags)); static void DestroyFrame _ANSI_ARGS_((ClientData clientData)); static void DisplayFrame _ANSI_ARGS_((ClientData clientData)); static void FrameCmdDeletedProc _ANSI_ARGS_(( ClientData clientData)); static void FrameEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static int FrameWidgetCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, int argc, char **argv)); /* *-------------------------------------------------------------- * * Tk_FrameCmd -- * * This procedure is invoked to process the "frame" and * "toplevel" Tcl commands. See the user documentation for * details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *-------------------------------------------------------------- */ int Tk_FrameCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window associated with * interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { Tk_Window tkwin = (Tk_Window) clientData; Frame *framePtr; Tk_Window new = NULL; char *className, *screenName, *arg; int i, c, length, toplevel; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " pathName ?options?\"", (char *) NULL); return TCL_ERROR; } /* * Pre-process the argument list. Scan through it to find any * "-class" and "-screen" options. These * arguments need to be processed specially, before the window * is configured using the usual Tk mechanisms. */ toplevel = (argv[0][0] == 't'); className = screenName = NULL; for (i = 2; i < argc; i += 2) { arg = argv[i]; length = strlen(arg); if (length < 2) { continue; } c = arg[1]; if ((c == 'c') && (strncmp(arg, "-class", strlen(arg)) == 0) && (length >= 3)) { className = argv[i+1]; } else if ((c == 's') && toplevel && (strncmp(arg, "-screen", strlen(arg)) == 0)) { screenName = argv[i+1]; } } /* * Create the window, and deal with the special options -classname, * and -screenname. The order here is tricky, * because we want to allow values for these options to come from * the database, yet we can't do that until the window is created. */ if (screenName == NULL) { screenName = (toplevel) ? "" : NULL; } new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], screenName); if (new == NULL) { goto error; } if (className == NULL) { className = Tk_GetOption(new, "class", "Class"); if (className == NULL) { className = (toplevel) ? "Toplevel" : "Frame"; } } Tk_SetClass(new, className); /* * 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. */ framePtr = (Frame *) TkInitFrame(interp, new, toplevel, argc-2, argv+2); if (framePtr == NULL) { return TCL_ERROR; } return TCL_OK; error: if (new != NULL) { Tk_DestroyWindow(new); } return TCL_ERROR; } /* *---------------------------------------------------------------------- * * TkInitFrame -- * * This procedure initializes a frame or toplevel widget. It's * separate from Tk_FrameCmd so that it can be used for the * main window, which has already been created elsewhere. * * Results: * Returns NULL if an error occurred while initializing the * frame. Otherwise returns a pointer to the frame's widget * record (for use by Tk_FrameCmd, if it was the caller). * * Side effects: * A widget record gets allocated, handlers get set up, etc.. * *---------------------------------------------------------------------- */ char * TkInitFrame(interp, tkwin, toplevel, argc, argv) Tcl_Interp *interp; /* Interpreter associated with the * application. */ Tk_Window tkwin; /* Window to use for frame or * top-level. Caller must already * have set window's class. */ int toplevel; /* Non-zero means that this is a * top-level window, 0 means it's a * frame. */ int argc; /* Number of configuration arguments * (not including class command and * window name). */ char *argv[]; /* Configuration arguments. */ { register Frame *framePtr; framePtr = (Frame *) ckalloc(sizeof(Frame)); framePtr->tkwin = tkwin; framePtr->interp = interp; framePtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(framePtr->tkwin), FrameWidgetCmd, (ClientData) framePtr, FrameCmdDeletedProc); framePtr->className = NULL; framePtr->mask = (toplevel) ? TOPLEVEL : FRAME; framePtr->screenName = NULL; framePtr->borderWidth = 0; framePtr->width = 0; framePtr->height = 0; framePtr->title = NULL; framePtr->takeFocus = NULL; framePtr->flags = 0; Tk_CreateEventHandler(framePtr->tkwin, CTK_EXPOSE_EVENT_MASK|CTK_DESTROY_EVENT_MASK, FrameEventProc, (ClientData) framePtr); if (ConfigureFrame(interp, framePtr, argc, argv, 0) != TCL_OK) { Tk_DestroyWindow(framePtr->tkwin); return NULL; } if (toplevel) { char *placeArgv[9]; placeArgv[0] = "place"; placeArgv[1] = Tk_PathName(framePtr->tkwin); placeArgv[2] = "-relx"; placeArgv[3] = "0.5"; placeArgv[4] = "-rely"; placeArgv[5] = "0.5"; placeArgv[6] = "-anchor"; placeArgv[7] = "center"; placeArgv[8] = NULL; if (Tk_PlaceCmd((ClientData) framePtr->tkwin, interp, 8, placeArgv) != TCL_OK) { panic("place failed for toplevel: %s: %s", placeArgv[1], interp->result); } } else { tkwin->fillStyle = CTK_INVISIBLE_STYLE; } Tcl_SetResult(interp,Tk_PathName(framePtr->tkwin),TCL_VOLATILE); return (char *) framePtr; } /* *-------------------------------------------------------------- * * FrameWidgetCmd -- * * This procedure 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 FrameWidgetCmd(clientData, interp, argc, argv) ClientData clientData; /* Information about frame widget. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { register Frame *framePtr = (Frame *) clientData; int result = TCL_OK; size_t length; int c, i; if (argc < 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " option ?arg arg ...?\"", (char *) NULL); return TCL_ERROR; } Tk_Preserve((ClientData) framePtr); c = argv[1][0]; length = strlen(argv[1]); if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0) && (length >= 2)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " cget option\"", (char *) NULL); result = TCL_ERROR; goto done; } result = Tk_ConfigureValue(interp, framePtr->tkwin, configSpecs, (char *) framePtr, argv[2], framePtr->mask); } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0) && (length >= 2)) { if (argc == 2) { result = Tk_ConfigureInfo(interp, framePtr->tkwin, configSpecs, (char *) framePtr, (char *) NULL, framePtr->mask); } else if (argc == 3) { result = Tk_ConfigureInfo(interp, framePtr->tkwin, configSpecs, (char *) framePtr, argv[2], framePtr->mask); } else { /* * Don't allow the options -class, -newcmap, -screen, * or -visual to be changed. */ for (i = 2; i < argc; i++) { length = strlen(argv[i]); if (length < 2) { continue; } c = argv[i][1]; if (((c == 'c') && (strncmp(argv[i], "-class", length) == 0) && (length >= 2)) || ((c == 'c') && (framePtr->mask == TOPLEVEL) && (strncmp(argv[i], "-colormap", length) == 0)) || ((c == 's') && (framePtr->mask == TOPLEVEL) && (strncmp(argv[i], "-screen", length) == 0)) || ((c == 'v') && (framePtr->mask == TOPLEVEL) && (strncmp(argv[i], "-visual", length) == 0))) { Tcl_AppendResult(interp, "can't modify ", argv[i], " option after widget is created", (char *) NULL); result = TCL_ERROR; goto done; } } result = ConfigureFrame(interp, framePtr, argc-2, argv+2, TK_CONFIG_ARGV_ONLY); } } else { Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be cget or configure", (char *) NULL); result = TCL_ERROR; } done: Tk_Release((ClientData) framePtr); return result; } /* *---------------------------------------------------------------------- * * DestroyFrame -- * * This procedure is invoked by Tk_EventuallyFree or Tk_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(clientData) ClientData clientData; /* Info about frame widget. */ { register Frame *framePtr = (Frame *) clientData; Tk_FreeOptions(configSpecs, (char *) framePtr, framePtr->mask); ckfree((char *) framePtr); } /* *---------------------------------------------------------------------- * * ConfigureFrame -- * * This procedure is called to process an argv/argc 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 interp->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(interp, framePtr, argc, argv, flags) Tcl_Interp *interp; /* Used for error reporting. */ register Frame *framePtr; /* Information about widget; may or may * not already have values for some fields. */ int argc; /* Number of valid entries in argv. */ char **argv; /* Arguments. */ int flags; /* Flags to pass to Tk_ConfigureWidget. */ { if (Tk_ConfigureWidget(interp, framePtr->tkwin, configSpecs, argc, argv, (char *) framePtr, flags | framePtr->mask) != TCL_OK) { return TCL_ERROR; } Tk_SetInternalBorder(framePtr->tkwin, framePtr->borderWidth); if ((framePtr->width > 0) || (framePtr->height > 0)) { Tk_GeometryRequest(framePtr->tkwin, framePtr->width, framePtr->height); } if (Tk_IsMapped(framePtr->tkwin)) { if (!(framePtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayFrame, (ClientData) framePtr); } framePtr->flags |= REDRAW_PENDING|CLEAR_NEEDED; } return TCL_OK; } /* *---------------------------------------------------------------------- * * DisplayFrame -- * * This procedure 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 clientData; /* Information about widget. */ { register Frame *framePtr = (Frame *) clientData; register Tk_Window tkwin = framePtr->tkwin; framePtr->flags &= ~REDRAW_PENDING; if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } Ctk_DrawBorder(tkwin, CTK_PLAIN_STYLE, framePtr->title); } /* *-------------------------------------------------------------- * * FrameEventProc -- * * This procedure is invoked by the Tk dispatcher on * structure changes to a frame. For frames with 3D * borders, this procedure 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, eventPtr) ClientData clientData; /* Information about window. */ register XEvent *eventPtr; /* Information about event. */ { register Frame *framePtr = (Frame *) clientData; if (eventPtr->type == CTK_EXPOSE_EVENT) { if ((framePtr->tkwin != NULL) && !(framePtr->flags & REDRAW_PENDING)) { Tcl_DoWhenIdle(DisplayFrame, (ClientData) framePtr); framePtr->flags |= REDRAW_PENDING; } } else if (eventPtr->type == CTK_DESTROY_EVENT) { if (framePtr->tkwin != NULL) { framePtr->tkwin = NULL; Tcl_DeleteCommand(framePtr->interp, Tcl_GetCommandName(framePtr->interp, framePtr->widgetCmd)); } if (framePtr->flags & REDRAW_PENDING) { Tcl_CancelIdleCall(DisplayFrame, (ClientData) framePtr); } Tk_EventuallyFree((ClientData) framePtr, DestroyFrame); } } /* *---------------------------------------------------------------------- * * FrameCmdDeletedProc -- * * This procedure 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 clientData; /* Pointer to widget record for widget. */ { Frame *framePtr = (Frame *) clientData; Tk_Window tkwin = framePtr->tkwin; /* * This procedure 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 procedure * destroys the widget. */ if (tkwin != NULL) { framePtr->tkwin = NULL; Tk_DestroyWindow(tkwin); } }