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; it isn't included in the
8
* normal wish, but it is included in "tktest".
10
* Copyright (c) 1991-1994 The Regents of the University of California.
11
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
13
* See the file "license.terms" for information on usage and redistribution
14
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
* RCS: @(#) $Id: tkSquare.c,v 1.2 1998/09/14 18:23:17 stanton Exp $
23
* A data structure of the following type is kept for each square
24
* widget managed by this file:
28
Tk_Window tkwin; /* Window that embodies the square. NULL
29
* means window has been deleted but
30
* widget record hasn't been cleaned up yet. */
31
Display *display; /* X's token for the window's display. */
32
Tcl_Interp *interp; /* Interpreter associated with widget. */
33
Tcl_Command widgetCmd; /* Token for square's widget command. */
34
int x, y; /* Position of square's upper-left corner
36
int size; /* Width and height of square. */
39
* Information used when displaying widget:
42
int borderWidth; /* Width of 3-D border around whole widget. */
43
Tk_3DBorder bgBorder; /* Used for drawing background. */
44
Tk_3DBorder fgBorder; /* For drawing square. */
45
int relief; /* Indicates whether window as a whole is
46
* raised, sunken, or flat. */
47
GC gc; /* Graphics context for copying from
48
* off-screen pixmap onto screen. */
49
int doubleBuffer; /* Non-zero means double-buffer redisplay
50
* with pixmap; zero means draw straight
51
* onto the display. */
52
int updatePending; /* Non-zero means a call to SquareDisplay
53
* has already been scheduled. */
57
* Information used for argv parsing.
60
static Tk_ConfigSpec configSpecs[] = {
61
{TK_CONFIG_BORDER, "-background", "background", "Background",
62
"#d9d9d9", Tk_Offset(Square, bgBorder), TK_CONFIG_COLOR_ONLY},
63
{TK_CONFIG_BORDER, "-background", "background", "Background",
64
"white", Tk_Offset(Square, bgBorder), TK_CONFIG_MONO_ONLY},
65
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
67
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
69
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
70
"2", Tk_Offset(Square, borderWidth), 0},
71
{TK_CONFIG_INT, "-dbl", "doubleBuffer", "DoubleBuffer",
72
"1", Tk_Offset(Square, doubleBuffer), 0},
73
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
75
{TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
76
"#b03060", Tk_Offset(Square, fgBorder), TK_CONFIG_COLOR_ONLY},
77
{TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
78
"black", Tk_Offset(Square, fgBorder), TK_CONFIG_MONO_ONLY},
79
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
80
"raised", Tk_Offset(Square, relief), 0},
81
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
86
* Forward declarations for procedures defined later in this file:
89
int SquareCmd _ANSI_ARGS_((ClientData clientData,
90
Tcl_Interp *interp, int argc, char **argv));
91
static void SquareCmdDeletedProc _ANSI_ARGS_((
92
ClientData clientData));
93
static int SquareConfigure _ANSI_ARGS_((Tcl_Interp *interp,
94
Square *squarePtr, int argc, char **argv,
96
static void SquareDestroy _ANSI_ARGS_((char *memPtr));
97
static void SquareDisplay _ANSI_ARGS_((ClientData clientData));
98
static void KeepInWindow _ANSI_ARGS_((Square *squarePtr));
99
static void SquareEventProc _ANSI_ARGS_((ClientData clientData,
101
static int SquareWidgetCmd _ANSI_ARGS_((ClientData clientData,
102
Tcl_Interp *, int argc, char **argv));
105
*--------------------------------------------------------------
109
* This procedure is invoked to process the "square" Tcl
110
* command. It creates a new "square" widget.
113
* A standard Tcl result.
116
* A new widget is created and configured.
118
*--------------------------------------------------------------
122
SquareCmd(clientData, interp, argc, argv)
123
ClientData clientData; /* Main window associated with
125
Tcl_Interp *interp; /* Current interpreter. */
126
int argc; /* Number of arguments. */
127
char **argv; /* Argument strings. */
129
Tk_Window main = (Tk_Window) clientData;
134
Tcl_AppendResult(interp, "wrong # args: should be \"",
135
argv[0], " pathName ?options?\"", (char *) NULL);
139
tkwin = Tk_CreateWindowFromPath(interp, main, argv[1], (char *) NULL);
143
Tk_SetClass(tkwin, "Square");
146
* Allocate and initialize the widget record.
149
squarePtr = (Square *) ckalloc(sizeof(Square));
150
squarePtr->tkwin = tkwin;
151
squarePtr->display = Tk_Display(tkwin);
152
squarePtr->interp = interp;
153
squarePtr->widgetCmd = Tcl_CreateCommand(interp,
154
Tk_PathName(squarePtr->tkwin), SquareWidgetCmd,
155
(ClientData) squarePtr, SquareCmdDeletedProc);
158
squarePtr->size = 20;
159
squarePtr->borderWidth = 0;
160
squarePtr->bgBorder = NULL;
161
squarePtr->fgBorder = NULL;
162
squarePtr->relief = TK_RELIEF_FLAT;
163
squarePtr->gc = None;
164
squarePtr->doubleBuffer = 1;
165
squarePtr->updatePending = 0;
167
Tk_CreateEventHandler(squarePtr->tkwin, ExposureMask|StructureNotifyMask,
168
SquareEventProc, (ClientData) squarePtr);
169
if (SquareConfigure(interp, squarePtr, argc-2, argv+2, 0) != TCL_OK) {
170
Tk_DestroyWindow(squarePtr->tkwin);
174
interp->result = Tk_PathName(squarePtr->tkwin);
179
*--------------------------------------------------------------
183
* This procedure is invoked to process the Tcl command
184
* that corresponds to a widget managed by this module.
185
* See the user documentation for details on what it does.
188
* A standard Tcl result.
191
* See the user documentation.
193
*--------------------------------------------------------------
197
SquareWidgetCmd(clientData, interp, argc, argv)
198
ClientData clientData; /* Information about square widget. */
199
Tcl_Interp *interp; /* Current interpreter. */
200
int argc; /* Number of arguments. */
201
char **argv; /* Argument strings. */
203
Square *squarePtr = (Square *) clientData;
209
Tcl_AppendResult(interp, "wrong # args: should be \"",
210
argv[0], " option ?arg arg ...?\"", (char *) NULL);
213
Tcl_Preserve((ClientData) squarePtr);
215
length = strlen(argv[1]);
216
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
219
Tcl_AppendResult(interp, "wrong # args: should be \"",
220
argv[0], " cget option\"",
224
result = Tk_ConfigureValue(interp, squarePtr->tkwin, configSpecs,
225
(char *) squarePtr, argv[2], 0);
226
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
229
result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
230
(char *) squarePtr, (char *) NULL, 0);
231
} else if (argc == 3) {
232
result = Tk_ConfigureInfo(interp, squarePtr->tkwin, configSpecs,
233
(char *) squarePtr, argv[2], 0);
235
result = SquareConfigure(interp, squarePtr, argc-2, argv+2,
236
TK_CONFIG_ARGV_ONLY);
238
} else if ((c == 'p') && (strncmp(argv[1], "position", length) == 0)) {
239
if ((argc != 2) && (argc != 4)) {
240
Tcl_AppendResult(interp, "wrong # args: should be \"",
241
argv[0], " position ?x y?\"", (char *) NULL);
245
if ((Tk_GetPixels(interp, squarePtr->tkwin, argv[2],
246
&squarePtr->x) != TCL_OK) || (Tk_GetPixels(interp,
247
squarePtr->tkwin, argv[3], &squarePtr->y) != TCL_OK)) {
250
KeepInWindow(squarePtr);
252
sprintf(interp->result, "%d %d", squarePtr->x, squarePtr->y);
253
} else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)) {
254
if ((argc != 2) && (argc != 3)) {
255
Tcl_AppendResult(interp, "wrong # args: should be \"",
256
argv[0], " size ?amount?\"", (char *) NULL);
262
if (Tk_GetPixels(interp, squarePtr->tkwin, argv[2], &i) != TCL_OK) {
265
if ((i <= 0) || (i > 100)) {
266
Tcl_AppendResult(interp, "bad size \"", argv[2],
267
"\"", (char *) NULL);
271
KeepInWindow(squarePtr);
273
sprintf(interp->result, "%d", squarePtr->size);
275
Tcl_AppendResult(interp, "bad option \"", argv[1],
276
"\": must be cget, configure, position, or size",
280
if (!squarePtr->updatePending) {
281
Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
282
squarePtr->updatePending = 1;
284
Tcl_Release((ClientData) squarePtr);
288
Tcl_Release((ClientData) squarePtr);
293
*----------------------------------------------------------------------
297
* This procedure is called to process an argv/argc list in
298
* conjunction with the Tk option database to configure (or
299
* reconfigure) a square widget.
302
* The return value is a standard Tcl result. If TCL_ERROR is
303
* returned, then interp->result contains an error message.
306
* Configuration information, such as colors, border width,
307
* etc. get set for squarePtr; old resources get freed,
310
*----------------------------------------------------------------------
314
SquareConfigure(interp, squarePtr, argc, argv, flags)
315
Tcl_Interp *interp; /* Used for error reporting. */
316
Square *squarePtr; /* Information about widget. */
317
int argc; /* Number of valid entries in argv. */
318
char **argv; /* Arguments. */
319
int flags; /* Flags to pass to
320
* Tk_ConfigureWidget. */
322
if (Tk_ConfigureWidget(interp, squarePtr->tkwin, configSpecs,
323
argc, argv, (char *) squarePtr, flags) != TCL_OK) {
328
* Set the background for the window and create a graphics context
329
* for use during redisplay.
332
Tk_SetWindowBackground(squarePtr->tkwin,
333
Tk_3DBorderColor(squarePtr->bgBorder)->pixel);
334
if ((squarePtr->gc == None) && (squarePtr->doubleBuffer)) {
336
gcValues.function = GXcopy;
337
gcValues.graphics_exposures = False;
338
squarePtr->gc = Tk_GetGC(squarePtr->tkwin,
339
GCFunction|GCGraphicsExposures, &gcValues);
343
* Register the desired geometry for the window. Then arrange for
344
* the window to be redisplayed.
347
Tk_GeometryRequest(squarePtr->tkwin, 200, 150);
348
Tk_SetInternalBorder(squarePtr->tkwin, squarePtr->borderWidth);
349
if (!squarePtr->updatePending) {
350
Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
351
squarePtr->updatePending = 1;
357
*--------------------------------------------------------------
361
* This procedure is invoked by the Tk dispatcher for various
368
* When the window gets deleted, internal structures get
369
* cleaned up. When it gets exposed, it is redisplayed.
371
*--------------------------------------------------------------
375
SquareEventProc(clientData, eventPtr)
376
ClientData clientData; /* Information about window. */
377
XEvent *eventPtr; /* Information about event. */
379
Square *squarePtr = (Square *) clientData;
381
if (eventPtr->type == Expose) {
382
if (!squarePtr->updatePending) {
383
Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
384
squarePtr->updatePending = 1;
386
} else if (eventPtr->type == ConfigureNotify) {
387
KeepInWindow(squarePtr);
388
if (!squarePtr->updatePending) {
389
Tcl_DoWhenIdle(SquareDisplay, (ClientData) squarePtr);
390
squarePtr->updatePending = 1;
392
} else if (eventPtr->type == DestroyNotify) {
393
if (squarePtr->tkwin != NULL) {
394
squarePtr->tkwin = NULL;
395
Tcl_DeleteCommandFromToken(squarePtr->interp,
396
squarePtr->widgetCmd);
398
if (squarePtr->updatePending) {
399
Tcl_CancelIdleCall(SquareDisplay, (ClientData) squarePtr);
401
Tcl_EventuallyFree((ClientData) squarePtr, SquareDestroy);
406
*----------------------------------------------------------------------
408
* SquareCmdDeletedProc --
410
* This procedure is invoked when a widget command is deleted. If
411
* the widget isn't already in the process of being destroyed,
412
* this command destroys it.
418
* The widget is destroyed.
420
*----------------------------------------------------------------------
424
SquareCmdDeletedProc(clientData)
425
ClientData clientData; /* Pointer to widget record for widget. */
427
Square *squarePtr = (Square *) clientData;
428
Tk_Window tkwin = squarePtr->tkwin;
431
* This procedure could be invoked either because the window was
432
* destroyed and the command was then deleted (in which case tkwin
433
* is NULL) or because the command was deleted, and then this procedure
434
* destroys the widget.
438
squarePtr->tkwin = NULL;
439
Tk_DestroyWindow(tkwin);
444
*--------------------------------------------------------------
448
* This procedure redraws the contents of a square window.
449
* It is invoked as a do-when-idle handler, so it only runs
450
* when there's nothing else for the application to do.
456
* Information appears on the screen.
458
*--------------------------------------------------------------
462
SquareDisplay(clientData)
463
ClientData clientData; /* Information about window. */
465
Square *squarePtr = (Square *) clientData;
466
Tk_Window tkwin = squarePtr->tkwin;
470
squarePtr->updatePending = 0;
471
if (!Tk_IsMapped(tkwin)) {
476
* Create a pixmap for double-buffering, if necessary.
479
if (squarePtr->doubleBuffer) {
480
pm = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
481
Tk_Width(tkwin), Tk_Height(tkwin),
482
DefaultDepthOfScreen(Tk_Screen(tkwin)));
485
d = Tk_WindowId(tkwin);
489
* Redraw the widget's background and border.
492
Tk_Fill3DRectangle(tkwin, d, squarePtr->bgBorder, 0, 0, Tk_Width(tkwin),
493
Tk_Height(tkwin), squarePtr->borderWidth, squarePtr->relief);
496
* Display the square.
499
Tk_Fill3DRectangle(tkwin, d, squarePtr->fgBorder, squarePtr->x,
500
squarePtr->y, squarePtr->size, squarePtr->size,
501
squarePtr->borderWidth, TK_RELIEF_RAISED);
504
* If double-buffered, copy to the screen and release the pixmap.
507
if (squarePtr->doubleBuffer) {
508
XCopyArea(Tk_Display(tkwin), pm, Tk_WindowId(tkwin), squarePtr->gc,
509
0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
511
Tk_FreePixmap(Tk_Display(tkwin), pm);
516
*----------------------------------------------------------------------
520
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
521
* to clean up the internal structure of a square at a safe time
522
* (when no-one is using it anymore).
528
* Everything associated with the square is freed up.
530
*----------------------------------------------------------------------
534
SquareDestroy(memPtr)
535
char *memPtr; /* Info about square widget. */
537
Square *squarePtr = (Square *) memPtr;
539
Tk_FreeOptions(configSpecs, (char *) squarePtr, squarePtr->display, 0);
540
if (squarePtr->gc != None) {
541
Tk_FreeGC(squarePtr->display, squarePtr->gc);
543
ckfree((char *) squarePtr);
547
*----------------------------------------------------------------------
551
* Adjust the position of the square if necessary to keep it in
552
* the widget's window.
558
* The x and y position of the square are adjusted if necessary
559
* to keep the square in the window.
561
*----------------------------------------------------------------------
565
KeepInWindow(squarePtr)
566
register Square *squarePtr; /* Pointer to widget record. */
570
if (squarePtr->relief != TK_RELIEF_FLAT) {
571
bd = squarePtr->borderWidth;
573
i = (Tk_Width(squarePtr->tkwin) - bd) - (squarePtr->x + squarePtr->size);
577
i = (Tk_Height(squarePtr->tkwin) - bd) - (squarePtr->y + squarePtr->size);
581
if (squarePtr->x < bd) {
584
if (squarePtr->y < bd) {