4 * This module implements "frame" widgets for the Tk
5 * toolkit. Frames are windows with a background color
6 * and possibly a 3-D effect, but no other attributes.
8 * Copyright 1990 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkFrame.c,v 1.27 92/08/21 16:17:24 ouster Exp $ SPRITE (Berkeley)";
27 * A data structure of the following type is kept for each
28 * frame that currently exists for this process:
32 Tk_Window tkwin
; /* Window that embodies the frame. NULL
33 * means that the window has been destroyed
34 * but the data structures haven't yet been
36 Tcl_Interp
*interp
; /* Interpreter associated with
37 * widget. Used to delete widget
39 Tk_Uid screenName
; /* If this window isn't a toplevel window
40 * then this is NULL; otherwise it gives
41 * the name of the screen on which window
43 Tk_3DBorder border
; /* Structure used to draw 3-D border and
45 int borderWidth
; /* Width of 3-D border (if any). */
46 int relief
; /* 3-d effect: TK_RELIEF_RAISED etc. */
47 int width
; /* Width to request for window. <= 0 means
48 * don't request any size. */
49 int height
; /* Height to request for window. <= 0 means
50 * don't request any size. */
51 char *geometry
; /* Geometry that user requested. NULL
52 * means use width and height instead.
54 Cursor cursor
; /* Current cursor for window, or None. */
55 int flags
; /* Various flags; see below for
60 * Flag bits for frames:
62 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
63 * has already been queued to redraw
65 * CLEAR_NEEDED; Need to clear the window when redrawing.
68 #define REDRAW_PENDING 1
69 #define CLEAR_NEEDED 2
71 static Tk_ConfigSpec configSpecs
[] = {
72 {TK_CONFIG_BORDER
, "-background", "background", "Background",
73 DEF_FRAME_BG_COLOR
, Tk_Offset(Frame
, border
), TK_CONFIG_COLOR_ONLY
},
74 {TK_CONFIG_BORDER
, "-background", "background", "Background",
75 DEF_FRAME_BG_MONO
, Tk_Offset(Frame
, border
), TK_CONFIG_MONO_ONLY
},
76 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
78 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
80 {TK_CONFIG_PIXELS
, "-borderwidth", "borderWidth", "BorderWidth",
81 DEF_FRAME_BORDER_WIDTH
, Tk_Offset(Frame
, borderWidth
), 0},
82 {TK_CONFIG_ACTIVE_CURSOR
, "-cursor", "cursor", "Cursor",
83 DEF_FRAME_CURSOR
, Tk_Offset(Frame
, cursor
), TK_CONFIG_NULL_OK
},
84 {TK_CONFIG_STRING
, "-geometry", "geometry", "Geometry",
85 DEF_FRAME_GEOMETRY
, Tk_Offset(Frame
, geometry
), TK_CONFIG_NULL_OK
},
86 {TK_CONFIG_PIXELS
, "-height", "height", "Height",
87 DEF_FRAME_HEIGHT
, Tk_Offset(Frame
, height
), 0},
88 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
89 DEF_FRAME_RELIEF
, Tk_Offset(Frame
, relief
), 0},
90 {TK_CONFIG_PIXELS
, "-width", "width", "Width",
91 DEF_FRAME_WIDTH
, Tk_Offset(Frame
, width
), 0},
92 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
97 * Forward declarations for procedures defined later in this file:
100 static int ConfigureFrame
_ANSI_ARGS_((Tcl_Interp
*interp
,
101 Frame
*framePtr
, int argc
, char **argv
, int flags
));
102 static void DestroyFrame
_ANSI_ARGS_((ClientData clientData
));
103 static void DisplayFrame
_ANSI_ARGS_((ClientData clientData
));
104 static void FrameEventProc
_ANSI_ARGS_((ClientData clientData
,
106 static int FrameWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
107 Tcl_Interp
*interp
, int argc
, char **argv
));
108 static void MapFrame
_ANSI_ARGS_((ClientData clientData
));
111 *--------------------------------------------------------------
115 * This procedure is invoked to process the "frame" and
116 * "toplevel" Tcl commands. See the user documentation for
117 * details on what it does.
120 * A standard Tcl result.
123 * See the user documentation.
125 *--------------------------------------------------------------
129 Tk_FrameCmd(clientData
, interp
, argc
, argv
)
130 ClientData clientData
; /* Main window associated with
132 Tcl_Interp
*interp
; /* Current interpreter. */
133 int argc
; /* Number of arguments. */
134 char **argv
; /* Argument strings. */
136 Tk_Window tkwin
= (Tk_Window
) clientData
;
138 register Frame
*framePtr
;
140 char *className
, *screen
;
144 Tcl_AppendResult(interp
, "wrong # args: should be \"",
145 argv
[0], " pathName ?options?\"", (char *) NULL
);
150 * The code below is a special workaround that extracts a few key
151 * options from the argument list now, rather than letting
152 * ConfigureFrame do it. This is necessary because we have
153 * to know the window's screen (if it's top-level) and its
154 * class before creating the window.
158 className
= (argv
[0][0] == 't') ? "Toplevel" : "Frame";
159 for (src
= 2, dst
= 2; src
< argc
; src
+= 2) {
164 && (strncmp(argv
[src
], "-class", strlen(argv
[src
])) == 0)) {
165 className
= argv
[src
+1];
166 } else if ((argv
[0][0] == 't') && (c
== 's')
167 && (strncmp(argv
[src
], "-screen", strlen(argv
[src
])) == 0)) {
168 screen
= argv
[src
+1];
170 argv
[dst
] = argv
[src
];
171 argv
[dst
+1] = argv
[src
+1];
178 * Provide a default screen for top-level windows (same as screen
182 if ((argv
[0][0] == 't') && (screen
== NULL
)) {
185 if (screen
!= NULL
) {
186 screenUid
= Tk_GetUid(screen
);
195 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], screenUid
);
201 Tk_SetClass(new, className
);
202 framePtr
= (Frame
*) ckalloc(sizeof(Frame
));
203 framePtr
->tkwin
= new;
204 framePtr
->interp
= interp
;
205 framePtr
->screenName
= screenUid
;
206 framePtr
->border
= NULL
;
207 framePtr
->geometry
= NULL
;
208 framePtr
->cursor
= None
;
210 Tk_CreateEventHandler(framePtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
211 FrameEventProc
, (ClientData
) framePtr
);
212 Tcl_CreateCommand(interp
, Tk_PathName(framePtr
->tkwin
),
213 FrameWidgetCmd
, (ClientData
) framePtr
, (void (*)()) NULL
);
215 if (ConfigureFrame(interp
, framePtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
216 Tk_DestroyWindow(framePtr
->tkwin
);
219 if (screenUid
!= NULL
) {
220 Tk_DoWhenIdle(MapFrame
, (ClientData
) framePtr
);
222 interp
->result
= Tk_PathName(framePtr
->tkwin
);
227 *--------------------------------------------------------------
231 * This procedure is invoked to process the Tcl command
232 * that corresponds to a frame widget. See the user
233 * documentation for details on what it does.
236 * A standard Tcl result.
239 * See the user documentation.
241 *--------------------------------------------------------------
245 FrameWidgetCmd(clientData
, interp
, argc
, argv
)
246 ClientData clientData
; /* Information about frame widget. */
247 Tcl_Interp
*interp
; /* Current interpreter. */
248 int argc
; /* Number of arguments. */
249 char **argv
; /* Argument strings. */
251 register Frame
*framePtr
= (Frame
*) clientData
;
257 Tcl_AppendResult(interp
, "wrong # args: should be \"",
258 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
261 Tk_Preserve((ClientData
) framePtr
);
263 length
= strlen(argv
[1]);
264 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
266 result
= Tk_ConfigureInfo(interp
, framePtr
->tkwin
, configSpecs
,
267 (char *) framePtr
, (char *) NULL
, 0);
268 } else if (argc
== 3) {
269 result
= Tk_ConfigureInfo(interp
, framePtr
->tkwin
, configSpecs
,
270 (char *) framePtr
, argv
[2], 0);
272 result
= ConfigureFrame(interp
, framePtr
, argc
-2, argv
+2,
273 TK_CONFIG_ARGV_ONLY
);
276 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
277 "\": must be configure", (char *) NULL
);
280 Tk_Release((ClientData
) framePtr
);
285 *----------------------------------------------------------------------
289 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
290 * to clean up the internal structure of a frame at a safe time
291 * (when no-one is using it anymore).
297 * Everything associated with the frame is freed up.
299 *----------------------------------------------------------------------
303 DestroyFrame(clientData
)
304 ClientData clientData
; /* Info about frame widget. */
306 register Frame
*framePtr
= (Frame
*) clientData
;
308 if (framePtr
->border
!= NULL
) {
309 Tk_Free3DBorder(framePtr
->border
);
311 if (framePtr
->geometry
!= NULL
) {
312 ckfree(framePtr
->geometry
);
314 if (framePtr
->cursor
!= None
) {
315 Tk_FreeCursor(framePtr
->cursor
);
317 ckfree((char *) framePtr
);
321 *----------------------------------------------------------------------
325 * This procedure is called to process an argv/argc list, plus
326 * the Tk option database, in order to configure (or
327 * reconfigure) a frame widget.
330 * The return value is a standard Tcl result. If TCL_ERROR is
331 * returned, then interp->result contains an error message.
334 * Configuration information, such as text string, colors, font,
335 * etc. get set for framePtr; old resources get freed, if there
338 *----------------------------------------------------------------------
342 ConfigureFrame(interp
, framePtr
, argc
, argv
, flags
)
343 Tcl_Interp
*interp
; /* Used for error reporting. */
344 register Frame
*framePtr
; /* Information about widget; may or may
345 * not already have values for some fields. */
346 int argc
; /* Number of valid entries in argv. */
347 char **argv
; /* Arguments. */
348 int flags
; /* Flags to pass to Tk_ConfigureWidget. */
350 if (Tk_ConfigureWidget(interp
, framePtr
->tkwin
, configSpecs
,
351 argc
, argv
, (char *) framePtr
, flags
) != TCL_OK
) {
355 Tk_SetBackgroundFromBorder(framePtr
->tkwin
, framePtr
->border
);
356 Tk_SetInternalBorder(framePtr
->tkwin
, framePtr
->borderWidth
);
357 if (framePtr
->geometry
!= NULL
) {
359 if (sscanf(framePtr
->geometry
, "%dx%d", &width
, &height
) != 2) {
360 Tcl_AppendResult(interp
, "bad geometry \"", framePtr
->geometry
,
361 "\": expected widthxheight", (char *) NULL
);
364 Tk_GeometryRequest(framePtr
->tkwin
, width
, height
);
365 } else if ((framePtr
->width
> 0) && (framePtr
->height
> 0)) {
366 Tk_GeometryRequest(framePtr
->tkwin
, framePtr
->width
,
370 if (Tk_IsMapped(framePtr
->tkwin
)
371 && !(framePtr
->flags
& REDRAW_PENDING
)) {
372 Tk_DoWhenIdle(DisplayFrame
, (ClientData
) framePtr
);
373 framePtr
->flags
|= REDRAW_PENDING
|CLEAR_NEEDED
;
379 *----------------------------------------------------------------------
383 * This procedure is invoked to display a frame widget.
389 * Commands are output to X to display the frame in its
392 *----------------------------------------------------------------------
396 DisplayFrame(clientData
)
397 ClientData clientData
; /* Information about widget. */
399 register Frame
*framePtr
= (Frame
*) clientData
;
400 register Tk_Window tkwin
= framePtr
->tkwin
;
402 framePtr
->flags
&= ~REDRAW_PENDING
;
403 if ((framePtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
407 if (framePtr
->flags
& CLEAR_NEEDED
) {
408 XClearWindow(Tk_Display(tkwin
), Tk_WindowId(tkwin
));
409 framePtr
->flags
&= ~CLEAR_NEEDED
;
411 if ((framePtr
->border
!= NULL
)
412 && (framePtr
->relief
!= TK_RELIEF_FLAT
)) {
413 Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
414 framePtr
->border
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
415 framePtr
->borderWidth
, framePtr
->relief
);
420 *--------------------------------------------------------------
424 * This procedure is invoked by the Tk dispatcher on
425 * structure changes to a frame. For frames with 3D
426 * borders, this procedure is also invoked for exposures.
432 * When the window gets deleted, internal structures get
433 * cleaned up. When it gets exposed, it is redisplayed.
435 *--------------------------------------------------------------
439 FrameEventProc(clientData
, eventPtr
)
440 ClientData clientData
; /* Information about window. */
441 register XEvent
*eventPtr
; /* Information about event. */
443 register Frame
*framePtr
= (Frame
*) clientData
;
445 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
446 if ((framePtr
->relief
!= TK_RELIEF_FLAT
) && (framePtr
->tkwin
!= NULL
)
447 && !(framePtr
->flags
& REDRAW_PENDING
)) {
448 Tk_DoWhenIdle(DisplayFrame
, (ClientData
) framePtr
);
449 framePtr
->flags
|= REDRAW_PENDING
;
451 } else if (eventPtr
->type
== DestroyNotify
) {
452 Tcl_DeleteCommand(framePtr
->interp
, Tk_PathName(framePtr
->tkwin
));
453 framePtr
->tkwin
= NULL
;
454 if (framePtr
->flags
& REDRAW_PENDING
) {
455 Tk_CancelIdleCall(DisplayFrame
, (ClientData
) framePtr
);
457 Tk_CancelIdleCall(MapFrame
, (ClientData
) framePtr
);
458 Tk_EventuallyFree((ClientData
) framePtr
, DestroyFrame
);
463 *----------------------------------------------------------------------
467 * This procedure is invoked as a when-idle handler to map a
468 * newly-created top-level frame.
474 * The frame given by the clientData argument is mapped.
476 *----------------------------------------------------------------------
481 ClientData clientData
; /* Pointer to frame structure. */
483 Frame
*framePtr
= (Frame
*) clientData
;
486 * Wait for all other background events to be processed before
487 * mapping window. This ensures that the window's correct geometry
488 * will have been determined before it is first mapped, so that the
489 * window manager doesn't get a false idea of its desired geometry.
493 if (Tk_DoOneEvent(TK_IDLE_EVENTS
) == 0) {
498 * After each event, make sure that the window still exists,
499 * and quit if the window has been destroyed.
502 if (framePtr
->tkwin
== NULL
) {
506 Tk_MapWindow(framePtr
->tkwin
);