OSDN Git Service

*** empty log message ***
[pf3gnuchains/sourceware.git] / tk / generic / tkSquare.c
1 /* 
2  * tkSquare.c --
3  *
4  *      This module implements "square" widgets that are object
5  *      based.  A "square" is a widget that displays a single square that can 
6  *      be moved around and resized.  This file is intended as an example
7  *      of how to build a widget;  it isn't included in the
8  *      normal wish, but it is included in "tktest".
9  *
10  * Copyright (c) 1997 Sun Microsystems, Inc.
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  * RCS: @(#) $Id$
16  */
17
18 #include "tkPort.h"
19 #define __NO_OLD_CONFIG
20 #include "tk.h"
21 #include "tkInt.h"
22
23 /*
24  * A data structure of the following type is kept for each square
25  * widget managed by this file:
26  */
27
28 typedef struct {
29     Tk_Window tkwin;            /* Window that embodies the square.  NULL
30                                  * means window has been deleted but
31                                  * widget record hasn't been cleaned up yet. */
32     Display *display;           /* X's token for the window's display. */
33     Tcl_Interp *interp;         /* Interpreter associated with widget. */
34     Tcl_Command widgetCmd;      /* Token for square's widget command. */
35     Tk_OptionTable optionTable; /* Token representing the configuration
36                                  * specifications. */
37     Tcl_Obj *xPtr, *yPtr;       /* Position of square's upper-left corner
38                                  * within widget. */
39     int x, y;
40     Tcl_Obj *sizeObjPtr;        /* Width and height of square. */
41
42     /*
43      * Information used when displaying widget:
44      */
45
46     Tcl_Obj *borderWidthPtr;    /* Width of 3-D border around whole widget. */
47     Tcl_Obj *bgBorderPtr;
48     Tcl_Obj *fgBorderPtr;
49     Tcl_Obj *reliefPtr;
50     GC gc;                      /* Graphics context for copying from
51                                  * off-screen pixmap onto screen. */
52     Tcl_Obj *doubleBufferPtr;   /* Non-zero means double-buffer redisplay
53                                  * with pixmap;  zero means draw straight
54                                  * onto the display. */
55     int updatePending;          /* Non-zero means a call to SquareDisplay
56                                  * has already been scheduled. */
57 } Square;
58
59 /*
60  * Information used for argv parsing.
61  */
62
63 static Tk_OptionSpec optionSpecs[] = {
64     {TK_OPTION_BORDER, "-background", "background", "Background",
65             "#d9d9d9", Tk_Offset(Square, bgBorderPtr), -1, 0,
66             (ClientData) "white"},
67     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
68             (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth"},
69     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
70             (char *) NULL, 0, -1, 0, (ClientData) "-background"},
71     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
72             "2", Tk_Offset(Square, borderWidthPtr), -1},
73     {TK_OPTION_BOOLEAN, "-dbl", "doubleBuffer", "DoubleBuffer",
74             "1", Tk_Offset(Square, doubleBufferPtr), -1},
75     {TK_OPTION_SYNONYM, "-fg", (char *) NULL, (char *) NULL,
76             (char *) NULL, 0, -1, 0, (ClientData) "-foreground"},
77     {TK_OPTION_BORDER, "-foreground", "foreground", "Foreground",
78             "#b03060", Tk_Offset(Square, fgBorderPtr), -1, 0,
79             (ClientData) "black"},
80     {TK_OPTION_PIXELS, "-posx", "posx", "PosX", "0",
81             Tk_Offset(Square, xPtr), -1},
82     {TK_OPTION_PIXELS, "-posy", "posy", "PosY", "0",
83             Tk_Offset(Square, yPtr), -1},
84     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
85             "raised", Tk_Offset(Square, reliefPtr), -1},
86     {TK_OPTION_PIXELS, "-size", "size", "Size", "20",
87             Tk_Offset(Square, sizeObjPtr), -1},
88     {TK_OPTION_END}
89 };
90
91 /*
92  * Forward declarations for procedures defined later in this file:
93  */
94
95 int                     SquareObjCmd _ANSI_ARGS_((ClientData clientData,
96                             Tcl_Interp *interp, int objc, 
97                             Tcl_Obj * CONST objv[]));
98 static void             SquareDeletedProc _ANSI_ARGS_((
99                             ClientData clientData));
100 static int              SquareConfigure _ANSI_ARGS_((Tcl_Interp *interp,
101                             Square *squarePtr));
102 static void             SquareDestroy _ANSI_ARGS_((char *memPtr));
103 static void             SquareDisplay _ANSI_ARGS_((ClientData clientData));
104 static void             KeepInWindow _ANSI_ARGS_((Square *squarePtr));
105 static void             SquareObjEventProc _ANSI_ARGS_((ClientData clientData,
106                             XEvent *eventPtr));
107 static int              SquareWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
108                             Tcl_Interp *, int objc, Tcl_Obj * CONST objv[]));
109 \f
110 /*
111  *--------------------------------------------------------------
112  *
113  * SquareCmd --
114  *
115  *      This procedure is invoked to process the "square" Tcl
116  *      command.  It creates a new "square" widget.
117  *
118  * Results:
119  *      A standard Tcl result.
120  *
121  * Side effects:
122  *      A new widget is created and configured.
123  *
124  *--------------------------------------------------------------
125  */
126
127 int
128 SquareObjCmd(clientData, interp, objc, objv)
129     ClientData clientData;      /* NULL. */
130     Tcl_Interp *interp;         /* Current interpreter. */
131     int objc;                   /* Number of arguments. */
132     Tcl_Obj * CONST objv[];     /* Argument objects. */
133 {
134     Square *squarePtr;
135     Tk_Window tkwin;
136     Tk_OptionTable optionTable;
137
138     if (objc < 2) {
139         Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
140         return TCL_ERROR;
141     }
142
143     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), 
144             Tcl_GetStringFromObj(objv[1], NULL), (char *) NULL);
145     if (tkwin == NULL) {
146         return TCL_ERROR;
147     }
148     Tk_SetClass(tkwin, "Square");
149
150     /*
151      * Create the option table for this widget class.  If it has
152      * already been created, the refcount will get bumped and just
153      * the pointer will be returned.  The refcount getting bumped
154      * does not concern us, because Tk will ensure the table is
155      * deleted when the interpreter is destroyed.
156      */
157
158     optionTable = Tk_CreateOptionTable(interp, optionSpecs);
159
160     /*
161      * Allocate and initialize the widget record.  The memset allows
162      * us to set just the non-NULL/0 items.
163      */
164
165     squarePtr                   = (Square *) ckalloc(sizeof(Square));
166     memset((void *) squarePtr, 0, (sizeof(Square)));
167
168     squarePtr->tkwin            = tkwin;
169     squarePtr->display          = Tk_Display(tkwin);
170     squarePtr->interp           = interp;
171     squarePtr->widgetCmd        = Tcl_CreateObjCommand(interp,
172             Tk_PathName(squarePtr->tkwin), SquareWidgetObjCmd,
173             (ClientData) squarePtr, SquareDeletedProc);
174     squarePtr->gc               = None;
175     squarePtr->optionTable      = optionTable;
176
177     if (Tk_InitOptions(interp, (char *) squarePtr, optionTable, tkwin)
178             != TCL_OK) {
179         Tk_DestroyWindow(squarePtr->tkwin);
180         ckfree((char *) squarePtr);
181         return TCL_ERROR;
182     }
183
184     Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
185             SquareObjEventProc, (ClientData) squarePtr);
186     if (Tk_SetOptions(interp, (char *) squarePtr, optionTable, objc - 2,
187             objv + 2, tkwin, NULL, (int *) NULL) != TCL_OK) {
188         goto error;
189     }
190     if (SquareConfigure(interp, squarePtr) != TCL_OK) {
191         goto error;
192     }
193
194     Tcl_SetObjResult(interp,
195             Tcl_NewStringObj(Tk_PathName(squarePtr->tkwin), -1));
196     return TCL_OK;
197
198 error:
199     Tk_DestroyWindow(squarePtr->tkwin);
200     return TCL_ERROR;
201 }
202 \f
203 /*
204  *--------------------------------------------------------------
205  *
206  * SquareWidgetObjCmd --
207  *
208  *      This procedure is invoked to process the Tcl command
209  *      that corresponds to a widget managed by this module.
210  *      See the user documentation for details on what it does.
211  *
212  * Results:
213  *      A standard Tcl result.
214  *
215  * Side effects:
216  *      See the user documentation.
217  *
218  *--------------------------------------------------------------
219  */
220
221 static int
222 SquareWidgetObjCmd(clientData, interp, objc, objv)
223     ClientData clientData;              /* Information about square widget. */
224     Tcl_Interp *interp;                 /* Current interpreter. */
225     int objc;                           /* Number of arguments. */
226     Tcl_Obj * CONST objv[];             /* Argument objects. */
227 {
228     Square *squarePtr = (Square *) clientData;
229     int result = TCL_OK;
230     static CONST char *squareOptions[] = {"cget", "configure", (char *) NULL};
231     enum {
232         SQUARE_CGET, SQUARE_CONFIGURE
233     };
234     Tcl_Obj *resultObjPtr;
235     int index;
236
237     if (objc < 2) {
238         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg...?");
239         return TCL_ERROR;
240     }
241
242     if (Tcl_GetIndexFromObj(interp, objv[1], squareOptions, "command",
243             0, &index) != TCL_OK) {
244         return TCL_ERROR;
245     }
246
247     Tcl_Preserve((ClientData) squarePtr);
248     
249     switch (index) {
250         case SQUARE_CGET: {
251             if (objc != 3) {
252                 Tcl_WrongNumArgs(interp, 2, objv, "option");
253                 goto error;
254             }
255             resultObjPtr = Tk_GetOptionValue(interp, (char *) squarePtr,
256                     squarePtr->optionTable, objv[2], squarePtr->tkwin);
257             if (resultObjPtr == NULL) {
258                 result = TCL_ERROR;
259             } else {
260                 Tcl_SetObjResult(interp, resultObjPtr);
261             }
262             break;
263         }
264         case SQUARE_CONFIGURE: {
265             resultObjPtr = NULL;
266             if (objc == 2) {
267                 resultObjPtr = Tk_GetOptionInfo(interp, (char *) squarePtr,
268                         squarePtr->optionTable, (Tcl_Obj *) NULL,
269                         squarePtr->tkwin);
270                 if (resultObjPtr == NULL) {
271                     result = TCL_ERROR;
272                 }
273             } else if (objc == 3) {
274                 resultObjPtr = Tk_GetOptionInfo(interp, (char *) squarePtr,
275                         squarePtr->optionTable, objv[2], squarePtr->tkwin);
276                 if (resultObjPtr == NULL) {
277                     result = TCL_ERROR;
278                 }
279             } else {
280                 result = Tk_SetOptions(interp, (char *) squarePtr,
281                         squarePtr->optionTable, objc - 2, objv + 2,
282                         squarePtr->tkwin, NULL, (int *) NULL);
283                 if (result == TCL_OK) {
284                     result = SquareConfigure(interp, squarePtr);
285                 }
286                 if (!squarePtr->updatePending) {
287                     Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
288                     squarePtr->updatePending = 1;
289                 }
290             }
291             if (resultObjPtr != NULL) {
292                 Tcl_SetObjResult(interp, resultObjPtr);
293             }
294         }
295     }
296     Tcl_Release((ClientData) squarePtr);
297     return result;
298
299     error:
300     Tcl_Release((ClientData) squarePtr);
301     return TCL_ERROR;
302 }
303 \f
304 /*
305  *----------------------------------------------------------------------
306  *
307  * SquareConfigure --
308  *
309  *      This procedure is called to process an argv/argc list in
310  *      conjunction with the Tk option database to configure (or
311  *      reconfigure) a square widget.
312  *
313  * Results:
314  *      The return value is a standard Tcl result.  If TCL_ERROR is
315  *      returned, then the interp's result contains an error message.
316  *
317  * Side effects:
318  *      Configuration information, such as colors, border width,
319  *      etc. get set for squarePtr;  old resources get freed,
320  *      if there were any.
321  *
322  *----------------------------------------------------------------------
323  */
324
325 static int
326 SquareConfigure(interp, squarePtr)
327     Tcl_Interp *interp;                 /* Used for error reporting. */
328     Square *squarePtr;                  /* Information about widget. */
329 {
330     int borderWidth;
331     Tk_3DBorder bgBorder;
332     int doubleBuffer;
333
334     /*
335      * Set the background for the window and create a graphics context
336      * for use during redisplay.
337      */
338
339     bgBorder = Tk_Get3DBorderFromObj(squarePtr->tkwin, 
340             squarePtr->bgBorderPtr);
341     Tk_SetWindowBackground(squarePtr->tkwin,
342             Tk_3DBorderColor(bgBorder)->pixel);
343     Tcl_GetBooleanFromObj(NULL, squarePtr->doubleBufferPtr, &doubleBuffer);
344     if ((squarePtr->gc == None) && (doubleBuffer)) {
345         XGCValues gcValues;
346         gcValues.function = GXcopy;
347         gcValues.graphics_exposures = False;
348         squarePtr->gc = Tk_GetGC(squarePtr->tkwin,
349                 GCFunction|GCGraphicsExposures, &gcValues);
350     }
351
352     /*
353      * Register the desired geometry for the window.  Then arrange for
354      * the window to be redisplayed.
355      */
356
357     Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
358     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->borderWidthPtr,
359             &borderWidth);
360     Tk_SetInternalBorder(squarePtr->tkwin, borderWidth);
361     if (!squarePtr->updatePending) {
362         Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
363         squarePtr->updatePending = 1;
364     }
365     KeepInWindow(squarePtr);
366     return TCL_OK;
367 }
368 \f
369 /*
370  *--------------------------------------------------------------
371  *
372  * SquareObjEventProc --
373  *
374  *      This procedure is invoked by the Tk dispatcher for various
375  *      events on squares.
376  *
377  * Results:
378  *      None.
379  *
380  * Side effects:
381  *      When the window gets deleted, internal structures get
382  *      cleaned up.  When it gets exposed, it is redisplayed.
383  *
384  *--------------------------------------------------------------
385  */
386
387 static void
388 SquareObjEventProc(clientData, eventPtr)
389     ClientData clientData;      /* Information about window. */
390     XEvent *eventPtr;           /* Information about event. */
391 {
392     Square *squarePtr = (Square *) clientData;
393
394     if (eventPtr->type == Expose) {
395         if (!squarePtr->updatePending) {
396             Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
397             squarePtr->updatePending = 1;
398         }
399     } else if (eventPtr->type == ConfigureNotify) {
400         KeepInWindow(squarePtr);
401         if (!squarePtr->updatePending) {
402             Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
403             squarePtr->updatePending = 1;
404         }
405     } else if (eventPtr->type == DestroyNotify) {
406         if (squarePtr->tkwin != NULL) {
407             Tk_FreeConfigOptions((char *) squarePtr, squarePtr->optionTable,
408                     squarePtr->tkwin);
409             if (squarePtr->gc != None) {
410                 Tk_FreeGC(squarePtr->display, squarePtr->gc);
411             }
412             squarePtr->tkwin = NULL;
413             Tcl_DeleteCommandFromToken(squarePtr->interp,
414                     squarePtr->widgetCmd);
415         }
416         if (squarePtr->updatePending) {
417             Tcl_CancelIdleCall(SquareDisplay, (ClientData) squarePtr);
418         }
419         Tcl_EventuallyFree((ClientData) squarePtr, SquareDestroy);
420     }
421 }
422 \f
423 /*
424  *----------------------------------------------------------------------
425  *
426  * SquareDeletedProc --
427  *
428  *      This procedure is invoked when a widget command is deleted.  If
429  *      the widget isn't already in the process of being destroyed,
430  *      this command destroys it.
431  *
432  * Results:
433  *      None.
434  *
435  * Side effects:
436  *      The widget is destroyed.
437  *
438  *----------------------------------------------------------------------
439  */
440
441 static void
442 SquareDeletedProc(clientData)
443     ClientData clientData;      /* Pointer to widget record for widget. */
444 {
445     Square *squarePtr = (Square *) clientData;
446     Tk_Window tkwin = squarePtr->tkwin;
447
448     /*
449      * This procedure could be invoked either because the window was
450      * destroyed and the command was then deleted (in which case tkwin
451      * is NULL) or because the command was deleted, and then this procedure
452      * destroys the widget.
453      */
454
455     if (tkwin != NULL) {
456         Tk_DestroyWindow(tkwin);
457     }
458 }
459 \f
460 /*
461  *--------------------------------------------------------------
462  *
463  * SquareDisplay --
464  *
465  *      This procedure redraws the contents of a square window.
466  *      It is invoked as a do-when-idle handler, so it only runs
467  *      when there's nothing else for the application to do.
468  *
469  * Results:
470  *      None.
471  *
472  * Side effects:
473  *      Information appears on the screen.
474  *
475  *--------------------------------------------------------------
476  */
477
478 static void
479 SquareDisplay(clientData)
480     ClientData clientData;      /* Information about window. */
481 {
482     Square *squarePtr = (Square *) clientData;
483     Tk_Window tkwin = squarePtr->tkwin;
484     Pixmap pm = None;
485     Drawable d;
486     int borderWidth, size, relief;
487     Tk_3DBorder bgBorder, fgBorder;
488     int doubleBuffer;
489
490     squarePtr->updatePending = 0;
491     if (!Tk_IsMapped(tkwin)) {
492         return;
493     }
494
495     /*
496      * Create a pixmap for double-buffering, if necessary.
497      */
498
499     Tcl_GetBooleanFromObj(NULL, squarePtr->doubleBufferPtr, &doubleBuffer);
500     if (doubleBuffer) {
501         pm = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
502                 Tk_Width(tkwin), Tk_Height(tkwin),
503                 DefaultDepthOfScreen(Tk_Screen(tkwin)));
504         d = pm;
505     } else {
506         d = Tk_WindowId(tkwin);
507     }
508
509     /*
510      * Redraw the widget's background and border.
511      */
512
513     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->borderWidthPtr,
514             &borderWidth);
515     bgBorder = Tk_Get3DBorderFromObj(squarePtr->tkwin, 
516             squarePtr->bgBorderPtr);
517     Tk_GetReliefFromObj(NULL, squarePtr->reliefPtr, &relief);
518     Tk_Fill3DRectangle(tkwin, d, bgBorder, 0, 0, Tk_Width(tkwin),
519             Tk_Height(tkwin), borderWidth, relief);
520
521     /*
522      * Display the square.
523      */
524
525     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->sizeObjPtr, &size);
526     fgBorder = Tk_Get3DBorderFromObj(squarePtr->tkwin, 
527             squarePtr->fgBorderPtr);
528     Tk_Fill3DRectangle(tkwin, d, fgBorder, squarePtr->x, squarePtr->y, size, 
529             size, borderWidth, TK_RELIEF_RAISED);
530
531     /*
532      * If double-buffered, copy to the screen and release the pixmap.
533      */
534
535     if (doubleBuffer) {
536         XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), squarePtr->gc,
537                 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
538                 0, 0);
539         Tk_FreePixmap(Tk_Display(tkwin), pm);
540     }
541 }
542 \f
543 /*
544  *----------------------------------------------------------------------
545  *
546  * SquareDestroy --
547  *
548  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
549  *      to clean up the internal structure of a square at a safe time
550  *      (when no-one is using it anymore).
551  *
552  * Results:
553  *      None.
554  *
555  * Side effects:
556  *      Everything associated with the square is freed up.
557  *
558  *----------------------------------------------------------------------
559  */
560
561 static void
562 SquareDestroy(memPtr)
563     char *memPtr;               /* Info about square widget. */
564 {
565     Square *squarePtr = (Square *) memPtr;
566     
567     ckfree((char *) squarePtr);
568 }
569 \f
570 /*
571  *----------------------------------------------------------------------
572  *
573  * KeepInWindow --
574  *
575  *      Adjust the position of the square if necessary to keep it in
576  *      the widget's window.
577  *
578  * Results:
579  *      None.
580  *
581  * Side effects:
582  *      The x and y position of the square are adjusted if necessary
583  *      to keep the square in the window.
584  *
585  *----------------------------------------------------------------------
586  */
587
588 static void
589 KeepInWindow(squarePtr)
590     register Square *squarePtr;         /* Pointer to widget record. */
591 {
592     int i, bd, relief;
593     int borderWidth, size;
594
595     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->borderWidthPtr,
596             &borderWidth);
597     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->xPtr, 
598             &squarePtr->x);
599     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->yPtr, 
600             &squarePtr->y);
601     Tk_GetPixelsFromObj(NULL, squarePtr->tkwin, squarePtr->sizeObjPtr, &size);
602     Tk_GetReliefFromObj(NULL, squarePtr->reliefPtr, &relief);
603     bd = 0;
604     if (relief != TK_RELIEF_FLAT) {
605         bd = borderWidth;
606     }
607     i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + size);
608     if (i < 0) {
609         squarePtr->x += i;
610     }
611     i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + size);
612     if (i < 0) {
613         squarePtr->y += i;
614     }
615     if (squarePtr->x < bd) {
616         squarePtr->x = bd;
617     }
618     if (squarePtr->y < bd) {
619         squarePtr->y = bd;
620     }
621 }