1/* 2 * tkTreeMarquee.c -- 3 * 4 * This module implements the selection rectangle for treectrl widgets. 5 * 6 * Copyright (c) 2002-2009 Tim Baker 7 * 8 * RCS: @(#) $Id: tkTreeMarquee.c,v 1.20 2010/02/27 21:11:04 treectrl Exp $ 9 */ 10 11#include "tkTreeCtrl.h" 12 13typedef struct TreeMarquee_ TreeMarquee_; 14 15/* 16 * The following structure holds info about the selection rectangle. 17 * There is one of these per TreeCtrl. 18 */ 19struct TreeMarquee_ 20{ 21 TreeCtrl *tree; 22 Tk_OptionTable optionTable; 23 int visible; /* -visible option. */ 24 int x1, y1, x2, y2; /* Opposing corners. */ 25 int onScreen; /* TRUE if it was drawn. */ 26 int sx, sy; /* Offset of canvas from top-left 27 * corner of the window when we 28 * were drawn. */ 29 int sw, sh; /* Width & height when drawn. */ 30}; 31 32#define MARQ_CONF_VISIBLE 0x0001 33 34static Tk_OptionSpec optionSpecs[] = { 35 {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, 36 "0", -1, Tk_Offset(TreeMarquee_, visible), 37 0, (ClientData) NULL, MARQ_CONF_VISIBLE}, 38 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, 39 (char *) NULL, 0, -1, 0, 0, 0} 40}; 41 42/* 43 *---------------------------------------------------------------------- 44 * 45 * TreeMarquee_Init -- 46 * 47 * Perform marquee-related initialization when a new TreeCtrl is 48 * created. 49 * 50 * Results: 51 * A standard Tcl result. 52 * 53 * Side effects: 54 * Memory is allocated. 55 * 56 *---------------------------------------------------------------------- 57 */ 58 59int 60TreeMarquee_Init( 61 TreeCtrl *tree /* Widget info. */ 62 ) 63{ 64 TreeMarquee marquee; 65 66 marquee = (TreeMarquee) ckalloc(sizeof(TreeMarquee_)); 67 memset(marquee, '\0', sizeof(TreeMarquee_)); 68 marquee->tree = tree; 69 marquee->optionTable = Tk_CreateOptionTable(tree->interp, optionSpecs); 70 if (Tk_InitOptions(tree->interp, (char *) marquee, marquee->optionTable, 71 tree->tkwin) != TCL_OK) { 72 WFREE(marquee, TreeMarquee_); 73 return TCL_ERROR; 74 } 75 tree->marquee = marquee; 76 return TCL_OK; 77} 78 79/* 80 *---------------------------------------------------------------------- 81 * 82 * TreeMarquee_Free -- 83 * 84 * Free marquee-related resources when a TreeCtrl is deleted. 85 * 86 * Results: 87 * None. 88 * 89 * Side effects: 90 * Memory is deallocated. 91 * 92 *---------------------------------------------------------------------- 93 */ 94 95void 96TreeMarquee_Free( 97 TreeMarquee marquee /* Marquee token. */ 98 ) 99{ 100 Tk_FreeConfigOptions((char *) marquee, marquee->optionTable, 101 marquee->tree->tkwin); 102 WFREE(marquee, TreeMarquee_); 103} 104 105/* 106 *---------------------------------------------------------------------- 107 * 108 * TreeMarquee_IsXOR -- 109 * 110 * Return true if the marquee is being drawn with XOR. 111 * 112 * Results: 113 * None. 114 * 115 * Side effects: 116 * None. 117 * 118 *---------------------------------------------------------------------- 119 */ 120 121int TreeMarquee_IsXOR(TreeMarquee marquee) 122{ 123#if defined(WIN32) 124 return FALSE; /* TRUE on XP, FALSE on Win7 (lots of flickering) */ 125#elif defined(MAC_TK_CARBON) 126 return TRUE; 127#elif defined(MAC_TK_COCOA) 128 return FALSE; 129#else 130 return TRUE; /* X11 */ 131#endif 132} 133 134/* 135 *---------------------------------------------------------------------- 136 * 137 * TreeMarquee_IsVisible -- 138 * 139 * Return true if the marquee is being drawn. 140 * 141 * Results: 142 * None. 143 * 144 * Side effects: 145 * None. 146 * 147 *---------------------------------------------------------------------- 148 */ 149 150int TreeMarquee_IsVisible(TreeMarquee marquee) 151{ 152 return marquee->visible; 153} 154 155/* 156 *---------------------------------------------------------------------- 157 * 158 * TreeMarquee_Display -- 159 * 160 * Draw the selection rectangle if it is not already displayed and if 161 * it's -visible option is TRUE. 162 * 163 * Results: 164 * None. 165 * 166 * Side effects: 167 * Stuff is drawn. 168 * 169 *---------------------------------------------------------------------- 170 */ 171 172void 173TreeMarquee_Display( 174 TreeMarquee marquee /* Marquee token. */ 175 ) 176{ 177 TreeCtrl *tree = marquee->tree; 178 179 if (!marquee->onScreen && marquee->visible) { 180 if (TreeMarquee_IsXOR(marquee)) { 181 marquee->sx = 0 - tree->xOrigin; 182 marquee->sy = 0 - tree->yOrigin; 183 TreeMarquee_DrawXOR(marquee, Tk_WindowId(tree->tkwin), 184 marquee->sx, marquee->sy); 185 } else { 186 marquee->sx = MIN(marquee->x1, marquee->x2) - tree->xOrigin; 187 marquee->sy = MIN(marquee->y1, marquee->y2) - tree->yOrigin; 188 marquee->sw = abs(marquee->x2 - marquee->x1) + 1; 189 marquee->sh = abs(marquee->y2 - marquee->y1) + 1; 190/* Tree_InvalidateItemArea(tree, marquee->sx, marquee->sy, 191 marquee->sx + marquee->sw, marquee->sy + marquee->sh);*/ 192 Tree_EventuallyRedraw(tree); 193 } 194 marquee->onScreen = TRUE; 195 } 196} 197 198/* 199 *---------------------------------------------------------------------- 200 * 201 * TreeMarquee_Undisplay -- 202 * 203 * Erase the selection rectangle if it is displayed. 204 * 205 * Results: 206 * None. 207 * 208 * Side effects: 209 * Stuff is drawn. 210 * 211 *---------------------------------------------------------------------- 212 */ 213 214void 215TreeMarquee_Undisplay( 216 TreeMarquee marquee /* Marquee token. */ 217 ) 218{ 219 TreeCtrl *tree = marquee->tree; 220 221 if (marquee->onScreen) { 222 if (TreeMarquee_IsXOR(marquee)) { 223 TreeMarquee_DrawXOR(marquee, Tk_WindowId(tree->tkwin), marquee->sx, marquee->sy); 224 } else { 225/* Tree_InvalidateItemArea(tree, marquee->sx, marquee->sy, 226 marquee->sx + marquee->sw, marquee->sy + marquee->sh);*/ 227 Tree_EventuallyRedraw(tree); 228 } 229 marquee->onScreen = FALSE; 230 } 231} 232 233/* 234 *---------------------------------------------------------------------- 235 * 236 * TreeMarquee_DrawXOR -- 237 * 238 * Draw (or erase) the selection rectangle. 239 * 240 * Results: 241 * None. 242 * 243 * Side effects: 244 * Stuff is drawn (or erased, since this is XOR drawing). 245 * 246 *---------------------------------------------------------------------- 247 */ 248 249void 250TreeMarquee_DrawXOR( 251 TreeMarquee marquee, /* Marquee token. */ 252 Drawable drawable, /* Where to draw. */ 253 int x1, int y1 /* Offset of canvas from top-left corner 254 * of the window. */ 255 ) 256{ 257 TreeCtrl *tree = marquee->tree; 258 int x, y, w, h; 259 DotState dotState; 260 261 x = MIN(marquee->x1, marquee->x2); 262 w = abs(marquee->x1 - marquee->x2) + 1; 263 y = MIN(marquee->y1, marquee->y2); 264 h = abs(marquee->y1 - marquee->y2) + 1; 265 266 TreeDotRect_Setup(tree, drawable, &dotState); 267 TreeDotRect_Draw(&dotState, x1 + x, y1 + y, w, h); 268 TreeDotRect_Restore(&dotState); 269} 270 271/* 272 *---------------------------------------------------------------------- 273 * 274 * TreeMarquee_Draw -- 275 * 276 * Draw the selection rectangle if it is visible. 277 * 278 * Results: 279 * None. 280 * 281 * Side effects: 282 * Stuff is drawn. 283 * 284 *---------------------------------------------------------------------- 285 */ 286 287void 288TreeMarquee_Draw( 289 TreeMarquee marquee, /* Marquee token. */ 290 TreeDrawable td) /* Where to draw. */ 291{ 292#if 1 /* Use XOR dotted rectangles where possible. */ 293 TreeCtrl *tree = marquee->tree; 294 295 if (!marquee->visible) 296 return; 297 298 /* Yes this is XOR drawing but we aren't erasing the previous 299 * marquee as when TreeMarquee_IsXOR() returns TRUE. */ 300 TreeMarquee_DrawXOR(marquee, td.drawable, 301 0 - tree->xOrigin, 0 - tree->yOrigin); 302#else /* */ 303 TreeCtrl *tree = marquee->tree; 304 int x, y, w, h; 305 GC gc; 306 XGCValues gcValues; 307 unsigned long mask; 308#ifdef WIN32 309 XPoint points[5]; 310 XRectangle rect; 311#endif 312#if 0 313 XColor *colorPtr; 314#endif 315 316 if (!marquee->visible) 317 return; 318 319 x = MIN(marquee->x1, marquee->x2); 320 w = abs(marquee->x1 - marquee->x2) + 1; 321 y = MIN(marquee->y1, marquee->y2); 322 h = abs(marquee->y1 - marquee->y2) + 1; 323 324#if 0 325 colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "gray50"); 326 gc = Tk_GCForColor(colorPtr, Tk_WindowId(tree->tkwin)); 327 328 XFillRectangle(tree->display, td.drawable, gc, 329 x - tree->drawableXOrigin, y - tree->drawableYOrigin, 330 w - 1, h - 1); 331#else /* Stippled rectangles: BUG not clipped to contentbox. */ 332 gcValues.stipple = Tk_GetBitmap(tree->interp, tree->tkwin, "gray50"); 333 gcValues.fill_style = FillStippled; 334 mask = GCStipple|GCFillStyle; 335 gc = Tk_GetGC(tree->tkwin, mask, &gcValues); 336 337#ifdef WIN32 338 /* XDrawRectangle ignores the stipple pattern. */ 339 rect.x = x - tree->drawableXOrigin; 340 rect.y = y - tree->drawableYOrigin; 341 rect.width = w; 342 rect.height = h; 343 points[0].x = rect.x, points[0].y = rect.y; 344 points[1].x = rect.x + rect.width - 1, points[1].y = rect.y; 345 points[2].x = rect.x + rect.width - 1, points[2].y = rect.y + rect.height - 1; 346 points[3].x = rect.x, points[3].y = rect.y + rect.height - 1; 347 points[4] = points[0]; 348 XDrawLines(tree->display, td.drawable, gc, points, 5, CoordModeOrigin); 349#else 350 XDrawRectangle(tree->display, td.drawable, gc, 351 x - tree->drawableXOrigin, y - tree->drawableYOrigin, 352 w - 1, h - 1); 353#endif 354 Tk_FreeGC(tree->display, gc); 355#endif 356#endif /* */ 357} 358 359/* 360 *---------------------------------------------------------------------- 361 * 362 * Marquee_Config -- 363 * 364 * This procedure is called to process an objc/objv list to set 365 * configuration options for a Marquee. 366 * 367 * Results: 368 * The return value is a standard Tcl result. If TCL_ERROR is 369 * returned, then an error message is left in interp's result. 370 * 371 * Side effects: 372 * Configuration information, such as text string, colors, font, 373 * etc. get set for marquee; old resources get freed, if there 374 * were any. Display changes may occur. 375 * 376 *---------------------------------------------------------------------- 377 */ 378 379static int 380Marquee_Config( 381 TreeMarquee marquee, /* Marquee record. */ 382 int objc, /* Number of arguments. */ 383 Tcl_Obj *CONST objv[] /* Argument values. */ 384 ) 385{ 386 TreeCtrl *tree = marquee->tree; 387 Tk_SavedOptions savedOptions; 388 int error; 389 Tcl_Obj *errorResult = NULL; 390 int mask; 391 392 for (error = 0; error <= 1; error++) { 393 if (error == 0) { 394 if (Tk_SetOptions(tree->interp, (char *) marquee, marquee->optionTable, 395 objc, objv, tree->tkwin, &savedOptions, &mask) != TCL_OK) { 396 mask = 0; 397 continue; 398 } 399 400 /* xxx */ 401 402 Tk_FreeSavedOptions(&savedOptions); 403 break; 404 } else { 405 errorResult = Tcl_GetObjResult(tree->interp); 406 Tcl_IncrRefCount(errorResult); 407 Tk_RestoreSavedOptions(&savedOptions); 408 409 /* xxx */ 410 411 Tcl_SetObjResult(tree->interp, errorResult); 412 Tcl_DecrRefCount(errorResult); 413 return TCL_ERROR; 414 } 415 } 416 417 if (mask & MARQ_CONF_VISIBLE) { 418 TreeMarquee_Undisplay(marquee); 419 TreeMarquee_Display(marquee); 420 } 421 422 return TCL_OK; 423} 424 425/* 426 *---------------------------------------------------------------------- 427 * 428 * TreeMarqueeCmd -- 429 * 430 * This procedure is invoked to process the [marquee] widget 431 * command. See the user documentation for details on what it 432 * does. 433 * 434 * Results: 435 * A standard Tcl result. 436 * 437 * Side effects: 438 * See the user documentation. 439 * 440 *---------------------------------------------------------------------- 441 */ 442 443int 444TreeMarqueeCmd( 445 ClientData clientData, /* Widget info. */ 446 Tcl_Interp *interp, /* Current interpreter. */ 447 int objc, /* Number of arguments. */ 448 Tcl_Obj *CONST objv[] /* Argument values. */ 449 ) 450{ 451 TreeCtrl *tree = clientData; 452 TreeMarquee marquee = tree->marquee; 453 static CONST char *commandNames[] = { "anchor", "cget", "configure", 454 "coords", "corner", "identify", (char *) NULL }; 455 enum { COMMAND_ANCHOR, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_COORDS, 456 COMMAND_CORNER, COMMAND_IDENTIFY }; 457 int index; 458 459 if (objc < 3) { 460 Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?"); 461 return TCL_ERROR; 462 } 463 464 if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, 465 &index) != TCL_OK) { 466 return TCL_ERROR; 467 } 468 469 switch (index) { 470 /* T marquee anchor ?x y?*/ 471 case COMMAND_ANCHOR: { 472 int x, y; 473 474 if (objc != 3 && objc != 5) { 475 Tcl_WrongNumArgs(interp, 3, objv, "?x y?"); 476 return TCL_ERROR; 477 } 478 if (objc == 3) { 479 FormatResult(interp, "%d %d", marquee->x1, marquee->y1); 480 break; 481 } 482 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) 483 return TCL_ERROR; 484 if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) 485 return TCL_ERROR; 486 if ((x == marquee->x1) && (y == marquee->y1)) 487 break; 488 TreeMarquee_Undisplay(tree->marquee); 489 marquee->x1 = x; 490 marquee->y1 = y; 491 TreeMarquee_Display(tree->marquee); 492 break; 493 } 494 495 /* T marquee cget option */ 496 case COMMAND_CGET: { 497 Tcl_Obj *resultObjPtr; 498 499 if (objc != 4) { 500 Tcl_WrongNumArgs(interp, 3, objv, "option"); 501 return TCL_ERROR; 502 } 503 resultObjPtr = Tk_GetOptionValue(interp, (char *) marquee, 504 marquee->optionTable, objv[3], tree->tkwin); 505 if (resultObjPtr == NULL) 506 return TCL_ERROR; 507 Tcl_SetObjResult(interp, resultObjPtr); 508 break; 509 } 510 511 /* T marquee configure ?option? ?value? ?option value ...? */ 512 case COMMAND_CONFIGURE: { 513 Tcl_Obj *resultObjPtr; 514 515 if (objc < 3) { 516 Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); 517 return TCL_ERROR; 518 } 519 if (objc <= 4) { 520 resultObjPtr = Tk_GetOptionInfo(interp, (char *) marquee, 521 marquee->optionTable, 522 (objc == 3) ? (Tcl_Obj *) NULL : objv[3], 523 tree->tkwin); 524 if (resultObjPtr == NULL) 525 return TCL_ERROR; 526 Tcl_SetObjResult(interp, resultObjPtr); 527 break; 528 } 529 return Marquee_Config(marquee, objc - 3, objv + 3); 530 } 531 532 /* T marquee coords ?x y x y? */ 533 case COMMAND_COORDS: { 534 int x1, y1, x2, y2; 535 536 if (objc != 3 && objc != 7) { 537 Tcl_WrongNumArgs(interp, 3, objv, "?x y x y?"); 538 return TCL_ERROR; 539 } 540 if (objc == 3) { 541 FormatResult(interp, "%d %d %d %d", marquee->x1, marquee->y1, 542 marquee->x2, marquee->y2); 543 break; 544 } 545 if (Tcl_GetIntFromObj(interp, objv[3], &x1) != TCL_OK) 546 return TCL_ERROR; 547 if (Tcl_GetIntFromObj(interp, objv[4], &y1) != TCL_OK) 548 return TCL_ERROR; 549 if (Tcl_GetIntFromObj(interp, objv[5], &x2) != TCL_OK) 550 return TCL_ERROR; 551 if (Tcl_GetIntFromObj(interp, objv[6], &y2) != TCL_OK) 552 return TCL_ERROR; 553 if (x1 == marquee->x1 && y1 == marquee->y1 && 554 x2 == marquee->x2 && y2 == marquee->y2) 555 break; 556 TreeMarquee_Undisplay(tree->marquee); 557 marquee->x1 = x1; 558 marquee->y1 = y1; 559 marquee->x2 = x2; 560 marquee->y2 = y2; 561 TreeMarquee_Display(tree->marquee); 562 break; 563 } 564 565 /* T marquee corner ?x y?*/ 566 case COMMAND_CORNER: { 567 int x, y; 568 569 if (objc != 3 && objc != 5) { 570 Tcl_WrongNumArgs(interp, 3, objv, "?x y?"); 571 return TCL_ERROR; 572 } 573 if (objc == 3) { 574 FormatResult(interp, "%d %d", marquee->x2, marquee->y2); 575 break; 576 } 577 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) 578 return TCL_ERROR; 579 if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK) 580 return TCL_ERROR; 581 if (x == marquee->x2 && y == marquee->y2) 582 break; 583 TreeMarquee_Undisplay(tree->marquee); 584 marquee->x2 = x; 585 marquee->y2 = y; 586 TreeMarquee_Display(tree->marquee); 587 break; 588 } 589 590 /* T marquee identify */ 591 case COMMAND_IDENTIFY: { 592 int x1, y1, x2, y2, n = 0; 593 int totalWidth = Tree_TotalWidth(tree); 594 int totalHeight = Tree_TotalHeight(tree); 595 TreeItemList items; 596 Tcl_Obj *listObj; 597 598 if (objc != 3) { 599 Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL); 600 return TCL_ERROR; 601 } 602 603 x1 = MIN(marquee->x1, marquee->x2); 604 x2 = MAX(marquee->x1, marquee->x2); 605 y1 = MIN(marquee->y1, marquee->y2); 606 y2 = MAX(marquee->y1, marquee->y2); 607 608 if (x2 <= 0) 609 break; 610 if (x1 >= totalWidth) 611 break; 612 613 if (y2 <= 0) 614 break; 615 if (y1 >= totalHeight) 616 break; 617 618 if (x1 < 0) 619 x1 = 0; 620 if (x2 > totalWidth) 621 x2 = totalWidth; 622 623 if (y1 < 0) 624 y1 = 0; 625 if (y2 > totalHeight) 626 y2 = totalHeight; 627 628 Tree_ItemsInArea(tree, &items, x1, y1, x2, y2); 629 if (TreeItemList_Count(&items) == 0) { 630 TreeItemList_Free(&items); 631 break; 632 } 633 634 listObj = Tcl_NewListObj(0, NULL); 635 for (n = 0; n < TreeItemList_Count(&items); n++) { 636 Tcl_Obj *subListObj = Tcl_NewListObj(0, NULL); 637 TreeItem item = TreeItemList_Nth(&items, n); 638 Tcl_ListObjAppendElement(interp, subListObj, 639 TreeItem_ToObj(tree, item)); 640 TreeItem_Identify2(tree, item, x1, y1, x2, y2, subListObj); 641 Tcl_ListObjAppendElement(interp, listObj, subListObj); 642 } 643 TreeItemList_Free(&items); 644 Tcl_SetObjResult(interp, listObj); 645 break; 646 } 647 } 648 649 return TCL_OK; 650} 651