4 * This module implements "square" widgets. A "square" is
5 * a widget that displays a single square that can be moved
6 * around and resized. This file is intended as an example
7 * of how to build a widget.
9 * Copyright 1991-1992 Regents of the University of California.
10 * Permission to use, copy, modify, and distribute this
11 * software and its documentation for any purpose and without
12 * fee is hereby granted, provided that the above copyright
13 * notice appear in all copies. The University of California
14 * makes no representations about the suitability of this
15 * software for any purpose. It is provided "as is" without
16 * express or implied warranty.
20 static char rcsid
[] = "$Header: /user6/ouster/wish/scripts/demos/RCS/tkSquare.c,v 1.2 92/04/29 11:45:17 ouster Exp $ SPRITE (Berkeley)";
27 * A data structure of the following type is kept for each square
28 * widget managed by this file:
32 Tk_Window tkwin
; /* Window that embodies the square. 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 widget. */
37 int x
, y
; /* Position of square's upper-left corner
39 int size
; /* Width and height of square. */
40 int flags
; /* Various flags; see below for
44 * Information used when displaying widget:
47 int borderWidth
; /* Width of 3-D border around whole widget. */
48 Tk_3DBorder bgBorder
; /* Used for drawing background. */
49 Tk_3DBorder fgBorder
; /* For drawing square. */
50 int relief
; /* Indicates whether window as a whole is
51 * raised, sunken, or flat. */
52 int doubleBuffer
; /* Non-zero means double-buffer redisplay
53 * with pixmap; zero means draw straight
54 * onto the display. */
58 * Flag bits for squares:
60 * REDRAW_PENDING - 1 means redraw has already been scheduled.
63 #define REDRAW_PENDING 1
66 * Information used for argv parsing.
69 static Tk_ConfigSpec configSpecs
[] = {
70 {TK_CONFIG_BORDER
, "-background", "background", "Background",
71 "#cdb79e", Tk_Offset(Square
, bgBorder
), TK_CONFIG_COLOR_ONLY
},
72 {TK_CONFIG_BORDER
, "-background", "background", "Background",
73 "white", Tk_Offset(Square
, bgBorder
), TK_CONFIG_MONO_ONLY
},
74 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
76 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
78 {TK_CONFIG_INT
, "-borderwidth", "borderWidth", "BorderWidth",
79 "2", Tk_Offset(Square
, borderWidth
), 0},
80 {TK_CONFIG_INT
, "-dbl", "doubleBuffer", "DoubleBuffer",
81 "1", Tk_Offset(Square
, doubleBuffer
), 0},
82 {TK_CONFIG_SYNONYM
, "-fg", "foreground", (char *) NULL
,
84 {TK_CONFIG_BORDER
, "-foreground", "foreground", "Foreground",
85 "#b03060", Tk_Offset(Square
, fgBorder
), TK_CONFIG_COLOR_ONLY
},
86 {TK_CONFIG_BORDER
, "-foreground", "foreground", "Foreground",
87 "black", Tk_Offset(Square
, fgBorder
), TK_CONFIG_MONO_ONLY
},
88 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
89 "raised", Tk_Offset(Square
, relief
), 0},
90 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
95 * Forward declarations for procedures defined later in this file:
98 static int ConfigureSquare
_ANSI_ARGS_((Tcl_Interp
*interp
,
99 Square
*squarePtr
, int argc
, char **argv
,
101 static void DestroySquare
_ANSI_ARGS_((ClientData clientData
));
102 static void DisplaySquare
_ANSI_ARGS_((ClientData clientData
));
103 static void KeepInWindow
_ANSI_ARGS_((Square
*squarePtr
));
104 static void SquareEventProc
_ANSI_ARGS_((ClientData clientData
,
106 static int SquareWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
107 Tcl_Interp
*, int argc
, char **argv
));
110 *--------------------------------------------------------------
114 * This procedure is invoked to process the "square" Tcl
115 * command. It creates a new "square" widget.
118 * A standard Tcl result.
121 * A new widget is created and configured.
123 *--------------------------------------------------------------
127 Tk_SquareCmd(clientData
, interp
, argc
, argv
)
128 ClientData clientData
; /* Main window associated with
130 Tcl_Interp
*interp
; /* Current interpreter. */
131 int argc
; /* Number of arguments. */
132 char **argv
; /* Argument strings. */
134 Tk_Window main
= (Tk_Window
) clientData
;
135 register Square
*squarePtr
;
139 Tcl_AppendResult(interp
, "wrong # args: should be \"",
140 argv
[0], " pathName ?options?\"", (char *) NULL
);
144 tkwin
= Tk_CreateWindowFromPath(interp
, main
, argv
[1], (char *) NULL
);
150 * Initialize fields that won't be initialized by ConfigureSquare,
151 * or which ConfigureSquare expects to have reasonable values
152 * (e.g. resource pointers).
155 squarePtr
= (Square
*) ckalloc(sizeof(Square
));
156 squarePtr
->tkwin
= tkwin
;
157 squarePtr
->interp
= interp
;
160 squarePtr
->size
= 20;
161 squarePtr
->bgBorder
= NULL
;
162 squarePtr
->fgBorder
= NULL
;
163 squarePtr
->flags
= 0;
165 Tk_SetClass(squarePtr
->tkwin
, "Square");
166 Tk_CreateEventHandler(squarePtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
167 SquareEventProc
, (ClientData
) squarePtr
);
168 Tcl_CreateCommand(interp
, Tk_PathName(squarePtr
->tkwin
), SquareWidgetCmd
,
169 (ClientData
) squarePtr
, (void (*)()) NULL
);
170 if (ConfigureSquare(interp
, squarePtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
171 Tk_DestroyWindow(squarePtr
->tkwin
);
175 interp
->result
= Tk_PathName(squarePtr
->tkwin
);
180 *----------------------------------------------------------------------
184 * This procedure is called to process an argv/argc list in
185 * conjunction with the Tk option database to configure (or
186 * reconfigure) a square widget.
189 * The return value is a standard Tcl result. If TCL_ERROR is
190 * returned, then interp->result contains an error message.
193 * Configuration information, such as colors, border width,
194 * etc. get set for squarePtr; old resources get freed,
197 *----------------------------------------------------------------------
201 ConfigureSquare(interp
, squarePtr
, argc
, argv
, flags
)
202 Tcl_Interp
*interp
; /* Used for error reporting. */
203 register Square
*squarePtr
; /* Information about widget. */
204 int argc
; /* Number of valid entries in argv. */
205 char **argv
; /* Arguments. */
206 int flags
; /* Flags to pass to
207 * Tk_ConfigureWidget. */
209 if (Tk_ConfigureWidget(interp
, squarePtr
->tkwin
, configSpecs
,
210 argc
, argv
, (char *) squarePtr
, flags
) != TCL_OK
) {
215 * A few options need special processing, such as setting the
216 * background from a 3-D border.
219 Tk_SetBackgroundFromBorder(squarePtr
->tkwin
, squarePtr
->bgBorder
);
222 * Register the desired geometry for the window. Then arrange for
223 * the window to be redisplayed.
226 Tk_GeometryRequest(squarePtr
->tkwin
, 200, 150);
227 Tk_SetInternalBorder(squarePtr
->tkwin
, squarePtr
->borderWidth
);
228 if (!(squarePtr
->flags
& REDRAW_PENDING
)) {
229 Tk_DoWhenIdle(DisplaySquare
, (ClientData
) squarePtr
);
230 squarePtr
->flags
|= REDRAW_PENDING
;
236 *--------------------------------------------------------------
240 * This procedure redraws the contents of a square window.
241 * It is invoked as a do-when-idle handler, so it only runs
242 * when there's nothing else for the application to do.
248 * Information appears on the screen.
250 *--------------------------------------------------------------
254 DisplaySquare(clientData
)
255 ClientData clientData
; /* Information about window. */
257 register Square
*squarePtr
= (Square
*) clientData
;
258 register Tk_Window tkwin
= squarePtr
->tkwin
;
262 squarePtr
->flags
&= ~REDRAW_PENDING
;
263 if ((tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
268 * Create a pixmap for double-buffering, if necessary.
271 if (squarePtr
->doubleBuffer
) {
272 pm
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
273 Tk_Width(tkwin
), Tk_Height(tkwin
),
274 DefaultDepthOfScreen(Tk_Screen(tkwin
)));
277 d
= Tk_WindowId(tkwin
);
281 * Redraw the widget's background and border.
284 Tk_Fill3DRectangle(Tk_Display(tkwin
), d
, squarePtr
->bgBorder
,
285 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
286 squarePtr
->borderWidth
, squarePtr
->relief
);
289 * Display the square.
292 Tk_Fill3DRectangle(Tk_Display(tkwin
), d
, squarePtr
->fgBorder
,
293 squarePtr
->x
, squarePtr
->y
, squarePtr
->size
, squarePtr
->size
,
294 squarePtr
->borderWidth
, TK_RELIEF_RAISED
);
297 * If double-buffered, copy to the screen and release the pixmap.
300 if (squarePtr
->doubleBuffer
) {
301 XCopyArea(Tk_Display(tkwin
), pm
, Tk_WindowId(tkwin
),
302 DefaultGCOfScreen(Tk_Screen(tkwin
)), 0, 0,
303 Tk_Width(tkwin
), Tk_Height(tkwin
), 0, 0);
304 XFreePixmap(Tk_Display(tkwin
), pm
);
309 *--------------------------------------------------------------
313 * This procedure is invoked to process the Tcl command
314 * that corresponds to a widget managed by this module.
315 * See the user documentation for details on what it does.
318 * A standard Tcl result.
321 * See the user documentation.
323 *--------------------------------------------------------------
327 SquareWidgetCmd(clientData
, interp
, argc
, argv
)
328 ClientData clientData
; /* Information about square widget. */
329 Tcl_Interp
*interp
; /* Current interpreter. */
330 int argc
; /* Number of arguments. */
331 char **argv
; /* Argument strings. */
333 register Square
*squarePtr
= (Square
*) clientData
;
339 Tcl_AppendResult(interp
, "wrong # args: should be \"",
340 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
343 Tk_Preserve((ClientData
) squarePtr
);
345 length
= strlen(argv
[1]);
346 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
348 result
= Tk_ConfigureInfo(interp
, squarePtr
->tkwin
, configSpecs
,
349 (char *) squarePtr
, (char *) NULL
, 0);
350 } else if (argc
== 3) {
351 result
= Tk_ConfigureInfo(interp
, squarePtr
->tkwin
, configSpecs
,
352 (char *) squarePtr
, argv
[2], 0);
354 result
= ConfigureSquare(interp
, squarePtr
, argc
-2, argv
+2,
355 TK_CONFIG_ARGV_ONLY
);
357 } else if ((c
== 'p') && (strncmp(argv
[1], "position", length
) == 0)) {
358 if ((argc
!= 2) && (argc
!= 4)) {
359 Tcl_AppendResult(interp
, "wrong # args: should be \"",
360 argv
[0], " position ?x y?\"", (char *) NULL
);
364 if ((Tcl_GetInt(interp
, argv
[2], &squarePtr
->x
) != TCL_OK
)
365 || (Tcl_GetInt(interp
, argv
[3], &squarePtr
->y
) != TCL_OK
)) {
368 KeepInWindow(squarePtr
);
370 sprintf(interp
->result
, "%d %d", squarePtr
->x
, squarePtr
->y
);
371 } else if ((c
== 's') && (strncmp(argv
[1], "size", length
) == 0)) {
372 if ((argc
!= 2) && (argc
!= 3)) {
373 Tcl_AppendResult(interp
, "wrong # args: should be \"",
374 argv
[0], " size ?amount?\"", (char *) NULL
);
380 if (Tcl_GetInt(interp
, argv
[2], &i
) != TCL_OK
) {
383 if ((i
<= 0) || (i
> 100)) {
384 Tcl_AppendResult(interp
, "bad size \"", argv
[2],
385 "\"", (char *) NULL
);
389 KeepInWindow(squarePtr
);
391 sprintf(interp
->result
, "%d", squarePtr
->size
);
393 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
394 "\": must be configure, position, or size", (char *) NULL
);
397 if (!(squarePtr
->flags
& REDRAW_PENDING
)) {
398 Tk_DoWhenIdle(DisplaySquare
, (ClientData
) squarePtr
);
399 squarePtr
->flags
|= REDRAW_PENDING
;
401 Tk_Release((ClientData
) squarePtr
);
405 Tk_Release((ClientData
) squarePtr
);
410 *--------------------------------------------------------------
414 * This procedure is invoked by the Tk dispatcher for various
421 * When the window gets deleted, internal structures get
422 * cleaned up. When it gets exposed, it is redisplayed.
424 *--------------------------------------------------------------
428 SquareEventProc(clientData
, eventPtr
)
429 ClientData clientData
; /* Information about window. */
430 XEvent
*eventPtr
; /* Information about event. */
432 Square
*squarePtr
= (Square
*) clientData
;
434 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
435 if (!(squarePtr
->flags
& REDRAW_PENDING
)) {
436 Tk_DoWhenIdle(DisplaySquare
, (ClientData
) squarePtr
);
437 squarePtr
->flags
|= REDRAW_PENDING
;
439 } else if (eventPtr
->type
== ConfigureNotify
) {
440 KeepInWindow(squarePtr
);
441 if (!(squarePtr
->flags
& REDRAW_PENDING
)) {
442 Tk_DoWhenIdle(DisplaySquare
, (ClientData
) squarePtr
);
443 squarePtr
->flags
|= REDRAW_PENDING
;
445 } else if (eventPtr
->type
== DestroyNotify
) {
446 Tcl_DeleteCommand(squarePtr
->interp
, Tk_PathName(squarePtr
->tkwin
));
447 squarePtr
->tkwin
= NULL
;
448 if (squarePtr
->flags
& REDRAW_PENDING
) {
449 Tk_CancelIdleCall(DisplaySquare
, (ClientData
) squarePtr
);
451 Tk_EventuallyFree((ClientData
) squarePtr
, DestroySquare
);
456 *----------------------------------------------------------------------
460 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
461 * to clean up the internal structure of a square at a safe time
462 * (when no-one is using it anymore).
468 * Everything associated with the square is freed up.
470 *----------------------------------------------------------------------
474 DestroySquare(clientData
)
475 ClientData clientData
; /* Info about square widget. */
477 register Square
*squarePtr
= (Square
*) clientData
;
479 if (squarePtr
->bgBorder
!= NULL
) {
480 Tk_Free3DBorder(squarePtr
->bgBorder
);
482 if (squarePtr
->fgBorder
!= NULL
) {
483 Tk_Free3DBorder(squarePtr
->fgBorder
);
485 ckfree((char *) squarePtr
);
489 *----------------------------------------------------------------------
493 * Adjust the position of the square if necessary to keep it in
494 * the widget's window.
500 * The x and y position of the square are adjusted if necessary
501 * to keep the square in the window.
503 *----------------------------------------------------------------------
507 KeepInWindow(squarePtr
)
508 register Square
*squarePtr
; /* Pointer to widget record. */
512 if (squarePtr
->relief
== TK_RELIEF_FLAT
) {
515 bd
= squarePtr
->borderWidth
;
517 i
= (Tk_Width(squarePtr
->tkwin
) - bd
) - (squarePtr
->x
+ squarePtr
->size
);
521 i
= (Tk_Height(squarePtr
->tkwin
) - bd
) - (squarePtr
->y
+ squarePtr
->size
);
525 if (squarePtr
->x
< bd
) {
528 if (squarePtr
->y
< bd
) {