]> www.wagner.pp.ru Git - oss/ck.git/blob - ckFrame.c
Ck console graphics toolkit
[oss/ck.git] / ckFrame.c
1 /* 
2  * ckFrame.c --
3  *
4  *      This module implements "frame" and "toplevel" widgets for the
5  *      toolkit.  Frames are windows with a background color
6  *      and possibly border, but no other attributes.
7  *
8  * Copyright (c) 1990-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  * Copyright (c) 1995 Christian Werner
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  */
15
16 #include "ckPort.h"
17 #include "ck.h"
18 #include "default.h"
19
20 /*
21  * A data structure of the following type is kept for each
22  * frame that currently exists for this process:
23  */
24
25 typedef struct {
26     CkWindow *winPtr;           /* Window that embodies the frame.  NULL
27                                  * means that the window has been destroyed
28                                  * but the data structures haven't yet been
29                                  * cleaned up.*/
30     Tcl_Interp *interp;         /* Interpreter associated with widget.
31                                  * Used to delete widget command.  */
32     Tcl_Command widgetCmd;      /* Token for frame's widget command. */
33     CkBorder *borderPtr;        /* Structure used to draw border. */
34     int fg, bg;                 /* Foreground/background colors. */
35     int attr;                   /* Video attributes. */
36     int width;                  /* Width to request for window.  <= 0 means
37                                  * don't request any size. */
38     int height;                 /* Height to request for window.  <= 0 means
39                                  * don't request any size. */
40     char *takeFocus;            /* Value of -takefocus option. */
41     int flags;                  /* Various flags;  see below for
42                                  * definitions. */
43 } Frame;
44
45 /*
46  * Flag bits for frames:
47  *
48  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler
49  *                              has already been queued to redraw
50  *                              this window.
51  */
52
53 #define REDRAW_PENDING          1
54
55 static Ck_ConfigSpec configSpecs[] = {
56     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
57         DEF_FRAME_ATTRIB, Ck_Offset(Frame, attr), 0},
58     {CK_CONFIG_COLOR, "-background", "background", "Background",
59         DEF_FRAME_BG_COLOR, Ck_Offset(Frame, bg), CK_CONFIG_COLOR_ONLY},
60     {CK_CONFIG_COLOR, "-background", "background", "Background",
61         DEF_FRAME_BG_MONO, Ck_Offset(Frame, bg), CK_CONFIG_MONO_ONLY},
62     {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
63         (char *) NULL, 0, 0},
64     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
65         DEF_FRAME_FG_COLOR, Ck_Offset(Frame, fg), CK_CONFIG_COLOR_ONLY},
66     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
67         DEF_FRAME_FG_MONO, Ck_Offset(Frame, fg), CK_CONFIG_MONO_ONLY},
68     {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
69         (char *) NULL, 0, 0},
70     {CK_CONFIG_BORDER, "-border", "border", "Border",
71         DEF_FRAME_BORDER, Ck_Offset(Frame, borderPtr), CK_CONFIG_NULL_OK},
72     {CK_CONFIG_COORD, "-height", "height", "Height",
73         DEF_FRAME_HEIGHT, Ck_Offset(Frame, height), 0},
74     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
75         DEF_FRAME_TAKE_FOCUS, Ck_Offset(Frame, takeFocus),
76         CK_CONFIG_NULL_OK},
77     {CK_CONFIG_COORD, "-width", "width", "Width",
78         DEF_FRAME_WIDTH, Ck_Offset(Frame, width), 0},
79     {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
80         (char *) NULL, 0, 0}
81 };
82
83 /*
84  * Forward declarations for procedures defined later in this file:
85  */
86
87 static int      ConfigureFrame _ANSI_ARGS_((Tcl_Interp *interp,
88                     Frame *framePtr, int argc, char **argv, int flags));
89 static void     DestroyFrame _ANSI_ARGS_((ClientData clientData));
90 static void     FrameCmdDeletedProc _ANSI_ARGS_((ClientData clientData));
91 static void     DisplayFrame _ANSI_ARGS_((ClientData clientData));
92 static void     FrameEventProc _ANSI_ARGS_((ClientData clientData,
93                     CkEvent *eventPtr));
94 static int      FrameWidgetCmd _ANSI_ARGS_((ClientData clientData,
95                     Tcl_Interp *interp, int argc, char **argv));
96 \f
97 /*
98  *--------------------------------------------------------------
99  *
100  * Ck_FrameCmd --
101  *
102  *      This procedure is invoked to process the "frame" and
103  *      "toplevel" Tcl commands.  See the user documentation for
104  *      details on what it does.
105  *
106  * Results:
107  *      A standard Tcl result.
108  *
109  * Side effects:
110  *      See the user documentation.
111  *
112  *--------------------------------------------------------------
113  */
114
115 int
116 Ck_FrameCmd(clientData, interp, argc, argv)
117     ClientData clientData;      /* Main window associated with
118                                  * interpreter. */
119     Tcl_Interp *interp;         /* Current interpreter. */
120     int argc;                   /* Number of arguments. */
121     char **argv;                /* Argument strings. */
122 {
123     CkWindow *winPtr = (CkWindow *) clientData;
124     CkWindow *new;
125     char *className;
126     int src, dst, toplevel;
127
128     if (argc < 2) {
129         Tcl_AppendResult(interp, "wrong # args: should be \"",
130                 argv[0], " pathName ?options?\"", (char *) NULL);
131         return TCL_ERROR;
132     }
133
134     /*
135      * The code below is a special hack that extracts a few key
136      * options from the argument list now, rather than letting
137      * ConfigureFrame do it.  This is necessary because we have
138      * to know the window's class before creating the window.
139      */
140
141     toplevel = *argv[0] == 't';
142     className = NULL;
143     for (src = 2, dst = 2; src < argc;  src += 2) {
144         char c;
145
146         c = argv[src][1];
147         if ((c == 'c')
148                 && (strncmp(argv[src], "-class", strlen(argv[src])) == 0)) {
149             className = argv[src+1];
150         } else {
151             argv[dst] = argv[src];
152             argv[dst+1] = argv[src+1];
153             dst += 2;
154         }
155     }
156     argc -= src-dst;
157
158     /*
159      * Create the window and initialize our structures and event handlers.
160      */
161
162     new = Ck_CreateWindowFromPath(interp, winPtr, argv[1], toplevel);
163     if (new == NULL)
164         return TCL_ERROR;
165     if (className == NULL) {
166         className = Ck_GetOption(new, "class", "Class");
167         if (className == NULL) {
168             className = (toplevel) ? "Toplevel" : "Frame";
169         }
170     }
171     Ck_SetClass(new, className);
172     return CkInitFrame(interp, new, argc-2, argv+2);
173 }
174 \f
175 /*
176  *----------------------------------------------------------------------
177  *
178  * CkInitFrame --
179  *
180  *      This procedure initializes a frame widget.  It's
181  *      separate from Ck_FrameCmd so that it can be used for the
182  *      main window, which has already been created elsewhere.
183  *
184  * Results:
185  *      A standard Tcl completion code.
186  *
187  * Side effects:
188  *      A widget record gets allocated, handlers get set up, etc..
189  *
190  *----------------------------------------------------------------------
191  */
192
193 int
194 CkInitFrame(interp, winPtr, argc, argv)
195     Tcl_Interp *interp;                 /* Interpreter associated with the
196                                          * application. */
197     CkWindow *winPtr;                   /* Window to use for frame or
198                                          * top-level. Caller must already
199                                          * have set window's class. */
200     int argc;                           /* Number of configuration arguments
201                                          * (not including class command and
202                                          * window name). */
203     char *argv[];                       /* Configuration arguments. */
204 {
205     Frame *framePtr;
206
207     framePtr = (Frame *) ckalloc(sizeof (Frame));
208     framePtr->winPtr = winPtr;
209     framePtr->interp = interp;
210     framePtr->widgetCmd = Tcl_CreateCommand(interp,
211         framePtr->winPtr->pathName, FrameWidgetCmd,
212             (ClientData) framePtr, FrameCmdDeletedProc);
213     framePtr->borderPtr = NULL;
214     framePtr->fg = 0;
215     framePtr->bg = 0;
216     framePtr->attr = 0;
217     framePtr->width = 1;
218     framePtr->height = 1;
219     framePtr->takeFocus = NULL;
220     framePtr->flags = 0;
221     Ck_CreateEventHandler(framePtr->winPtr,
222             CK_EV_MAP | CK_EV_EXPOSE | CK_EV_DESTROY,
223             FrameEventProc, (ClientData) framePtr);
224     if (ConfigureFrame(interp, framePtr, argc, argv, 0) != TCL_OK) {
225         Ck_DestroyWindow(framePtr->winPtr);
226         return TCL_ERROR;
227     }
228     interp->result = framePtr->winPtr->pathName;
229     return TCL_OK;
230 }
231 \f
232 /*
233  *--------------------------------------------------------------
234  *
235  * FrameWidgetCmd --
236  *
237  *      This procedure is invoked to process the Tcl command
238  *      that corresponds to a frame widget.  See the user
239  *      documentation for details on what it does.
240  *
241  * Results:
242  *      A standard Tcl result.
243  *
244  * Side effects:
245  *      See the user documentation.
246  *
247  *--------------------------------------------------------------
248  */
249
250 static int
251 FrameWidgetCmd(clientData, interp, argc, argv)
252     ClientData clientData;      /* Information about frame widget. */
253     Tcl_Interp *interp;         /* Current interpreter. */
254     int argc;                   /* Number of arguments. */
255     char **argv;                /* Argument strings. */
256 {
257     Frame *framePtr = (Frame *) clientData;
258     int result = TCL_OK;
259     int length;
260     char c;
261
262     if (argc < 2) {
263         Tcl_AppendResult(interp, "wrong # args: should be \"",
264                 argv[0], " option ?arg arg ...?\"", (char *) NULL);
265         return TCL_ERROR;
266     }
267     Ck_Preserve((ClientData) framePtr);
268     c = argv[1][0];
269     length = strlen(argv[1]);
270     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
271         && (length >= 2)) {
272         if (argc != 3) {
273             Tcl_AppendResult(interp, "wrong # args: should be \"",
274                     argv[0], " cget option\"",
275                     (char *) NULL);
276             goto error;
277         }
278         result = Ck_ConfigureValue(interp, framePtr->winPtr, configSpecs,
279                 (char *) framePtr, argv[2], 0);
280     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
281         if (argc == 2) {
282             result = Ck_ConfigureInfo(interp, framePtr->winPtr, configSpecs,
283                     (char *) framePtr, (char *) NULL, 0);
284         } else if (argc == 3) {
285             result = Ck_ConfigureInfo(interp, framePtr->winPtr, configSpecs,
286                     (char *) framePtr, argv[2], 0);
287         } else {
288             result = ConfigureFrame(interp, framePtr, argc-2, argv+2,
289                     CK_CONFIG_ARGV_ONLY);
290         }
291     } else {
292         Tcl_AppendResult(interp, "bad option \"", argv[1],
293                 "\":  must be cget or configure", (char *) NULL);
294         goto error;
295     }
296     Ck_Release((ClientData) framePtr);
297     return result;
298
299 error:
300     Ck_Release((ClientData) framePtr);
301     return TCL_ERROR;
302 }
303 \f
304 /*
305  *----------------------------------------------------------------------
306  *
307  * DestroyFrame --
308  *
309  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
310  *      to clean up the internal structure of a frame at a safe time
311  *      (when no-one is using it anymore).
312  *
313  * Results:
314  *      None.
315  *
316  * Side effects:
317  *      Everything associated with the frame is freed up.
318  *
319  *----------------------------------------------------------------------
320  */
321
322 static void
323 DestroyFrame(clientData)
324     ClientData clientData;      /* Info about frame widget. */
325 {
326     Frame *framePtr = (Frame *) clientData;
327
328     Ck_FreeOptions(configSpecs, (char *) framePtr, 0);
329     ckfree((char *) framePtr);
330 }
331 \f
332 /*
333  *----------------------------------------------------------------------
334  *
335  * FrameCmdDeletedProc --
336  *
337  *      This procedure is invoked when a widget command is deleted.  If
338  *      the widget isn't already in the process of being destroyed,
339  *      this command destroys it.
340  *
341  * Results:
342  *      None.
343  *
344  * Side effects:
345  *      The widget is destroyed.
346  *
347  *----------------------------------------------------------------------
348  */
349
350 static void
351 FrameCmdDeletedProc(clientData)
352     ClientData clientData;      /* Pointer to widget record for widget. */
353 {
354     Frame *framePtr = (Frame *) clientData;
355     CkWindow *winPtr = framePtr->winPtr;
356
357     /*
358      * This procedure could be invoked either because the window was
359      * destroyed and the command was then deleted (in which case tkwin
360      * is NULL) or because the command was deleted, and then this procedure
361      * destroys the widget.
362      */
363
364     if (winPtr != NULL) {
365         framePtr->winPtr = NULL;
366         Ck_DestroyWindow(winPtr);
367     }
368 }
369 \f
370 /*
371  *----------------------------------------------------------------------
372  *
373  * ConfigureFrame --
374  *
375  *      This procedure is called to process an argv/argc list, plus
376  *      the option database, in order to configure (or
377  *      reconfigure) a frame widget.
378  *
379  * Results:
380  *      The return value is a standard Tcl result.  If TCL_ERROR is
381  *      returned, then interp->result contains an error message.
382  *
383  * Side effects:
384  *      Configuration information, such as text string, colors, font,
385  *      etc. get set for framePtr;  old resources get freed, if there
386  *      were any.
387  *
388  *----------------------------------------------------------------------
389  */
390
391 static int
392 ConfigureFrame(interp, framePtr, argc, argv, flags)
393     Tcl_Interp *interp;         /* Used for error reporting. */
394     Frame *framePtr;            /* Information about widget;  may or may
395                                  * not already have values for some fields. */
396     int argc;                   /* Number of valid entries in argv. */
397     char **argv;                /* Arguments. */
398     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
399 {
400     if (Ck_ConfigureWidget(interp, framePtr->winPtr, configSpecs,
401             argc, argv, (char *) framePtr, flags) != TCL_OK) {
402         return TCL_ERROR;
403     }
404
405     Ck_SetWindowAttr(framePtr->winPtr, framePtr->fg, framePtr->bg,
406         framePtr->attr);
407     Ck_SetInternalBorder(framePtr->winPtr, framePtr->borderPtr != NULL);
408     if ((framePtr->width > 0) || (framePtr->height > 0))
409         Ck_GeometryRequest(framePtr->winPtr, framePtr->width,
410             framePtr->height);
411     if ((framePtr->winPtr->flags & CK_MAPPED)
412             && !(framePtr->flags & REDRAW_PENDING)) {
413         Tk_DoWhenIdle(DisplayFrame, (ClientData) framePtr);
414         framePtr->flags |= REDRAW_PENDING;
415     }
416     return TCL_OK;
417 }
418 \f
419 /*
420  *----------------------------------------------------------------------
421  *
422  * DisplayFrame --
423  *
424  *      This procedure is invoked to display a frame widget.
425  *
426  * Results:
427  *      None.
428  *
429  * Side effects:
430  *      Commands are output to display the frame in its
431  *      current mode.
432  *
433  *----------------------------------------------------------------------
434  */
435
436 static void
437 DisplayFrame(clientData)
438     ClientData clientData;      /* Information about widget. */
439 {
440     Frame *framePtr = (Frame *) clientData;
441     CkWindow *winPtr = framePtr->winPtr;
442
443     framePtr->flags &= ~REDRAW_PENDING;
444     if ((framePtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED)) {
445         return;
446     }
447     Ck_ClearToBot(winPtr, 0, 0);
448     if (framePtr->borderPtr != NULL)
449         Ck_DrawBorder(winPtr, framePtr->borderPtr, 0, 0,
450             winPtr->width, winPtr->height);
451     Ck_EventuallyRefresh(winPtr);
452 }
453 \f
454 /*
455  *--------------------------------------------------------------
456  *
457  * FrameEventProc --
458  *
459  *      This procedure is invoked by the dispatcher on
460  *      structure changes to a frame.
461  *
462  * Results:
463  *      None.
464  *
465  * Side effects:
466  *      When the window gets deleted, internal structures get
467  *      cleaned up.  When it gets exposed, it is redisplayed.
468  *
469  *--------------------------------------------------------------
470  */
471
472 static void
473 FrameEventProc(clientData, eventPtr)
474     ClientData clientData;      /* Information about window. */
475     CkEvent *eventPtr;          /* Information about event. */
476 {
477     Frame *framePtr = (Frame *) clientData;
478
479     if (eventPtr->type == CK_EV_EXPOSE && framePtr->winPtr != NULL &&
480         !(framePtr->flags & REDRAW_PENDING)) {
481         Tk_DoWhenIdle(DisplayFrame, (ClientData) framePtr);
482         framePtr->flags |= REDRAW_PENDING;
483     } else if (eventPtr->type == CK_EV_DESTROY) {
484         if (framePtr->winPtr != NULL) {
485             framePtr->winPtr = NULL;
486             Tcl_DeleteCommand(framePtr->interp,
487                     Tcl_GetCommandName(framePtr->interp, framePtr->widgetCmd));
488         }
489         if (framePtr->flags & REDRAW_PENDING)
490             Tk_CancelIdleCall(DisplayFrame, (ClientData) framePtr);
491         Ck_EventuallyFree((ClientData) framePtr, (Ck_FreeProc *) DestroyFrame);
492     }
493 }