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