1/* 2 * tkTreeColumn.c -- 3 * 4 * This module implements treectrl widget's columns. 5 * 6 * Copyright (c) 2002-2009 Tim Baker 7 * Copyright (c) 2002-2003 Christian Krone 8 * Copyright (c) 2003 ActiveState Corporation 9 * 10 * RCS: @(#) $Id: tkTreeColumn.c,v 1.92 2010/03/08 17:02:29 treectrl Exp $ 11 */ 12 13#include "tkTreeCtrl.h" 14 15typedef struct TreeColumn_ TreeColumn_; 16 17#ifdef UNIFORM_GROUP 18typedef struct UniformGroup { 19 Tcl_HashEntry *hPtr; /* Entry in TreeCtrl.uniformGroupHash */ 20 int refCount; /* Number of columns in this group. */ 21 int minSize; /* Used during layout. */ 22} UniformGroup; 23#endif 24 25/* 26 * The following structure holds information about a single 27 * column in a TreeCtrl. 28 */ 29struct TreeColumn_ 30{ 31 Tcl_Obj *textObj; /* -text */ 32 char *text; /* -text */ 33 int width; /* -width */ 34 Tcl_Obj *widthObj; /* -width */ 35 int minWidth; /* -minwidth */ 36 Tcl_Obj *minWidthObj; /* -minwidth */ 37 int maxWidth; /* -maxwidth */ 38 Tcl_Obj *maxWidthObj; /* -maxwidth */ 39#ifdef DEPRECATED 40 int stepWidth; /* -stepwidth */ 41 Tcl_Obj *stepWidthObj; /* -stepwidth */ 42 int widthHack; /* -widthhack */ 43#endif /* DEPRECATED */ 44 Tk_Font tkfont; /* -font */ 45 Tk_Justify justify; /* -justify */ 46 int itemJustify; /* -itemjustify */ 47 PerStateInfo border; /* -background */ 48 Tcl_Obj *borderWidthObj; /* -borderwidth */ 49 int borderWidth; /* -borderwidth */ 50 XColor *textColor; /* -textcolor */ 51 int expand; /* -expand */ 52 int squeeze; /* -squeeze */ 53 int visible; /* -visible */ 54 int resize; /* -resize */ 55 TagInfo *tagInfo; /* -tags */ 56 char *imageString; /* -image */ 57 PerStateInfo arrowBitmap; /* -arrowbitmap */ 58 PerStateInfo arrowImage; /* -arrowimage */ 59 Pixmap bitmap; /* -bitmap */ 60 Tcl_Obj *itemBgObj; /* -itembackground */ 61 TreeStyle itemStyle; /* -itemstyle */ 62 int button; /* -button */ 63 Tcl_Obj *textPadXObj; /* -textpadx */ 64 int *textPadX; /* -textpadx */ 65 Tcl_Obj *textPadYObj; /* -textpady */ 66 int *textPadY; /* -textpady */ 67 Tcl_Obj *imagePadXObj; /* -imagepadx */ 68 int *imagePadX; /* -imagepadx */ 69 Tcl_Obj *imagePadYObj; /* -imagepady */ 70 int *imagePadY; /* -imagepady */ 71 Tcl_Obj *arrowPadXObj; /* -arrowpadx */ 72 int *arrowPadX; /* -arrowpadx */ 73 Tcl_Obj *arrowPadYObj; /* -arrowpady */ 74 int *arrowPadY; /* -arrowpady */ 75 76#define ARROW_NONE 0 77#define ARROW_UP 1 78#define ARROW_DOWN 2 79 int arrow; /* -arrow */ 80 81#define SIDE_LEFT 0 82#define SIDE_RIGHT 1 83 int arrowSide; /* -arrowside */ 84 int arrowGravity; /* -arrowgravity */ 85 86#define COLUMN_STATE_NORMAL 0 87#define COLUMN_STATE_ACTIVE 1 88#define COLUMN_STATE_PRESSED 2 89 int state; /* -state */ 90 91 int lock; /* -lock */ 92 93 TreeCtrl *tree; 94 Tk_OptionTable optionTable; 95 int id; /* unique column identifier */ 96 int index; /* order in list of columns */ 97 int textLen; 98 int textWidth; 99 Tk_Image image; 100 int neededWidth; /* calculated from borders + image/bitmap + 101 * text + arrow */ 102 int neededHeight; /* calculated from borders + image/bitmap + 103 * text */ 104 int offset; /* Total width of preceding columns */ 105 int useWidth; /* -width, -minwidth, or required+expansion */ 106 int widthOfItems; /* width of all TreeItemColumns */ 107 int itemBgCount; 108 XColor **itemBgColor; 109 GC bitmapGC; 110 TreeColumn prev; 111 TreeColumn next; 112 TextLayout textLayout; /* multi-line titles */ 113 int textLayoutWidth; /* width passed to TextLayout_Compute */ 114 int textLayoutInvalid; 115#define TEXT_WRAP_NULL -1 116#define TEXT_WRAP_CHAR 0 117#define TEXT_WRAP_WORD 1 118 int textWrap; /* -textwrap */ 119 int textLines; /* -textlines */ 120#ifdef UNIFORM_GROUP 121 UniformGroup *uniform; /* -uniform */ 122 int weight; /* -weight */ 123#endif 124 TreeColumnDInfo dInfo; /* Display info. */ 125}; 126 127#ifdef UNIFORM_GROUP 128/* 129 *---------------------------------------------------------------------- 130 * 131 * UniformGroupCO_Set -- 132 * UniformGroupCO_Get -- 133 * UniformGroupCO_Restore -- 134 * UniformGroupCO_Free -- 135 * 136 * These procedures implement a TK_OPTION_CUSTOM where the custom 137 * option is a UniformGroup. 138 * 139 * Results: 140 * None. 141 * 142 * Side effects: 143 * None. 144 * 145 *---------------------------------------------------------------------- 146 */ 147 148static int 149UniformGroupCO_Set( 150 ClientData clientData, 151 Tcl_Interp *interp, 152 Tk_Window tkwin, 153 Tcl_Obj **valuePtr, 154 char *recordPtr, 155 int internalOffset, 156 char *saveInternalPtr, 157 int flags 158 ) 159{ 160 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; 161 int objEmpty; 162 UniformGroup **internalPtr, *new; 163 164 if (internalOffset >= 0) 165 internalPtr = (UniformGroup **) (recordPtr + internalOffset); 166 else 167 internalPtr = NULL; 168 169 objEmpty = ObjectIsEmpty((*valuePtr)); 170 171 if ((flags & TK_OPTION_NULL_OK) && objEmpty) 172 (*valuePtr) = NULL; 173 174 if (internalPtr != NULL) { 175 if (*valuePtr != NULL) { 176 int isNew; 177 Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&tree->uniformGroupHash, 178 Tcl_GetString(*valuePtr), &isNew); 179 if (isNew) { 180 new = (UniformGroup *) ckalloc(sizeof(UniformGroup)); 181 new->refCount = 0; 182 new->hPtr = hPtr; 183 Tcl_SetHashValue(hPtr, (ClientData) new); 184 } else { 185 new = (UniformGroup *) Tcl_GetHashValue(hPtr); 186 } 187 new->refCount++; 188#ifdef TREECTRL_DEBUG 189 if (tree->debug.enable) 190 dbwin("UniformGroupCO_Set: %s refCount=%d\n", Tcl_GetString(*valuePtr), new->refCount); 191#endif 192 } else { 193 new = NULL; 194 } 195 *((UniformGroup **) saveInternalPtr) = *internalPtr; 196 *internalPtr = new; 197 } 198 199 return TCL_OK; 200} 201 202static Tcl_Obj * 203UniformGroupCO_Get( 204 ClientData clientData, 205 Tk_Window tkwin, 206 char *recordPtr, 207 int internalOffset 208 ) 209{ 210 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; 211 UniformGroup *uniform = *(UniformGroup **) (recordPtr + internalOffset); 212 213 if (uniform == NULL) 214 return NULL; 215 return Tcl_NewStringObj(Tcl_GetHashKey(&tree->uniformGroupHash, 216 uniform->hPtr), -1); 217} 218 219static void 220UniformGroupCO_Restore( 221 ClientData clientData, 222 Tk_Window tkwin, 223 char *internalPtr, 224 char *saveInternalPtr 225 ) 226{ 227 *(UniformGroup **) internalPtr = *(UniformGroup **) saveInternalPtr; 228} 229 230static void 231UniformGroupCO_Free( 232 ClientData clientData, 233 Tk_Window tkwin, 234 char *internalPtr 235 ) 236{ 237 UniformGroup *uniform = *(UniformGroup **) internalPtr; 238 239#ifdef TREECTRL_DEBUG 240 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; 241 if (tree->debug.enable && uniform != NULL) { 242 dbwin("UniformGroupCO_Free: %s refCount=%d\n", Tcl_GetHashKey(&tree->uniformGroupHash, uniform->hPtr), uniform->refCount - 1); 243 } 244#endif 245 if ((uniform != NULL) && (--uniform->refCount <= 0)) { 246 Tcl_DeleteHashEntry(uniform->hPtr); 247 ckfree((char *) uniform); 248 *((UniformGroup **) internalPtr) = NULL; 249 } 250} 251 252static Tk_ObjCustomOption uniformGroupCO = 253{ 254 "uniform group", 255 UniformGroupCO_Set, 256 UniformGroupCO_Get, 257 UniformGroupCO_Restore, 258 UniformGroupCO_Free, 259 (ClientData) NULL 260}; 261#endif /* UNIFORM_GROUP */ 262 263static CONST char *arrowST[] = { "none", "up", "down", (char *) NULL }; 264static CONST char *arrowSideST[] = { "left", "right", (char *) NULL }; 265static CONST char *stateST[] = { "normal", "active", "pressed", (char *) NULL }; 266static CONST char *lockST[] = { "left", "none", "right", (char *) NULL }; 267static CONST char *justifyStrings[] = { 268 "left", "right", "center", (char *) NULL 269}; 270 271#define COLU_CONF_IMAGE 0x0001 272#define COLU_CONF_NWIDTH 0x0002 /* neededWidth */ 273#define COLU_CONF_NHEIGHT 0x0004 /* neededHeight */ 274#define COLU_CONF_TWIDTH 0x0008 /* totalWidth */ 275#define COLU_CONF_ITEMBG 0x0010 276#define COLU_CONF_DISPLAY 0x0040 277#define COLU_CONF_JUSTIFY 0x0080 278#define COLU_CONF_TAGS 0x0100 279#define COLU_CONF_TEXT 0x0200 280#define COLU_CONF_BITMAP 0x0400 281#define COLU_CONF_RANGES 0x0800 282 283static Tk_OptionSpec columnSpecs[] = { 284 {TK_OPTION_STRING_TABLE, "-arrow", (char *) NULL, (char *) NULL, 285 "none", -1, Tk_Offset(TreeColumn_, arrow), 286 0, (ClientData) arrowST, COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 287 {TK_OPTION_CUSTOM, "-arrowbitmap", (char *) NULL, (char *) NULL, 288 (char *) NULL, 289 Tk_Offset(TreeColumn_, arrowBitmap.obj), Tk_Offset(TreeColumn_, arrowBitmap), 290 TK_OPTION_NULL_OK, (ClientData) NULL, 291 COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 292 {TK_OPTION_STRING_TABLE, "-arrowgravity", (char *) NULL, (char *) NULL, 293 "left", -1, Tk_Offset(TreeColumn_, arrowGravity), 294 0, (ClientData) arrowSideST, COLU_CONF_DISPLAY}, 295 {TK_OPTION_CUSTOM, "-arrowimage", (char *) NULL, (char *) NULL, 296 (char *) NULL, 297 Tk_Offset(TreeColumn_, arrowImage.obj), Tk_Offset(TreeColumn_, arrowImage), 298 TK_OPTION_NULL_OK, (ClientData) NULL, 299 COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 300 {TK_OPTION_CUSTOM, "-arrowpadx", (char *) NULL, (char *) NULL, 301 "6", Tk_Offset(TreeColumn_, arrowPadXObj), Tk_Offset(TreeColumn_, arrowPadX), 302 0, (ClientData) &TreeCtrlCO_pad, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, 303 {TK_OPTION_CUSTOM, "-arrowpady", (char *) NULL, (char *) NULL, 304 "0", Tk_Offset(TreeColumn_, arrowPadYObj), Tk_Offset(TreeColumn_, arrowPadY), 305 0, (ClientData) &TreeCtrlCO_pad, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, 306 {TK_OPTION_STRING_TABLE, "-arrowside", (char *) NULL, (char *) NULL, 307 "right", -1, Tk_Offset(TreeColumn_, arrowSide), 308 0, (ClientData) arrowSideST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, 309 /* NOTE: -background is a per-state option, so DEF_BUTTON_BG_COLOR 310 * must be a list of one element */ 311 {TK_OPTION_CUSTOM, "-background", (char *) NULL, (char *) NULL, 312 (char *) NULL /* initialized later */, 313 Tk_Offset(TreeColumn_, border.obj), Tk_Offset(TreeColumn_, border), 314 0, (ClientData) NULL, COLU_CONF_DISPLAY}, 315 {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, 316 (char *) NULL, -1, Tk_Offset(TreeColumn_, bitmap), 317 TK_OPTION_NULL_OK, (ClientData) NULL, 318 COLU_CONF_BITMAP | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 319 {TK_OPTION_PIXELS, "-borderwidth", (char *) NULL, (char *) NULL, 320 "2", Tk_Offset(TreeColumn_, borderWidthObj), Tk_Offset(TreeColumn_, borderWidth), 321 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 322 {TK_OPTION_BOOLEAN, "-button", (char *) NULL, (char *) NULL, 323 "1", -1, Tk_Offset(TreeColumn_, button), 324 0, (ClientData) NULL, 0}, 325 {TK_OPTION_BOOLEAN, "-expand", (char *) NULL, (char *) NULL, 326 "0", -1, Tk_Offset(TreeColumn_, expand), 327 0, (ClientData) NULL, COLU_CONF_TWIDTH}, 328 {TK_OPTION_FONT, "-font", (char *) NULL, (char *) NULL, 329 (char *) NULL, -1, Tk_Offset(TreeColumn_, tkfont), 330 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_NWIDTH | 331 COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY | COLU_CONF_TEXT}, 332 {TK_OPTION_STRING, "-image", (char *) NULL, (char *) NULL, 333 (char *) NULL, -1, Tk_Offset(TreeColumn_, imageString), 334 TK_OPTION_NULL_OK, (ClientData) NULL, 335 COLU_CONF_IMAGE | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 336 {TK_OPTION_CUSTOM, "-imagepadx", (char *) NULL, (char *) NULL, 337 "6", Tk_Offset(TreeColumn_, imagePadXObj), 338 Tk_Offset(TreeColumn_, imagePadX), 0, (ClientData) &TreeCtrlCO_pad, 339 COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, 340 {TK_OPTION_CUSTOM, "-imagepady", (char *) NULL, (char *) NULL, 341 "0", Tk_Offset(TreeColumn_, imagePadYObj), 342 Tk_Offset(TreeColumn_, imagePadY), 0, (ClientData) &TreeCtrlCO_pad, 343 COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 344 {TK_OPTION_STRING, "-itembackground", (char *) NULL, (char *) NULL, 345 (char *) NULL, Tk_Offset(TreeColumn_, itemBgObj), -1, 346 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_ITEMBG}, 347 {TK_OPTION_CUSTOM, "-itemjustify", (char *) NULL, (char *) NULL, 348 (char *) NULL, -1, Tk_Offset(TreeColumn_, itemJustify), 349 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_JUSTIFY}, 350 {TK_OPTION_CUSTOM, "-itemstyle", (char *) NULL, (char *) NULL, 351 (char *) NULL, -1, Tk_Offset(TreeColumn_, itemStyle), 352 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_style, 0}, 353 {TK_OPTION_JUSTIFY, "-justify", (char *) NULL, (char *) NULL, 354 "left", -1, Tk_Offset(TreeColumn_, justify), 355 0, (ClientData) NULL, COLU_CONF_DISPLAY | COLU_CONF_JUSTIFY}, 356 {TK_OPTION_STRING_TABLE, "-lock", (char *) NULL, (char *) NULL, 357 "none", -1, Tk_Offset(TreeColumn_, lock), 0, (ClientData) lockST, 0}, 358 {TK_OPTION_PIXELS, "-maxwidth", (char *) NULL, (char *) NULL, 359 (char *) NULL, Tk_Offset(TreeColumn_, maxWidthObj), 360 Tk_Offset(TreeColumn_, maxWidth), 361 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, 362 {TK_OPTION_PIXELS, "-minwidth", (char *) NULL, (char *) NULL, 363 (char *) NULL, Tk_Offset(TreeColumn_, minWidthObj), 364 Tk_Offset(TreeColumn_, minWidth), 365 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, 366 {TK_OPTION_BOOLEAN, "-resize", (char *) NULL, (char *) NULL, 367 "1", -1, Tk_Offset(TreeColumn_, resize), 0, (ClientData) NULL, 0}, 368 {TK_OPTION_BOOLEAN, "-squeeze", (char *) NULL, (char *) NULL, 369 "0", -1, Tk_Offset(TreeColumn_, squeeze), 370 0, (ClientData) NULL, COLU_CONF_TWIDTH}, 371 {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, 372 "normal", -1, Tk_Offset(TreeColumn_, state), 0, (ClientData) stateST, 373 COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 374#ifdef DEPRECATED 375 {TK_OPTION_PIXELS, "-stepwidth", (char *) NULL, (char *) NULL, 376 (char *) NULL, Tk_Offset(TreeColumn_, stepWidthObj), 377 Tk_Offset(TreeColumn_, stepWidth), 378 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_RANGES}, 379#endif /* DEPRECATED */ 380 {TK_OPTION_CUSTOM, "-tags", (char *) NULL, (char *) NULL, 381 (char *) NULL, -1, Tk_Offset(TreeColumn_, tagInfo), 382 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_tagInfo, COLU_CONF_TAGS}, 383 {TK_OPTION_STRING, "-text", (char *) NULL, (char *) NULL, 384 (char *) NULL, Tk_Offset(TreeColumn_, textObj), Tk_Offset(TreeColumn_, text), 385 TK_OPTION_NULL_OK, (ClientData) NULL, 386 COLU_CONF_TEXT | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 387 {TK_OPTION_COLOR, "-textcolor", (char *) NULL, (char *) NULL, 388 DEF_BUTTON_FG, -1, Tk_Offset(TreeColumn_, textColor), 389 0, (ClientData) NULL, COLU_CONF_DISPLAY}, 390 {TK_OPTION_INT, "-textlines", (char *) NULL, (char *) NULL, 391 "1", -1, Tk_Offset(TreeColumn_, textLines), 392 0, (ClientData) NULL, COLU_CONF_TEXT | COLU_CONF_NWIDTH | 393 COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 394 {TK_OPTION_CUSTOM, "-textpadx", (char *) NULL, (char *) NULL, 395 "6", Tk_Offset(TreeColumn_, textPadXObj), 396 Tk_Offset(TreeColumn_, textPadX), 0, (ClientData) &TreeCtrlCO_pad, 397 COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, 398 {TK_OPTION_CUSTOM, "-textpady", (char *) NULL, (char *) NULL, 399 "0", Tk_Offset(TreeColumn_, textPadYObj), 400 Tk_Offset(TreeColumn_, textPadY), 0, (ClientData) &TreeCtrlCO_pad, 401 COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, 402#ifdef UNIFORM_GROUP 403 {TK_OPTION_CUSTOM, "-uniform", (char *) NULL, (char *) NULL, 404 (char *) NULL, -1, Tk_Offset(TreeColumn_, uniform), TK_OPTION_NULL_OK, 405 (ClientData) &uniformGroupCO, COLU_CONF_TWIDTH}, 406 {TK_OPTION_INT, "-weight", (char *) NULL, (char *) NULL, 407 "1", -1, Tk_Offset(TreeColumn_, weight), 408 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, 409#endif 410 {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, 411 (char *) NULL, Tk_Offset(TreeColumn_, widthObj), Tk_Offset(TreeColumn_, width), 412 TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, 413 {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL, 414 "1", -1, Tk_Offset(TreeColumn_, visible), 415 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_DISPLAY}, 416#ifdef DEPRECATED 417 {TK_OPTION_BOOLEAN, "-widthhack", (char *) NULL, (char *) NULL, 418 "0", -1, Tk_Offset(TreeColumn_, widthHack), 419 0, (ClientData) NULL, COLU_CONF_RANGES}, 420#endif /* DEPRECATED */ 421 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, 422 (char *) NULL, 0, -1, 0, 0, 0} 423}; 424 425#define IS_TAIL(C) ((C) == tree->columnTail) 426#define IS_ALL(C) (((C) == COLUMN_ALL) || ((C) == COLUMN_NTAIL)) 427 428/* 429 *---------------------------------------------------------------------- 430 * 431 * ColumnCO_Set -- 432 * 433 * Tk_ObjCustomOption.setProc(). Converts a Tcl_Obj holding a 434 * column description into a pointer to a Column. 435 * 436 * Results: 437 * A standard Tcl result. 438 * 439 * Side effects: 440 * May store a TreeColumn pointer into the internal representation 441 * pointer. May change the pointer to the Tcl_Obj to NULL to indicate 442 * that the specified string was empty and that is acceptable. 443 * 444 *---------------------------------------------------------------------- 445 */ 446 447static int 448ColumnCO_Set( 449 ClientData clientData, /* CFO_xxx flags to control the conversion. */ 450 Tcl_Interp *interp, /* Current interpreter. */ 451 Tk_Window tkwin, /* Window for which option is being set. */ 452 Tcl_Obj **value, /* Pointer to the pointer to the value object. 453 * We use a pointer to the pointer because 454 * we may need to return a value (NULL). */ 455 char *recordPtr, /* Pointer to storage for the widget record. */ 456 int internalOffset, /* Offset within *recordPtr at which the 457 * internal value is to be stored. */ 458 char *saveInternalPtr, /* Pointer to storage for the old value. */ 459 int flags /* Flags for the option, set Tk_SetOptions. */ 460 ) 461{ 462 int cfoFlags = (int) clientData; 463 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; 464 int objEmpty; 465 TreeColumn new, *internalPtr; 466 467 if (internalOffset >= 0) 468 internalPtr = (TreeColumn *) (recordPtr + internalOffset); 469 else 470 internalPtr = NULL; 471 472 objEmpty = ObjectIsEmpty((*value)); 473 474 if ((flags & TK_OPTION_NULL_OK) && objEmpty) 475 (*value) = NULL; 476 else { 477 if (TreeColumn_FromObj(tree, (*value), &new, cfoFlags) != TCL_OK) 478 return TCL_ERROR; 479 } 480 if (internalPtr != NULL) { 481 if ((*value) == NULL) 482 new = NULL; 483 *((TreeColumn *) saveInternalPtr) = *internalPtr; 484 *internalPtr = new; 485 } 486 487 return TCL_OK; 488} 489 490/* 491 *---------------------------------------------------------------------- 492 * 493 * ColumnCO_Get -- 494 * 495 * Tk_ObjCustomOption.getProc(). Converts a TreeColumn into a 496 * Tcl_Obj string representation. 497 * 498 * Results: 499 * Tcl_Obj containing the string representation of the column. 500 * Returns NULL if the TreeColumn is NULL. 501 * 502 * Side effects: 503 * May create a new Tcl_Obj. 504 * 505 *---------------------------------------------------------------------- 506 */ 507 508static Tcl_Obj * 509ColumnCO_Get( 510 ClientData clientData, /* Not used. */ 511 Tk_Window tkwin, /* Window for which option is being set. */ 512 char *recordPtr, /* Pointer to widget record. */ 513 int internalOffset /* Offset within *recordPtr containing the 514 * sticky value. */ 515 ) 516{ 517 TreeColumn value = *(TreeColumn *) (recordPtr + internalOffset); 518 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData; 519 if (value == NULL) 520 return NULL; 521#if 0 522 if (value == COLUMN_ALL) 523 return Tcl_NewStringObj("all", -1); 524#endif 525 return TreeColumn_ToObj(tree, value); 526} 527 528/* 529 *---------------------------------------------------------------------- 530 * 531 * ColumnCO_Restore -- 532 * 533 * Tk_ObjCustomOption.restoreProc(). Restores a TreeColumn value 534 * from a saved value. 535 * 536 * Results: 537 * None. 538 * 539 * Side effects: 540 * Restores the old value. 541 * 542 *---------------------------------------------------------------------- 543 */ 544 545static void 546ColumnCO_Restore( 547 ClientData clientData, /* Not used. */ 548 Tk_Window tkwin, /* Not used. */ 549 char *internalPtr, /* Where to store old value. */ 550 char *saveInternalPtr) /* Pointer to old value. */ 551{ 552 *(TreeColumn *) internalPtr = *(TreeColumn *) saveInternalPtr; 553} 554 555/* 556 * The following structure contains pointers to functions used for processing 557 * a custom config option that handles Tcl_Obj<->TreeColumn conversion. 558 * A column description must refer to a single column. 559 */ 560Tk_ObjCustomOption TreeCtrlCO_column = 561{ 562 "column", 563 ColumnCO_Set, 564 ColumnCO_Get, 565 ColumnCO_Restore, 566 NULL, 567 (ClientData) (CFO_NOT_NULL) 568}; 569 570/* 571 * The following structure contains pointers to functions used for processing 572 * a custom config option that handles Tcl_Obj<->TreeColumn conversion. 573 * A column description must refer to a single column. 574 * "tail" is not allowed. 575 */ 576Tk_ObjCustomOption TreeCtrlCO_column_NOT_TAIL = 577{ 578 "column", 579 ColumnCO_Set, 580 ColumnCO_Get, 581 ColumnCO_Restore, 582 NULL, 583 (ClientData) (CFO_NOT_NULL | CFO_NOT_TAIL) 584}; 585 586static Tk_OptionSpec dragSpecs[] = { 587 {TK_OPTION_BOOLEAN, "-enable", (char *) NULL, (char *) NULL, 588 "0", -1, Tk_Offset(TreeCtrl, columnDrag.enable), 589 0, (ClientData) NULL, 0}, 590 {TK_OPTION_INT, "-imagealpha", (char *) NULL, (char *) NULL, 591 "128", -1, Tk_Offset(TreeCtrl, columnDrag.alpha), 592 0, (ClientData) NULL, 0}, 593 {TK_OPTION_COLOR, "-imagecolor", (char *) NULL, (char *) NULL, 594 "gray75", -1, Tk_Offset(TreeCtrl, columnDrag.color), 595 0, (ClientData) NULL, 0}, 596 {TK_OPTION_CUSTOM, "-imagecolumn", (char *) NULL, (char *) NULL, 597 (char *) NULL, -1, Tk_Offset(TreeCtrl, columnDrag.column), 598 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_column_NOT_TAIL, 0}, 599 {TK_OPTION_PIXELS, "-imageoffset", (char *) NULL, (char *) NULL, 600 (char *) NULL, Tk_Offset(TreeCtrl, columnDrag.offsetObj), 601 Tk_Offset(TreeCtrl, columnDrag.offset), 0, (ClientData) NULL, 0}, 602 {TK_OPTION_COLOR, "-indicatorcolor", (char *) NULL, (char *) NULL, 603 "Black", -1, Tk_Offset(TreeCtrl, columnDrag.indColor), 604 0, (ClientData) NULL, 0}, 605 {TK_OPTION_CUSTOM, "-indicatorcolumn", (char *) NULL, (char *) NULL, 606 (char *) NULL, -1, Tk_Offset(TreeCtrl, columnDrag.indColumn), 607 TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_column, 0}, 608 {TK_OPTION_STRING_TABLE, "-indicatorside", (char *) NULL, (char *) NULL, 609 "left", -1, Tk_Offset(TreeCtrl, columnDrag.indSide), 610 0, (ClientData) arrowSideST, 0}, 611 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, 612 (char *) NULL, 0, -1, 0, 0, 0} 613}; 614 615/* 616 *---------------------------------------------------------------------- 617 * 618 * ImageChangedProc -- 619 * 620 * This procedure is invoked by the image code whenever the manager 621 * for an image does something that affects the size or contents 622 * of an image displayed in a column header. 623 * 624 * Results: 625 * None. 626 * 627 * Side effects: 628 * Invalidates the size of the column and schedules a redisplay. 629 * 630 *---------------------------------------------------------------------- 631 */ 632 633static void 634ImageChangedProc( 635 ClientData clientData, /* Pointer to Column record. */ 636 int x, int y, /* Upper left pixel (within image) 637 * that must be redisplayed. */ 638 int width, int height, /* Dimensions of area to redisplay 639 * (may be <= 0). */ 640 int imageWidth, int imageHeight /* New dimensions of image. */ 641 ) 642{ 643 /* I would like to know the image was deleted... */ 644 TreeColumn column = clientData; 645 TreeCtrl *tree = column->tree; 646 647 /* Duplicate the effects of configuring the -image option. */ 648 column->neededWidth = -1; 649 column->neededHeight = -1; 650 tree->headerHeight = -1; 651 tree->widthOfColumns = -1; 652 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 653 Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH | DINFO_DRAW_HEADER); 654} 655 656/* 657 *---------------------------------------------------------------------- 658 * 659 * ColumnStateFromObj -- 660 * 661 * Parses a string object containing "state" or "!state" to a 662 * state bit flag. 663 * This function is passed to PerStateInfo_FromObj(). 664 * 665 * Results: 666 * A standard Tcl result. 667 * 668 * Side effects: 669 * None. 670 * 671 *---------------------------------------------------------------------- 672 */ 673 674static int 675ColumnStateFromObj( 676 TreeCtrl *tree, /* Widget info. */ 677 Tcl_Obj *obj, /* String object to parse. */ 678 int *stateOff, /* OR'd with state bit if "!state" is 679 * specified. Caller must initialize. */ 680 int *stateOn /* OR'd with state bit if "state" is 681 * specified. Caller must initialize. */ 682 ) 683{ 684 Tcl_Interp *interp = tree->interp; 685 int i, op = STATE_OP_ON, op2, op3, length, state = 0; 686 char ch0, *string; 687 CONST char *stateNames[4] = { "normal", "active", "pressed", "up" }; 688 int states[3]; 689 690 states[STATE_OP_ON] = 0; 691 states[STATE_OP_OFF] = 0; 692 states[STATE_OP_TOGGLE] = 0; 693 694 string = Tcl_GetStringFromObj(obj, &length); 695 if (length == 0) 696 goto unknown; 697 ch0 = string[0]; 698 if (ch0 == '!') { 699 op = STATE_OP_OFF; 700 ++string; 701 ch0 = string[0]; 702 } else if (ch0 == '~') { 703 if (1) { 704 FormatResult(interp, "can't specify '~' for this command"); 705 return TCL_ERROR; 706 } 707 op = STATE_OP_TOGGLE; 708 ++string; 709 ch0 = string[0]; 710 } 711 for (i = 0; i < 4; i++) { 712 if ((ch0 == stateNames[i][0]) && !strcmp(string, stateNames[i])) { 713 state = 1L << i; 714 break; 715 } 716 } 717 if (state == 0) 718 goto unknown; 719 720 if (op == STATE_OP_ON) { 721 op2 = STATE_OP_OFF; 722 op3 = STATE_OP_TOGGLE; 723 } 724 else if (op == STATE_OP_OFF) { 725 op2 = STATE_OP_ON; 726 op3 = STATE_OP_TOGGLE; 727 } else { 728 op2 = STATE_OP_ON; 729 op3 = STATE_OP_OFF; 730 } 731 states[op2] &= ~state; 732 states[op3] &= ~state; 733 states[op] |= state; 734 735 *stateOn |= states[STATE_OP_ON]; 736 *stateOff |= states[STATE_OP_OFF]; 737 738 return TCL_OK; 739 740unknown: 741 FormatResult(interp, "unknown state \"%s\"", string); 742 return TCL_ERROR; 743} 744 745/* 746 *---------------------------------------------------------------------- 747 * 748 * Column_MakeState -- 749 * 750 * Return a bit mask suitable for passing to the PerState_xxx 751 * functions. 752 * 753 * Results: 754 * State flags for the column's current state. 755 * 756 * Side effects: 757 * None. 758 * 759 *---------------------------------------------------------------------- 760 */ 761 762static int 763Column_MakeState( 764 TreeColumn column /* Column record. */ 765 ) 766{ 767 int state = 0; 768 if (column->state == COLUMN_STATE_NORMAL) 769 state |= 1L << 0; 770 else if (column->state == COLUMN_STATE_ACTIVE) 771 state |= 1L << 1; 772 else if (column->state == COLUMN_STATE_PRESSED) 773 state |= 1L << 2; 774 if (column->arrow == ARROW_UP) 775 state |= 1L << 3; 776 return state; 777} 778 779/* 780 *---------------------------------------------------------------------- 781 * 782 * TreeColumn_FirstAndLast -- 783 * 784 * Determine the order of two columns and swap them if needed. 785 * 786 * Results: 787 * The return value is the number of columns in the range between 788 * first and last. 789 * 790 * Side effects: 791 * None. 792 * 793 *---------------------------------------------------------------------- 794 */ 795 796int 797TreeColumn_FirstAndLast( 798 TreeColumn *first, /* Column token. */ 799 TreeColumn *last /* Column token. */ 800 ) 801{ 802 int indexFirst, indexLast, index; 803 804 indexFirst = TreeColumn_Index(*first); 805 indexLast = TreeColumn_Index(*last); 806 if (indexFirst > indexLast) { 807 TreeColumn column = *first; 808 *first = *last; 809 *last = column; 810 811 index = indexFirst; 812 indexFirst = indexLast; 813 indexLast = index; 814 } 815 return indexLast - indexFirst + 1; 816} 817 818/* 819 *---------------------------------------------------------------------- 820 * 821 * ColumnHasTag -- 822 * 823 * Checks whether a column has a certain tag. 824 * 825 * Results: 826 * Returns TRUE if the column has the given tag. 827 * 828 * Side effects: 829 * None. 830 * 831 *---------------------------------------------------------------------- 832 */ 833 834static int 835ColumnHasTag( 836 TreeColumn column, /* The column to test. */ 837 Tk_Uid tag /* Tag to look for. */ 838 ) 839{ 840 TagInfo *tagInfo = column->tagInfo; 841 Tk_Uid *tagPtr; 842 int count; 843 844 if (tagInfo == NULL) 845 return 0; 846 847 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags; 848 count > 0; tagPtr++, count--) { 849 if (*tagPtr == tag) { 850 return 1; 851 } 852 } 853 return 0; 854} 855 856typedef struct Qualifiers { 857 TreeCtrl *tree; 858 int visible; /* 1 for -visible TRUE, 859 0 for -visible FALSE, 860 -1 for unspecified. */ 861 int states[3]; /* States that must be on or off. */ 862 TagExpr expr; /* Tag expression. */ 863 int exprOK; /* TRUE if expr is valid. */ 864 int lock; /* COLUMN_LOCK_xxx or -1 */ 865 int ntail; /* 1 for !tail, 866 * 0 for unspecified. */ 867 Tk_Uid tag; /* Tag (without operators) or NULL. */ 868} Qualifiers; 869 870/* 871 *---------------------------------------------------------------------- 872 * 873 * Qualifiers_Init -- 874 * 875 * Helper routine for TreeItem_FromObj. 876 * 877 * Results: 878 * None. 879 * 880 * Side effects: 881 * None. 882 * 883 *---------------------------------------------------------------------- 884 */ 885 886static void 887Qualifiers_Init( 888 TreeCtrl *tree, /* Widget info. */ 889 Qualifiers *q /* Out: Initialized qualifiers. */ 890 ) 891{ 892 q->tree = tree; 893 q->visible = -1; 894 q->states[0] = q->states[1] = q->states[2] = 0; 895 q->exprOK = FALSE; 896 q->lock = -1; 897 q->ntail = 0; 898 q->tag = NULL; 899} 900 901/* 902 *---------------------------------------------------------------------- 903 * 904 * Qualifiers_Scan -- 905 * 906 * Helper routine for TreeItem_FromObj. 907 * 908 * Results: 909 * TCL_OK or TCL_ERROR. 910 * 911 * Side effects: 912 * None. 913 * 914 *---------------------------------------------------------------------- 915 */ 916 917static int 918Qualifiers_Scan( 919 Qualifiers *q, /* Must call Qualifiers_Init first, 920 * and Qualifiers_Free if result is TCL_OK. */ 921 int objc, /* Number of arguments. */ 922 Tcl_Obj **objv, /* Argument values. */ 923 int startIndex, /* First objv[] index to look at. */ 924 int *argsUsed /* Out: number of objv[] used. */ 925 ) 926{ 927 TreeCtrl *tree = q->tree; 928 Tcl_Interp *interp = tree->interp; 929 int qual, j = startIndex; 930 931 static CONST char *qualifiers[] = { 932 "lock", "state", "tag", "visible", "!tail", "!visible", NULL 933 }; 934 enum qualEnum { 935 QUAL_LOCK, QUAL_STATE, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_TAIL, 936 QUAL_NOT_VISIBLE 937 }; 938 /* Number of arguments used by qualifiers[]. */ 939 static int qualArgs[] = { 940 2, 2, 2, 1, 1, 1 941 }; 942 943 *argsUsed = 0; 944 945 for (; j < objc; ) { 946 if (Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0, 947 &qual) != TCL_OK) 948 break; 949 if (objc - j < qualArgs[qual]) { 950 Tcl_AppendResult(interp, "missing arguments to \"", 951 Tcl_GetString(objv[j]), "\" qualifier", NULL); 952 goto errorExit; 953 } 954 switch ((enum qualEnum) qual) { 955 case QUAL_LOCK: { 956 if (Tcl_GetIndexFromObj(interp, objv[j + 1], lockST, 957 "lock", 0, &q->lock) != TCL_OK) 958 goto errorExit; 959 break; 960 } 961 case QUAL_STATE: { 962 int i, listObjc; 963 Tcl_Obj **listObjv; 964 965 if (Tcl_ListObjGetElements(interp, objv[j + 1], 966 &listObjc, &listObjv) != TCL_OK) 967 goto errorExit; 968 q->states[STATE_OP_OFF] = q->states[STATE_OP_ON] = 0; 969 for (i = 0; i < listObjc; i++) { 970 if (ColumnStateFromObj(tree, listObjv[i], 971 &q->states[STATE_OP_OFF], 972 &q->states[STATE_OP_ON]) != TCL_OK) 973 goto errorExit; 974 } 975 break; 976 } 977 case QUAL_TAG: { 978 if (tree->columnTagExpr) { 979 if (q->exprOK) 980 TagExpr_Free(&q->expr); 981 if (TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK) 982 return TCL_ERROR; 983 q->exprOK = TRUE; 984 } else { 985 q->tag = Tk_GetUid(Tcl_GetString(objv[j + 1])); 986 } 987 break; 988 } 989 case QUAL_VISIBLE: { 990 q->visible = 1; 991 break; 992 } 993 case QUAL_NOT_TAIL: { 994 q->ntail = 1; 995 break; 996 } 997 case QUAL_NOT_VISIBLE: { 998 q->visible = 0; 999 break; 1000 } 1001 } 1002 *argsUsed += qualArgs[qual]; 1003 j += qualArgs[qual]; 1004 } 1005 return TCL_OK; 1006errorExit: 1007 if (q->exprOK) 1008 TagExpr_Free(&q->expr); 1009 return TCL_ERROR; 1010} 1011 1012/* 1013 *---------------------------------------------------------------------- 1014 * 1015 * Qualifies -- 1016 * 1017 * Helper routine for TreeItem_FromObj. 1018 * 1019 * Results: 1020 * Returns TRUE if the item meets the given criteria. 1021 * 1022 * Side effects: 1023 * None. 1024 * 1025 *---------------------------------------------------------------------- 1026 */ 1027 1028static int 1029Qualifies( 1030 Qualifiers *q, /* Qualifiers to check. */ 1031 TreeColumn column /* The column to test. May be NULL. */ 1032 ) 1033{ 1034 /* Note: if the column is NULL it is a "match" because we have run 1035 * out of columns to check. */ 1036 if (column == NULL) 1037 return 1; 1038 if ((q->ntail == 1) && (column == column->tree->columnTail)) 1039 return 0; 1040 if ((q->visible == 1) && !column->visible) 1041 return 0; 1042 else if ((q->visible == 0) && column->visible) 1043 return 0; 1044 if (q->states[STATE_OP_OFF] & Column_MakeState(column)) 1045 return 0; 1046 if ((q->states[STATE_OP_ON] & Column_MakeState(column)) != q->states[STATE_OP_ON]) 1047 return 0; 1048 if (q->exprOK && !TagExpr_Eval(&q->expr, column->tagInfo)) 1049 return 0; 1050 if ((q->lock != -1) && (column->lock != q->lock)) 1051 return 0; 1052 if ((q->tag != NULL) && !ColumnHasTag(column, q->tag)) 1053 return 0; 1054 return 1; 1055} 1056 1057/* 1058 *---------------------------------------------------------------------- 1059 * 1060 * Qualifiers_Free -- 1061 * 1062 * Helper routine for TreeItem_FromObj. 1063 * 1064 * Results: 1065 * None. 1066 * 1067 * Side effects: 1068 * None. 1069 * 1070 *---------------------------------------------------------------------- 1071 */ 1072 1073static void 1074Qualifiers_Free( 1075 Qualifiers *q /* Out: Initialized qualifiers. */ 1076 ) 1077{ 1078 if (q->exprOK) 1079 TagExpr_Free(&q->expr); 1080} 1081 1082/* 1083 *---------------------------------------------------------------------- 1084 * 1085 * TreeColumnList_FromObj -- 1086 * 1087 * Parse a Tcl_Obj column description to get a list of columns. 1088 * 1089 * -- returning a single column -- 1090 * ID MODIFIERS 1091 * first QUALIFIERS MODIFIERS 1092 * end|last QUALIFIERS MODIFIERS 1093 * order N QUALIFIERS MODIFIERS 1094 * tail 1095 * tree 1096 * -- returning multiple columns -- 1097 * all QUALIFIERS 1098 * QUALIFIERS (like "all QUALIFIERS") 1099 * list listOfDescs 1100 * range first last QUALIFIERS 1101 * tag tagExpr QUALIFIERS 1102 * TAG-EXPR QUALIFIERS MODIFIERS 1103 * 1104 * MODIFIERS: 1105 * -- returning a single column -- 1106 * next QUALIFIERS 1107 * prev QUALIFIERS 1108 * 1109 * QUALIFIERS: 1110 * state stateList 1111 * tag tagExpr 1112 * visible 1113 * !visible 1114 * !tail 1115 * 1116 * Results: 1117 * A standard Tcl result. 1118 * 1119 * Side effects: 1120 * None. 1121 * 1122 *---------------------------------------------------------------------- 1123 */ 1124 1125int 1126TreeColumnList_FromObj( 1127 TreeCtrl *tree, /* Widget info. */ 1128 Tcl_Obj *objPtr, /* Column description. */ 1129 TreeColumnList *columns, /* Uninitialized list. Caller must free 1130 * it with TreeColumnList_Free unless the 1131 * result of this function is TCL_ERROR. */ 1132 int flags /* CFO_xxx flags. */ 1133 ) 1134{ 1135 Tcl_Interp *interp = tree->interp; 1136 int i, objc, index, listIndex; 1137 Tcl_Obj **objv, *elemPtr; 1138 TreeColumn column = NULL; 1139 Qualifiers q; 1140 int qualArgsTotal; 1141 1142 static CONST char *indexName[] = { 1143 "all", "end", "first", "last", "list", "order", "range", "tail", 1144 "tree", (char *) NULL 1145 }; 1146 enum indexEnum { 1147 INDEX_ALL, INDEX_END, INDEX_FIRST, INDEX_LAST, INDEX_LIST, INDEX_ORDER, 1148 INDEX_RANGE, INDEX_TAIL, INDEX_TREE 1149 } ; 1150 /* Number of arguments used by indexName[]. */ 1151 static int indexArgs[] = { 1152 1, 1, 1, 1, 2, 2, 3, 1, 1 1153 }; 1154 /* Boolean: can indexName[] be followed by 1 or more qualifiers. */ 1155 static int indexQual[] = { 1156 1, 0, 1, 1, 0, 1, 1, 0, 0 1157 }; 1158 1159 static CONST char *modifiers[] = { 1160 "next", "prev", (char *) NULL 1161 }; 1162 enum modEnum { 1163 TMOD_NEXT, TMOD_PREV 1164 }; 1165 /* Number of arguments used by modifiers[]. */ 1166 static int modArgs[] = { 1167 1, 1 1168 }; 1169 /* Boolean: can modifiers[] be followed by 1 or more qualifiers. */ 1170 static int modQual[] = { 1171 1, 1 1172 }; 1173 1174 TreeColumnList_Init(tree, columns, 0); 1175 Qualifiers_Init(tree, &q); 1176 1177 if (Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK) 1178 goto badDesc; 1179 if (objc == 0) 1180 goto badDesc; 1181 1182 listIndex = 0; 1183 elemPtr = objv[listIndex]; 1184 if (Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index) 1185 == TCL_OK) { 1186 1187 if (objc - listIndex < indexArgs[index]) { 1188 Tcl_AppendResult(interp, "missing arguments to \"", 1189 Tcl_GetString(elemPtr), "\" keyword", NULL); 1190 goto errorExit; 1191 } 1192 1193 qualArgsTotal = 0; 1194 if (indexQual[index]) { 1195 if (Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index], 1196 &qualArgsTotal) != TCL_OK) { 1197 goto errorExit; 1198 } 1199 } 1200 1201 switch ((enum indexEnum) index) { 1202 case INDEX_ALL: { 1203 if (qualArgsTotal) { 1204 column = tree->columns; 1205 while (column != NULL) { 1206 if (Qualifies(&q, column)) { 1207 TreeColumnList_Append(columns, column); 1208 } 1209 column = column->next; 1210 } 1211 if (!(flags & CFO_NOT_TAIL) && 1212 Qualifies(&q, tree->columnTail)) { 1213 TreeColumnList_Append(columns, tree->columnTail); 1214 } 1215 column = NULL; 1216 } else if (flags & CFO_LIST_ALL) { 1217 column = tree->columns; 1218 while (column != NULL) { 1219 TreeColumnList_Append(columns, column); 1220 column = column->next; 1221 } 1222 if (!(flags & CFO_NOT_TAIL)) 1223 TreeColumnList_Append(columns, tree->columnTail); 1224 column = NULL; 1225 } else if (flags & CFO_NOT_TAIL) { 1226 column = COLUMN_NTAIL; 1227 } else { 1228 column = COLUMN_ALL; 1229 } 1230 break; 1231 } 1232 case INDEX_FIRST: { 1233 column = tree->columns; 1234 while (!Qualifies(&q, column)) 1235 column = column->next; 1236 break; 1237 } 1238 case INDEX_END: 1239 case INDEX_LAST: { 1240 column = tree->columnLast; 1241 while (!Qualifies(&q, column)) { 1242 column = column->prev; 1243 } 1244 break; 1245 } 1246 case INDEX_LIST: { 1247 int listObjc; 1248 Tcl_Obj **listObjv; 1249 int count; 1250 1251 if (Tcl_ListObjGetElements(interp, objv[listIndex + 1], 1252 &listObjc, &listObjv) != TCL_OK) 1253 goto errorExit; 1254 for (i = 0; i < listObjc; i++) { 1255 TreeColumnList column2s; 1256 if (TreeColumnList_FromObj(tree, listObjv[i], &column2s, 1257 flags) != TCL_OK) 1258 goto errorExit; 1259 TreeColumnList_Concat(columns, &column2s); 1260 TreeColumnList_Free(&column2s); 1261 } 1262 /* If any of the column descriptions in the list is "all", then 1263 * clear the list of columns and use "all". */ 1264 count = TreeColumnList_Count(columns); 1265 for (i = 0; i < count; i++) { 1266 TreeColumn column = TreeColumnList_Nth(columns, i); 1267 if (IS_ALL(column)) 1268 break; 1269 } 1270 if (i < count) { 1271 TreeColumnList_Free(columns); 1272 if (flags & CFO_NOT_TAIL) 1273 column = COLUMN_NTAIL; 1274 else 1275 column = COLUMN_ALL; 1276 } else 1277 column = NULL; 1278 break; 1279 } 1280 case INDEX_ORDER: { 1281 int order; 1282 1283 if (Tcl_GetIntFromObj(NULL, objv[listIndex + 1], &order) != TCL_OK) 1284 goto errorExit; 1285 column = tree->columns; 1286 while (column != NULL) { 1287 if (Qualifies(&q, column)) 1288 if (order-- <= 0) 1289 break; 1290 column = column->next; 1291 } 1292 break; 1293 } 1294 case INDEX_RANGE: { 1295 TreeColumn _first, _last; 1296 1297 if (TreeColumn_FromObj(tree, objv[listIndex + 1], 1298 &_first, CFO_NOT_NULL) != TCL_OK) 1299 goto errorExit; 1300 if (TreeColumn_FromObj(tree, objv[listIndex + 2], 1301 &_last, CFO_NOT_NULL) != TCL_OK) 1302 goto errorExit; 1303 (void) TreeColumn_FirstAndLast(&_first, &_last); 1304 column = _first; 1305 while (1) { 1306 if (Qualifies(&q, column)) { 1307 TreeColumnList_Append(columns, column); 1308 } 1309 if (column == _last) 1310 break; 1311 column = column->next; 1312 if (column == NULL) 1313 column = tree->columnTail; 1314 } 1315 column = NULL; 1316 break; 1317 } 1318 case INDEX_TAIL: { 1319 column = tree->columnTail; 1320 break; 1321 } 1322 case INDEX_TREE: { 1323 column = tree->columnTree; 1324 break; 1325 } 1326 } 1327 listIndex += indexArgs[index] + qualArgsTotal; 1328 1329 /* No indexName[] was found. */ 1330 } else { 1331 int gotId = FALSE, id; 1332 TagExpr expr; 1333 1334 if (tree->columnPrefixLen) { 1335 char *end, *t = Tcl_GetString(elemPtr); 1336 if (strncmp(t, tree->columnPrefix, tree->columnPrefixLen) == 0) { 1337 t += tree->columnPrefixLen; 1338 id = strtoul(t, &end, 10); 1339 if ((end != t) && (*end == '\0')) 1340 gotId = TRUE; 1341 } 1342 1343 } else if (Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) { 1344 gotId = TRUE; 1345 } 1346 if (gotId) { 1347 column = tree->columns; 1348 while (column) { 1349 if (column->id == id) 1350 break; 1351 column = column->next; 1352 } 1353 listIndex++; 1354 goto gotFirstPart; 1355 } 1356 1357 /* Try a list of qualifiers. This has the same effect as 1358 * "all QUALIFIERS". */ 1359 if (Qualifiers_Scan(&q, objc, objv, listIndex, &qualArgsTotal) 1360 != TCL_OK) { 1361 goto errorExit; 1362 } 1363 if (qualArgsTotal) { 1364 column = tree->columns; 1365 while (column != NULL) { 1366 if (Qualifies(&q, column)) { 1367 TreeColumnList_Append(columns, column); 1368 } 1369 column = column->next; 1370 } 1371 if (!(flags & CFO_NOT_TAIL) && 1372 Qualifies(&q, tree->columnTail)) { 1373 TreeColumnList_Append(columns, tree->columnTail); 1374 } 1375 column = NULL; 1376 listIndex += qualArgsTotal; 1377 goto gotFirstPart; 1378 } 1379 1380 /* Try a tag or tag expression followed by qualifiers. */ 1381 if (objc > 1) { 1382 if (Qualifiers_Scan(&q, objc, objv, listIndex + 1, 1383 &qualArgsTotal) != TCL_OK) { 1384 goto errorExit; 1385 } 1386 } 1387 if (tree->columnTagExpr) { 1388 if (TagExpr_Init(tree, elemPtr, &expr) != TCL_OK) 1389 goto errorExit; 1390 column = tree->columns; 1391 while (column != NULL) { 1392 if (TagExpr_Eval(&expr, column->tagInfo) && Qualifies(&q, column)) { 1393 TreeColumnList_Append(columns, column); 1394 } 1395 column = column->next; 1396 } 1397 if (!(flags & CFO_NOT_TAIL) && 1398 TagExpr_Eval(&expr, tree->columnTail->tagInfo) && 1399 Qualifies(&q, tree->columnTail)) { 1400 TreeColumnList_Append(columns, tree->columnTail); 1401 } 1402 TagExpr_Free(&expr); 1403 } else { 1404 Tk_Uid tag = Tk_GetUid(Tcl_GetString(elemPtr)); 1405 column = tree->columns; 1406 while (column != NULL) { 1407 if (ColumnHasTag(column, tag) && Qualifies(&q, column)) { 1408 TreeColumnList_Append(columns, column); 1409 } 1410 column = column->next; 1411 } 1412 if (!(flags & CFO_NOT_TAIL) && 1413 ColumnHasTag(tree->columnTail, tag) && 1414 Qualifies(&q, tree->columnTail)) { 1415 TreeColumnList_Append(columns, tree->columnTail); 1416 } 1417 } 1418 column = NULL; 1419 listIndex += 1 + qualArgsTotal; 1420 } 1421 1422gotFirstPart: 1423 1424 /* If 1 column, use it and clear the list. */ 1425 if (TreeColumnList_Count(columns) == 1) { 1426 column = TreeColumnList_Nth(columns, 0); 1427 columns->count = 0; 1428 } 1429 1430 /* If "all" but only tail column exists, use it. */ 1431 if (IS_ALL(column) && (tree->columns == NULL) && !(flags & CFO_NOT_TAIL)) 1432 column = tree->columnTail; 1433 1434 /* If > 1 column, no modifiers may follow. */ 1435 if ((TreeColumnList_Count(columns) > 1) || IS_ALL(column)) { 1436 if (listIndex < objc) { 1437 Tcl_AppendResult(interp, "unexpected arguments after \"", 1438 (char *) NULL); 1439 for (i = 0; i < listIndex; i++) { 1440 Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (char *) NULL); 1441 if (i != listIndex - 1) 1442 Tcl_AppendResult(interp, " ", (char *) NULL); 1443 } 1444 Tcl_AppendResult(interp, "\"", (char *) NULL); 1445 goto errorExit; 1446 } 1447 } 1448 1449 /* This means a valid specification was given, but there is no such column */ 1450 if ((TreeColumnList_Count(columns) == 0) && (column == NULL)) { 1451 if (flags & CFO_NOT_NULL) 1452 goto notNull; 1453 /* Empty list returned */ 1454 goto goodExit; 1455 } 1456 1457 /* Process any modifiers following the column we matched above. */ 1458 for (; listIndex < objc; /* nothing */) { 1459 int qualArgsTotal = 0; 1460 1461 elemPtr = objv[listIndex]; 1462 if (Tcl_GetIndexFromObj(interp, elemPtr, modifiers, "modifier", 0, 1463 &index) != TCL_OK) { 1464 goto errorExit; 1465 } 1466 if (objc - listIndex < modArgs[index]) { 1467 Tcl_AppendResult(interp, "missing arguments to \"", 1468 Tcl_GetString(elemPtr), "\" modifier", NULL); 1469 goto errorExit; 1470 } 1471 if (modQual[index]) { 1472 Qualifiers_Free(&q); 1473 Qualifiers_Init(tree, &q); 1474 if (Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index], 1475 &qualArgsTotal) != TCL_OK) { 1476 goto errorExit; 1477 } 1478 } 1479 switch ((enum modEnum) index) { 1480 case TMOD_NEXT: { 1481 int isTail = IS_TAIL(column); 1482 if (isTail) { 1483 column = NULL; 1484 break; 1485 } 1486 column = column->next; 1487 while (!Qualifies(&q, column)) 1488 column = column->next; 1489 if (column == NULL) { 1490 column = tree->columnTail; 1491 if (!Qualifies(&q, column)) 1492 column = NULL; 1493 } 1494 break; 1495 } 1496 case TMOD_PREV: { 1497 int isTail = IS_TAIL(column); 1498 if (isTail) 1499 column = tree->columnLast; 1500 else 1501 column = column->prev; 1502 while (!Qualifies(&q, column)) 1503 column = column->prev; 1504 break; 1505 } 1506 } 1507 if ((TreeColumnList_Count(columns) == 0) && (column == NULL)) { 1508 if (flags & CFO_NOT_NULL) 1509 goto notNull; 1510 /* Empty list returned. */ 1511 goto goodExit; 1512 } 1513 listIndex += modArgs[index] + qualArgsTotal; 1514 } 1515 if ((flags & CFO_NOT_MANY) && (IS_ALL(column) || 1516 (TreeColumnList_Count(columns) > 1))) { 1517 FormatResult(interp, "can't specify > 1 column for this command"); 1518 goto errorExit; 1519 } 1520 if ((flags & CFO_NOT_NULL) && (TreeColumnList_Count(columns) == 0) && 1521 (column == NULL)) { 1522notNull: 1523 FormatResult(interp, "column \"%s\" doesn't exist", Tcl_GetString(objPtr)); 1524 goto errorExit; 1525 } 1526 if (TreeColumnList_Count(columns)) { 1527 if (flags & (CFO_NOT_TAIL)) { 1528 int i; 1529 for (i = 0; i < TreeColumnList_Count(columns); i++) { 1530 column = TreeColumnList_Nth(columns, i); 1531 if ((flags & CFO_NOT_TAIL) && IS_TAIL(column)) 1532 goto notTail; 1533 } 1534 } 1535 } else if (IS_ALL(column)) { 1536 TreeColumnList_Append(columns, column); 1537 } else { 1538 if ((flags & CFO_NOT_TAIL) && IS_TAIL(column)) { 1539notTail: 1540 FormatResult(interp, "can't specify \"tail\" for this command"); 1541 goto errorExit; 1542 } 1543 TreeColumnList_Append(columns, column); 1544 } 1545goodExit: 1546 Qualifiers_Free(&q); 1547 return TCL_OK; 1548 1549badDesc: 1550 FormatResult(interp, "bad column description \"%s\"", Tcl_GetString(objPtr)); 1551 goto errorExit; 1552 1553errorExit: 1554 Qualifiers_Free(&q); 1555 TreeColumnList_Free(columns); 1556 return TCL_ERROR; 1557} 1558 1559/* 1560 *---------------------------------------------------------------------- 1561 * 1562 * TreeColumn_FromObj -- 1563 * 1564 * Parse a Tcl_Obj column description to get a single column. 1565 * 1566 * Results: 1567 * TCL_OK or TCL_ERROR. 1568 * 1569 * Side effects: 1570 * None. 1571 * 1572 *---------------------------------------------------------------------- 1573 */ 1574 1575int 1576TreeColumn_FromObj( 1577 TreeCtrl *tree, /* Widget info. */ 1578 Tcl_Obj *objPtr, /* Object to parse to a column. */ 1579 TreeColumn *columnPtr, /* Returned column. */ 1580 int flags /* CFO_xxx flags */ 1581 ) 1582{ 1583 TreeColumnList columns; 1584 1585 if (TreeColumnList_FromObj(tree, objPtr, &columns, flags | CFO_NOT_MANY) != TCL_OK) 1586 return TCL_ERROR; 1587 /* May be NULL. */ 1588 (*columnPtr) = TreeColumnList_Nth(&columns, 0); 1589 TreeColumnList_Free(&columns); 1590 return TCL_OK; 1591} 1592 1593/* 1594 *---------------------------------------------------------------------- 1595 * 1596 * TreeColumnForEach_Start -- 1597 * 1598 * Begin iterating over items. A command might accept two column 1599 * descriptions for a range of column, or a single column description 1600 * which may itself refer to multiple column. Either column 1601 * description could be "all". 1602 * 1603 * Results: 1604 * Returns the first column to iterate over. If an error occurs 1605 * then ColumnForEach.error is set to 1. 1606 * 1607 * Side effects: 1608 * None. 1609 * 1610 *---------------------------------------------------------------------- 1611 */ 1612 1613TreeColumn 1614TreeColumnForEach_Start( 1615 TreeColumnList *columns, /* List of columns. */ 1616 TreeColumnList *column2s, /* List of columns or NULL. */ 1617 ColumnForEach *iter /* Returned info, pass to 1618 TreeColumnForEach_Next. */ 1619 ) 1620{ 1621 TreeCtrl *tree = columns->tree; 1622 TreeColumn column, column2 = NULL; 1623 1624 column = TreeColumnList_Nth(columns, 0); 1625 if (column2s) 1626 column2 = TreeColumnList_Nth(column2s, 0); 1627 1628 iter->tree = tree; 1629 iter->all = FALSE; 1630 iter->ntail = FALSE; 1631 iter->error = 0; 1632 iter->list = NULL; 1633 1634 if (IS_ALL(column) || IS_ALL(column2)) { 1635 iter->all = TRUE; 1636 iter->ntail = (column == COLUMN_NTAIL) || (column2 == COLUMN_NTAIL); 1637 if (tree->columns == NULL) 1638 return iter->current = iter->ntail ? NULL : tree->columnTail; 1639 iter->next = TreeColumn_Next(tree->columns); 1640 return iter->current = tree->columns; 1641 } 1642 1643 if (column2 != NULL) { 1644 if (TreeColumn_FirstAndLast(&column, &column2) == 0) { 1645 iter->error = 1; 1646 return NULL; 1647 } 1648 iter->next = TreeColumn_Next(column); 1649 iter->last = column2; 1650 return iter->current = column; 1651 } 1652 1653 iter->list = columns; 1654 iter->index = 0; 1655 return iter->current = column; 1656} 1657 1658/* 1659 *---------------------------------------------------------------------- 1660 * 1661 * TreeColumnForEach_Next -- 1662 * 1663 * Returns the next column to iterate over. Keep calling this until 1664 * the result is NULL. 1665 * 1666 * Results: 1667 * Returns the next column to iterate over or NULL. 1668 * 1669 * Side effects: 1670 * None. 1671 * 1672 *---------------------------------------------------------------------- 1673 */ 1674 1675TreeColumn 1676TreeColumnForEach_Next( 1677 ColumnForEach *iter /* Initialized by TreeColumnForEach_Start. */ 1678 ) 1679{ 1680 TreeCtrl *tree = iter->tree; 1681 TreeColumn column; 1682 1683 if (iter->all) { 1684 if (iter->current == tree->columnTail) 1685 return iter->current = NULL; 1686 column = iter->next; 1687 if (column == NULL) 1688 return iter->current = iter->ntail ? NULL : tree->columnTail; 1689 iter->next = TreeColumn_Next(column); 1690 return iter->current = column; 1691 } 1692 1693 if (iter->list != NULL) { 1694 if (iter->index >= TreeColumnList_Count(iter->list)) 1695 return iter->current = NULL; 1696 return iter->current = TreeColumnList_Nth(iter->list, ++iter->index); 1697 } 1698 1699 if (iter->current == iter->last) 1700 return iter->current = NULL; 1701 column = iter->next; 1702 iter->next = TreeColumn_Next(column); 1703 return iter->current = column; 1704} 1705 1706/* 1707 *---------------------------------------------------------------------- 1708 * 1709 * TreeColumn_ToObj -- 1710 * 1711 * Return a Tcl_Obj representing a column. 1712 * 1713 * Results: 1714 * A Tcl_Obj. 1715 * 1716 * Side effects: 1717 * Allocates a Tcl_Obj. 1718 * 1719 *---------------------------------------------------------------------- 1720 */ 1721 1722Tcl_Obj * 1723TreeColumn_ToObj( 1724 TreeCtrl *tree, /* Widget info. */ 1725 TreeColumn column /* Column token to get Tcl_Obj for. */ 1726 ) 1727{ 1728 if (column == tree->columnTail) 1729 return Tcl_NewStringObj("tail", -1); 1730 if (tree->columnPrefixLen) { 1731 char buf[100 + TCL_INTEGER_SPACE]; 1732 (void) sprintf(buf, "%s%d", tree->columnPrefix, column->id); 1733 return Tcl_NewStringObj(buf, -1); 1734 } 1735 return Tcl_NewIntObj(column->id); 1736} 1737 1738/* 1739 *---------------------------------------------------------------------- 1740 * 1741 * Tree_FindColumn -- 1742 * 1743 * Get the N'th column in a TreeCtrl. 1744 * 1745 * Results: 1746 * Token for the N'th column. 1747 * 1748 * Side effects: 1749 * None. 1750 * 1751 *---------------------------------------------------------------------- 1752 */ 1753 1754TreeColumn 1755Tree_FindColumn( 1756 TreeCtrl *tree, /* Widget info. */ 1757 int columnIndex /* 0-based index of the column to return. */ 1758 ) 1759{ 1760 TreeColumn column = tree->columns; 1761 1762 while (column != NULL) { 1763 if (column->index == columnIndex) 1764 break; 1765 column = column->next; 1766 } 1767 return column; 1768} 1769 1770/* 1771 *---------------------------------------------------------------------- 1772 * 1773 * TreeColumn_Next -- 1774 * 1775 * Return the column to the right of the given one. 1776 * 1777 * Results: 1778 * Token for the next column. 1779 * 1780 * Side effects: 1781 * None. 1782 * 1783 *---------------------------------------------------------------------- 1784 */ 1785 1786TreeColumn 1787TreeColumn_Next( 1788 TreeColumn column /* Column token. */ 1789 ) 1790{ 1791 return column->next; 1792} 1793 1794/* 1795 *---------------------------------------------------------------------- 1796 * 1797 * TreeColumn_Prev -- 1798 * 1799 * Return the column to the left of the given one. 1800 * 1801 * Results: 1802 * Token for the previous column. 1803 * 1804 * Side effects: 1805 * None. 1806 * 1807 *---------------------------------------------------------------------- 1808 */ 1809 1810TreeColumn 1811TreeColumn_Prev( 1812 TreeColumn column /* Column token. */ 1813 ) 1814{ 1815 return column->prev; 1816} 1817 1818/* 1819 *---------------------------------------------------------------------- 1820 * 1821 * Column_FreeColors -- 1822 * 1823 * Frees an array of XColors. This is used to free the -itembackground 1824 * array of colors. 1825 * 1826 * Results: 1827 * None. 1828 * 1829 * Side effects: 1830 * Memory is deallocated, colors are freed. 1831 * 1832 *---------------------------------------------------------------------- 1833 */ 1834 1835static void 1836Column_FreeColors( 1837 XColor **colors, /* Array of colors. May be NULL. */ 1838 int count /* Number of colors. */ 1839 ) 1840{ 1841 int i; 1842 1843 if (colors == NULL) { 1844 return; 1845 } 1846 for (i = 0; i < count; i++) { 1847 if (colors[i] != NULL) { 1848 Tk_FreeColor(colors[i]); 1849 } 1850 } 1851 WCFREE(colors, XColor *, count); 1852} 1853 1854/* 1855 *---------------------------------------------------------------------- 1856 * 1857 * Column_Move -- 1858 * 1859 * Move a column before another. 1860 * 1861 * Results: 1862 * If the column is moved, then the list of item-columns for every item 1863 * is rearranged and the treectrl option -defaultstyles is rearranged. 1864 * Whether the column is moved or not, the .index field of every 1865 * column is recalculated. 1866 * 1867 * Side effects: 1868 * A redisplay is scheduled if the moved column is visible. 1869 * 1870 *---------------------------------------------------------------------- 1871 */ 1872 1873static void 1874Column_Move( 1875 TreeColumn move, /* Column to move. */ 1876 TreeColumn before /* Column to place 'move' in front of. 1877 * May be the same as 'move'. */ 1878 ) 1879{ 1880 TreeCtrl *tree = move->tree; 1881 TreeColumn column, prev, next, last; 1882 Tcl_HashEntry *hPtr; 1883 Tcl_HashSearch search; 1884 TreeItem item; 1885 int index; 1886#ifdef DEPRECATED 1887 int numStyles; 1888#endif 1889 1890 if (move == before) 1891 goto renumber; 1892 if (move->index == before->index - 1) 1893 goto renumber; 1894 1895 /* Move the column in every item */ 1896 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 1897 while (hPtr != NULL) { 1898 item = (TreeItem) Tcl_GetHashValue(hPtr); 1899 TreeItem_MoveColumn(tree, item, move->index, before->index); 1900 hPtr = Tcl_NextHashEntry(&search); 1901 } 1902 1903 /* Indicate that all items must recalculate their list of spans. */ 1904 TreeItem_SpansInvalidate(tree, NULL); 1905 1906#ifdef DEPRECATED 1907 /* Re-order -defaultstyle */ 1908 numStyles = tree->defaultStyle.numStyles; 1909 if ((numStyles > 0) && ((before->index < numStyles) || 1910 (move->index < numStyles))) { 1911 TreeStyle style, *styles; 1912 int i, j; 1913 Tcl_Obj *staticObjv[STATIC_SIZE], **objv = staticObjv; 1914 1915 /* Case 1: move existing */ 1916 if ((before->index <= numStyles) && (move->index < numStyles)) { 1917 styles = tree->defaultStyle.styles; 1918 style = styles[move->index]; 1919 for (i = move->index; i < numStyles - 1; i++) 1920 styles[i] = styles[i + 1]; 1921 j = before->index; 1922 if (move->index < before->index) 1923 j--; 1924 for (i = numStyles - 1; i > j; i--) 1925 styles[i] = styles[i - 1]; 1926 styles[j] = style; 1927 1928 /* Case 2: insert empty between existing */ 1929 } else if (before->index < numStyles) { 1930 numStyles++; 1931 styles = (TreeStyle *) ckalloc(numStyles * sizeof(TreeStyle)); 1932 for (i = 0; i < before->index; i++) 1933 styles[i] = tree->defaultStyle.styles[i]; 1934 styles[i++] = NULL; 1935 for (; i < numStyles; i++) 1936 styles[i] = tree->defaultStyle.styles[i - 1]; 1937 1938 /* Case 3: move existing past end */ 1939 } else { 1940 numStyles += before->index - numStyles; 1941 styles = (TreeStyle *) ckalloc(numStyles * sizeof(TreeStyle)); 1942 style = tree->defaultStyle.styles[move->index]; 1943 for (i = 0; i < move->index; i++) 1944 styles[i] = tree->defaultStyle.styles[i]; 1945 for (; i < tree->defaultStyle.numStyles - 1; i++) 1946 styles[i] = tree->defaultStyle.styles[i + 1]; 1947 for (; i < numStyles - 1; i++) 1948 styles[i] = NULL; 1949 styles[i] = style; 1950 } 1951 Tcl_DecrRefCount(tree->defaultStyle.stylesObj); 1952 STATIC_ALLOC(objv, Tcl_Obj *, numStyles); 1953 for (i = 0; i < numStyles; i++) { 1954 if (styles[i] != NULL) 1955 objv[i] = TreeStyle_ToObj(styles[i]); 1956 else 1957 objv[i] = Tcl_NewObj(); 1958 } 1959 tree->defaultStyle.stylesObj = Tcl_NewListObj(numStyles, objv); 1960 Tcl_IncrRefCount(tree->defaultStyle.stylesObj); 1961 STATIC_FREE(objv, Tcl_Obj *, numStyles); 1962 if (styles != tree->defaultStyle.styles) { 1963 ckfree((char *) tree->defaultStyle.styles); 1964 tree->defaultStyle.styles = styles; 1965 tree->defaultStyle.numStyles = numStyles; 1966 } 1967 } 1968#endif /* DEPRECATED */ 1969 1970 /* Unlink. */ 1971 prev = move->prev; 1972 next = move->next; 1973 if (prev == NULL) 1974 tree->columns = next; 1975 else 1976 prev->next = next; 1977 if (next == NULL) 1978 tree->columnLast = prev; 1979 else 1980 next->prev = prev; 1981 1982 /* Link. */ 1983 if (before == tree->columnTail) { 1984 last = tree->columnLast; 1985 last->next = move; 1986 move->prev = last; 1987 move->next = NULL; 1988 tree->columnLast = move; 1989 } else { 1990 prev = before->prev; 1991 if (prev == NULL) 1992 tree->columns = move; 1993 else 1994 prev->next = move; 1995 before->prev = move; 1996 move->prev = prev; 1997 move->next = before; 1998 } 1999 2000 /* Renumber columns */ 2001renumber: 2002 tree->columnLockLeft = NULL; 2003 tree->columnLockNone = NULL; 2004 tree->columnLockRight = NULL; 2005 2006 index = 0; 2007 column = tree->columns; 2008 while (column != NULL) { 2009 column->index = index++; 2010 if (column->lock == COLUMN_LOCK_LEFT && tree->columnLockLeft == NULL) 2011 tree->columnLockLeft = column; 2012 if (column->lock == COLUMN_LOCK_NONE && tree->columnLockNone == NULL) 2013 tree->columnLockNone = column; 2014 if (column->lock == COLUMN_LOCK_RIGHT && tree->columnLockRight == NULL) 2015 tree->columnLockRight = column; 2016 column = column->next; 2017 } 2018 2019 if (move->visible) { 2020 /* Must update column widths because of expansion. */ 2021 /* Also update columnTreeLeft. */ 2022 tree->widthOfColumns = -1; 2023 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 2024 Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH); 2025 } 2026} 2027 2028/* 2029 *---------------------------------------------------------------------- 2030 * 2031 * Column_Config -- 2032 * 2033 * This procedure is called to process an objc/objv list to set 2034 * configuration options for a Column. 2035 * 2036 * Results: 2037 * The return value is a standard Tcl result. If TCL_ERROR is 2038 * returned, then an error message is left in interp's result. 2039 * 2040 * Side effects: 2041 * Configuration information, such as text string, colors, font, 2042 * etc. get set for column; old resources get freed, if there 2043 * were any. Display changes may occur. 2044 * 2045 *---------------------------------------------------------------------- 2046 */ 2047 2048static int 2049Column_Config( 2050 TreeColumn column, /* Column record. */ 2051 int objc, /* Number of arguments. */ 2052 Tcl_Obj *CONST objv[], /* Argument values. */ 2053 int createFlag /* TRUE if the Column is being created. */ 2054 ) 2055{ 2056 TreeCtrl *tree = column->tree; 2057 TreeColumn_ saved; 2058 TreeColumn walk; 2059 Tk_SavedOptions savedOptions; 2060 int error; 2061 Tcl_Obj *errorResult = NULL; 2062 int mask, maskFree = 0; 2063 XGCValues gcValues; 2064 unsigned long gcMask; 2065/* int stateOld = Column_MakeState(column), stateNew;*/ 2066 int visible = column->visible; 2067 int lock = column->lock; 2068 2069 /* Init these to prevent compiler warnings */ 2070 saved.image = NULL; 2071 saved.itemBgCount = 0; 2072 saved.itemBgColor = NULL; 2073 2074 for (error = 0; error <= 1; error++) { 2075 if (error == 0) { 2076 if (Tk_SetOptions(tree->interp, (char *) column, 2077 column->optionTable, objc, objv, tree->tkwin, 2078 &savedOptions, &mask) != TCL_OK) { 2079 mask = 0; 2080 continue; 2081 } 2082 2083 /* Wouldn't have to do this if Tk_InitOptions() would return 2084 * a mask of configured options like Tk_SetOptions() does. */ 2085 if (createFlag) { 2086 if (column->imageString != NULL) 2087 mask |= COLU_CONF_IMAGE; 2088 if (column->itemBgObj != NULL) 2089 mask |= COLU_CONF_ITEMBG; 2090 } 2091 2092 /* 2093 * Step 1: Save old values 2094 */ 2095 2096 if (mask & COLU_CONF_IMAGE) 2097 saved.image = column->image; 2098 if (mask & COLU_CONF_ITEMBG) { 2099 saved.itemBgColor = column->itemBgColor; 2100 saved.itemBgCount = column->itemBgCount; 2101 } 2102 2103 if (column == tree->columnTail) { 2104 if (column->itemStyle != NULL) { 2105 FormatResult(tree->interp, 2106 "can't change the -itemstyle option of the tail column"); 2107 continue; 2108 } 2109 if (column->lock != COLUMN_LOCK_NONE) { 2110 FormatResult(tree->interp, 2111 "can't change the -lock option of the tail column"); 2112 continue; 2113 } 2114 } 2115 2116 /* 2117 * Step 2: Process new values 2118 */ 2119 2120 if (mask & COLU_CONF_IMAGE) { 2121 if (column->imageString == NULL) { 2122 column->image = NULL; 2123 } else { 2124 column->image = Tk_GetImage(tree->interp, tree->tkwin, 2125 column->imageString, ImageChangedProc, 2126 (ClientData) column); 2127 if (column->image == NULL) 2128 continue; 2129 maskFree |= COLU_CONF_IMAGE; 2130 } 2131 } 2132 2133 if (mask & COLU_CONF_ITEMBG) { 2134 if (column->itemBgObj == NULL) { 2135 column->itemBgColor = NULL; 2136 column->itemBgCount = 0; 2137 } else { 2138 int i, length, listObjc; 2139 Tcl_Obj **listObjv; 2140 XColor **colors; 2141 2142 if (Tcl_ListObjGetElements(tree->interp, column->itemBgObj, 2143 &listObjc, &listObjv) != TCL_OK) 2144 continue; 2145 colors = (XColor **) ckalloc(sizeof(XColor *) * listObjc); 2146 for (i = 0; i < listObjc; i++) 2147 colors[i] = NULL; 2148 for (i = 0; i < listObjc; i++) { 2149 /* Can specify "" for tree background */ 2150 (void) Tcl_GetStringFromObj(listObjv[i], &length); 2151 if (length != 0) { 2152 colors[i] = Tk_AllocColorFromObj(tree->interp, 2153 tree->tkwin, listObjv[i]); 2154 if (colors[i] == NULL) 2155 break; 2156 } 2157 } 2158 if (i < listObjc) { 2159 Column_FreeColors(colors, listObjc); 2160 continue; 2161 } 2162 column->itemBgColor = colors; 2163 column->itemBgCount = listObjc; 2164 maskFree |= COLU_CONF_ITEMBG; 2165 } 2166 } 2167 2168 /* 2169 * Step 3: Free saved values 2170 */ 2171 2172 if (mask & COLU_CONF_IMAGE) { 2173 if (saved.image != NULL) 2174 Tk_FreeImage(saved.image); 2175 } 2176 if (mask & COLU_CONF_ITEMBG) 2177 Column_FreeColors(saved.itemBgColor, saved.itemBgCount); 2178 Tk_FreeSavedOptions(&savedOptions); 2179 break; 2180 } else { 2181 errorResult = Tcl_GetObjResult(tree->interp); 2182 Tcl_IncrRefCount(errorResult); 2183 Tk_RestoreSavedOptions(&savedOptions); 2184 2185 /* 2186 * Free new values. 2187 */ 2188 if (maskFree & COLU_CONF_IMAGE) 2189 Tk_FreeImage(column->image); 2190 if (maskFree & COLU_CONF_ITEMBG) 2191 Column_FreeColors(column->itemBgColor, column->itemBgCount); 2192 2193 /* 2194 * Restore old values. 2195 */ 2196 if (mask & COLU_CONF_IMAGE) 2197 column->image = saved.image; 2198 if (mask & COLU_CONF_ITEMBG) { 2199 column->itemBgColor = saved.itemBgColor; 2200 column->itemBgCount = saved.itemBgCount; 2201 } 2202 2203 Tcl_SetObjResult(tree->interp, errorResult); 2204 Tcl_DecrRefCount(errorResult); 2205 return TCL_ERROR; 2206 } 2207 } 2208 2209 /* Indicate that all items must recalculate their list of spans. */ 2210 if (visible != column->visible || lock != column->lock) 2211 TreeItem_SpansInvalidate(tree, NULL); 2212 2213 /* Wouldn't have to do this if Tk_InitOptions() would return 2214 * a mask of configured options like Tk_SetOptions() does. */ 2215 if (createFlag) { 2216 if (column->textObj != NULL) 2217 mask |= COLU_CONF_TEXT; 2218 if (column->bitmap != None) 2219 mask |= COLU_CONF_BITMAP; 2220 } 2221 2222 if (mask & COLU_CONF_TEXT) { 2223 if (column->textObj != NULL) 2224 (void) Tcl_GetStringFromObj(column->textObj, &column->textLen); 2225 else 2226 column->textLen = 0; 2227 if (column->textLen) { 2228 Tk_Font tkfont = column->tkfont ? column->tkfont : tree->tkfont; 2229 column->textWidth = Tk_TextWidth(tkfont, column->text, column->textLen); 2230 } else 2231 column->textWidth = 0; 2232 } 2233 2234 if (mask & COLU_CONF_BITMAP) { 2235 if (column->bitmapGC != None) { 2236 Tk_FreeGC(tree->display, column->bitmapGC); 2237 column->bitmapGC = None; 2238 } 2239 if (column->bitmap != None) { 2240 gcValues.clip_mask = column->bitmap; 2241 gcValues.graphics_exposures = False; 2242 gcMask = GCClipMask | GCGraphicsExposures; 2243 column->bitmapGC = Tk_GetGC(tree->tkwin, gcMask, &gcValues); 2244 } 2245 } 2246 2247 if (mask & COLU_CONF_ITEMBG) { 2248 if (!createFlag) { 2249 /* Set max -itembackground */ 2250 tree->columnBgCnt = 0; 2251 walk = tree->columns; 2252 while (walk != NULL) { 2253 if (walk->visible) { 2254 if (walk->itemBgCount > tree->columnBgCnt) 2255 tree->columnBgCnt = walk->itemBgCount; 2256 } 2257 walk = walk->next; 2258 } 2259 } 2260 Tree_DInfoChanged(tree, DINFO_INVALIDATE); 2261 } 2262 2263 if (!createFlag && (column->lock != lock)) { 2264 TreeColumn before = NULL; 2265 switch (column->lock) { 2266 case COLUMN_LOCK_LEFT: 2267 before = tree->columnLockNone; 2268 if (before == NULL) 2269 before = tree->columnLockRight; 2270 break; 2271 case COLUMN_LOCK_NONE: 2272 if (lock == COLUMN_LOCK_LEFT) { 2273 before = tree->columnLockNone; 2274 if (before == NULL) 2275 before = tree->columnLockRight; 2276 } else 2277 before = tree->columnLockRight; 2278 break; 2279 case COLUMN_LOCK_RIGHT: 2280 before = NULL; 2281 break; 2282 } 2283 if (before == NULL) 2284 before = tree->columnTail; 2285 Column_Move(column, before); 2286 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH); 2287 } 2288 2289 if (mask & (COLU_CONF_NWIDTH | COLU_CONF_TWIDTH)) 2290 mask |= COLU_CONF_NHEIGHT; 2291 if (mask & (COLU_CONF_JUSTIFY | COLU_CONF_TEXT)) 2292 column->textLayoutInvalid = TRUE; 2293 2294 if (mask & COLU_CONF_NWIDTH) 2295 column->neededWidth = -1; 2296 if (mask & COLU_CONF_NHEIGHT) { 2297 column->neededHeight = -1; 2298 tree->headerHeight = -1; 2299 } 2300 2301 /* FIXME: only this column needs to be redisplayed. */ 2302 if (mask & COLU_CONF_JUSTIFY) 2303 Tree_DInfoChanged(tree, DINFO_INVALIDATE); 2304 2305 /* -stepwidth and -widthhack */ 2306 if (mask & COLU_CONF_RANGES) 2307 Tree_DInfoChanged(tree, DINFO_REDO_RANGES); 2308 2309 /* Redraw everything */ 2310 if (mask & (COLU_CONF_TWIDTH | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT)) { 2311 tree->widthOfColumns = -1; 2312 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 2313 Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH | DINFO_DRAW_HEADER); 2314 } 2315 2316 /* Redraw header only */ 2317 else if (mask & COLU_CONF_DISPLAY) { 2318 Tree_DInfoChanged(tree, DINFO_DRAW_HEADER); 2319 } 2320 2321 return TCL_OK; 2322} 2323 2324/* 2325 *---------------------------------------------------------------------- 2326 * 2327 * Column_Alloc -- 2328 * 2329 * Allocate and initialize a new Column record. 2330 * 2331 * Results: 2332 * Pointer to the new Column, or NULL if errors occurred. 2333 * 2334 * Side effects: 2335 * Memory is allocated. 2336 * 2337 *---------------------------------------------------------------------- 2338 */ 2339 2340static TreeColumn 2341Column_Alloc( 2342 TreeCtrl *tree /* Widget info. */ 2343 ) 2344{ 2345 TreeColumn column; 2346 2347 column = (TreeColumn) ckalloc(sizeof(TreeColumn_)); 2348 memset(column, '\0', sizeof(TreeColumn_)); 2349 column->tree = tree; 2350 column->optionTable = Tk_CreateOptionTable(tree->interp, columnSpecs); 2351 column->itemJustify = -1; 2352 if (Tk_InitOptions(tree->interp, (char *) column, column->optionTable, 2353 tree->tkwin) != TCL_OK) { 2354 WFREE(column, TreeColumn_); 2355 return NULL; 2356 } 2357#if 0 2358 if (Tk_SetOptions(header->tree->interp, (char *) column, 2359 column->optionTable, 0, 2360 NULL, header->tree->tkwin, &savedOptions, 2361 (int *) NULL) != TCL_OK) { 2362 WFREE(column, TreeColumn_); 2363 return NULL; 2364 } 2365#endif 2366 column->neededWidth = column->neededHeight = -1; 2367 tree->headerHeight = tree->widthOfColumns = -1; 2368 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 2369 column->id = tree->nextColumnId++; 2370 tree->columnCount++; 2371 return column; 2372} 2373 2374/* 2375 *---------------------------------------------------------------------- 2376 * 2377 * Column_Free -- 2378 * 2379 * Free a Column. 2380 * 2381 * Results: 2382 * Pointer to the next column. 2383 * 2384 * Side effects: 2385 * Memory is deallocated. If this is the last column being 2386 * deleted, the TreeCtrl.nextColumnId field is reset to zero. 2387 * 2388 *---------------------------------------------------------------------- 2389 */ 2390 2391static TreeColumn 2392Column_Free( 2393 TreeColumn column /* Column record. */ 2394 ) 2395{ 2396 TreeCtrl *tree = column->tree; 2397 TreeColumn next = column->next; 2398 2399 Column_FreeColors(column->itemBgColor, column->itemBgCount); 2400 if (column->bitmapGC != None) 2401 Tk_FreeGC(tree->display, column->bitmapGC); 2402 if (column->image != NULL) 2403 Tk_FreeImage(column->image); 2404 if (column->textLayout != NULL) 2405 TextLayout_Free(column->textLayout); 2406 TreeDisplay_FreeColumnDInfo(tree, column); 2407 Tk_FreeConfigOptions((char *) column, column->optionTable, tree->tkwin); 2408 WFREE(column, TreeColumn_); 2409 tree->columnCount--; 2410 if (tree->columnCount == 0) 2411 tree->nextColumnId = 0; 2412 return next; 2413} 2414 2415/* 2416 *---------------------------------------------------------------------- 2417 * 2418 * TreeColumn_SetDInfo -- 2419 * 2420 * Store a display-info token in a column. Called by the display 2421 * code. 2422 * 2423 * Results: 2424 * None. 2425 * 2426 * Side effects: 2427 * None. 2428 * 2429 *---------------------------------------------------------------------- 2430 */ 2431 2432void 2433TreeColumn_SetDInfo( 2434 TreeColumn column, /* Column record. */ 2435 TreeColumnDInfo dInfo /* Display info token. */ 2436 ) 2437{ 2438 column->dInfo = dInfo; 2439} 2440 2441/* 2442 *---------------------------------------------------------------------- 2443 * 2444 * TreeColumn_GetDInfo -- 2445 * 2446 * Return the display-info token of a column. Called by the display 2447 * code. 2448 * 2449 * Results: 2450 * The display-info token or NULL. 2451 * 2452 * Side effects: 2453 * None. 2454 * 2455 *---------------------------------------------------------------------- 2456 */ 2457 2458TreeColumnDInfo 2459TreeColumn_GetDInfo( 2460 TreeColumn column /* Column record. */ 2461 ) 2462{ 2463 return column->dInfo; 2464} 2465 2466/* 2467 *---------------------------------------------------------------------- 2468 * 2469 * TreeColumn_FixedWidth -- 2470 * 2471 * Return the value of the -width option. 2472 * 2473 * Results: 2474 * The pixel width or -1 if the -width option is unspecified. 2475 * 2476 * Side effects: 2477 * None. 2478 * 2479 *---------------------------------------------------------------------- 2480 */ 2481 2482int 2483TreeColumn_FixedWidth( 2484 TreeColumn column /* Column token. */ 2485 ) 2486{ 2487 return column->widthObj ? column->width : -1; 2488} 2489 2490/* 2491 *---------------------------------------------------------------------- 2492 * 2493 * TreeColumn_MinWidth -- 2494 * 2495 * Return the value of the -minwidth option. 2496 * 2497 * Results: 2498 * The pixel width or -1 if the -minwidth option is unspecified. 2499 * 2500 * Side effects: 2501 * None. 2502 * 2503 *---------------------------------------------------------------------- 2504 */ 2505 2506int 2507TreeColumn_MinWidth( 2508 TreeColumn column /* Column token. */ 2509 ) 2510{ 2511 return column->minWidthObj ? column->minWidth : -1; 2512} 2513 2514/* 2515 *---------------------------------------------------------------------- 2516 * 2517 * TreeColumn_MaxWidth -- 2518 * 2519 * Return the value of the -maxwidth option. 2520 * 2521 * Results: 2522 * The pixel width or -1 if the -maxwidth option is unspecified. 2523 * 2524 * Side effects: 2525 * None. 2526 * 2527 *---------------------------------------------------------------------- 2528 */ 2529 2530int 2531TreeColumn_MaxWidth( 2532 TreeColumn column /* Column token. */ 2533 ) 2534{ 2535 return column->maxWidthObj ? column->maxWidth : -1; 2536} 2537 2538#ifdef DEPRECATED 2539/* 2540 *---------------------------------------------------------------------- 2541 * 2542 * TreeColumn_StepWidth -- 2543 * 2544 * Return the value of the -stepwidth option. 2545 * NOTE: -stepwidth is deprecated. 2546 * 2547 * Results: 2548 * The pixel width or -1 if the -stepwidth option is unspecified. 2549 * 2550 * Side effects: 2551 * None. 2552 * 2553 *---------------------------------------------------------------------- 2554 */ 2555 2556int 2557TreeColumn_StepWidth( 2558 TreeColumn column /* Column token. */ 2559 ) 2560{ 2561 return column->stepWidthObj ? column->stepWidth : -1; 2562} 2563#endif /* DEPRECATED */ 2564 2565/* 2566 *---------------------------------------------------------------------- 2567 * 2568 * Column_UpdateTextLayout -- 2569 * 2570 * Recalculate the TextLayout for the text displayed in the 2571 * column header. The old TextLayout (if any) is freed. If 2572 * there is no text or if it is only one line then no TextLayout 2573 * is created. 2574 * 2575 * Results: 2576 * None. 2577 * 2578 * Side effects: 2579 * Memory may be allocated/deallocated. 2580 * 2581 *---------------------------------------------------------------------- 2582 */ 2583 2584static void 2585Column_UpdateTextLayout( 2586 TreeColumn column, /* Column record. */ 2587 int width /* Maximum line length. Zero means there 2588 * is no limit. */ 2589 ) 2590{ 2591 Tk_Font tkfont; 2592 char *text = column->text; 2593 int textLen = column->textLen; 2594 int justify = column->justify; 2595 int maxLines = MAX(column->textLines, 0); /* -textlines */ 2596 int wrap = TEXT_WRAP_WORD; /* -textwrap */ 2597 int flags = 0; 2598 int i, multiLine = FALSE; 2599 2600 if (column->textLayout != NULL) { 2601 TextLayout_Free(column->textLayout); 2602 column->textLayout = NULL; 2603 } 2604 2605 if ((text == NULL) || (textLen == 0)) 2606 return; 2607 2608 for (i = 0; i < textLen; i++) { 2609 if ((text[i] == '\n') || (text[i] == '\r')) { 2610 multiLine = TRUE; 2611 break; 2612 } 2613 } 2614 2615#ifdef MAC_OSX_TK 2616 /* The height of the header is fixed on Aqua. There is only room for 2617 * a single line of text. */ 2618 if (column->tree->useTheme) 2619 maxLines = 1; 2620#endif 2621 2622 if (!multiLine && ((maxLines == 1) || (!width || (width >= column->textWidth)))) 2623 return; 2624 2625 tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; 2626 2627 if (wrap == TEXT_WRAP_WORD) 2628 flags |= TK_WHOLE_WORDS; 2629 2630 column->textLayout = TextLayout_Compute(tkfont, text, 2631 Tcl_NumUtfChars(text, textLen), width, justify, maxLines, 2632 0, 0, flags); 2633} 2634 2635/* 2636 *---------------------------------------------------------------------- 2637 * 2638 * Column_GetArrowSize -- 2639 * 2640 * Return the size of the sort arrow displayed in the column header 2641 * for the column's current state. 2642 * 2643 * Results: 2644 * Height and width of the arrow. 2645 * 2646 * Side effects: 2647 * None. 2648 * 2649 *---------------------------------------------------------------------- 2650 */ 2651 2652static void 2653Column_GetArrowSize( 2654 TreeColumn column, /* Column record. */ 2655 int *widthPtr, /* Returned width. */ 2656 int *heightPtr /* Returned height. */ 2657 ) 2658{ 2659 TreeCtrl *tree = column->tree; 2660 int state = Column_MakeState(column); 2661 int arrowWidth = -1, arrowHeight; 2662 Tk_Image image; 2663 Pixmap bitmap; 2664 2665 /* image > bitmap > theme > draw */ 2666 image = PerStateImage_ForState(tree, &column->arrowImage, 2667 state, NULL); 2668 if (image != NULL) { 2669 Tk_SizeOfImage(image, &arrowWidth, &arrowHeight); 2670 } 2671 if (arrowWidth == -1) { 2672 bitmap = PerStateBitmap_ForState(tree, &column->arrowBitmap, 2673 state, NULL); 2674 if (bitmap != None) { 2675 Tk_SizeOfBitmap(tree->display, bitmap, &arrowWidth, &arrowHeight); 2676 } 2677 } 2678 if ((arrowWidth == -1) && tree->useTheme && 2679 TreeTheme_GetArrowSize(tree, Tk_WindowId(tree->tkwin), 2680 column->arrow == ARROW_UP, &arrowWidth, &arrowHeight) == TCL_OK) { 2681 } 2682 if (arrowWidth == -1) { 2683 Tk_Font tkfont = column->tkfont ? column->tkfont : tree->tkfont; 2684 Tk_FontMetrics fm; 2685 Tk_GetFontMetrics(tkfont, &fm); 2686 arrowWidth = (fm.linespace + column->textPadY[PAD_TOP_LEFT] + 2687 column->textPadY[PAD_BOTTOM_RIGHT] + column->borderWidth * 2) / 2; 2688 if (!(arrowWidth & 1)) 2689 arrowWidth--; 2690 arrowHeight = arrowWidth; 2691 } 2692 2693 (*widthPtr) = arrowWidth; 2694 (*heightPtr) = arrowHeight; 2695} 2696 2697/* 2698 * The following structure holds size/position info for all the graphical 2699 * elements of a column header. 2700 */ 2701struct Layout 2702{ 2703 Tk_Font tkfont; 2704 Tk_FontMetrics fm; 2705 int width; /* Provided by caller */ 2706 int height; /* Provided by caller */ 2707 int textLeft; 2708 int textWidth; 2709 int bytesThatFit; 2710 int imageLeft; 2711 int imageWidth; 2712 int arrowLeft; 2713 int arrowWidth; 2714 int arrowHeight; 2715}; 2716 2717/* 2718 * The following structure is used by the Column_DoLayout() procedure to 2719 * hold size/position info for each graphical element displayed in the 2720 * header. 2721 */ 2722struct LayoutPart 2723{ 2724 int padX[2]; 2725 int padY[2]; 2726 int width; 2727 int height; 2728 int left; 2729 int top; 2730}; 2731 2732/* 2733 *---------------------------------------------------------------------- 2734 * 2735 * Column_DoLayout -- 2736 * 2737 * Arrange all the graphical elements making up a column header. 2738 * 2739 * Results: 2740 * Layout info is returned. 2741 * 2742 * Side effects: 2743 * None. 2744 * 2745 *---------------------------------------------------------------------- 2746 */ 2747 2748static void 2749Column_DoLayout( 2750 TreeColumn column, /* Column record. */ 2751 struct Layout *layout /* Returned layout info. The width and 2752 * height fields must be initialized. */ 2753 ) 2754{ 2755#if defined(MAC_OSX_TK) 2756 TreeCtrl *tree = column->tree; 2757#endif 2758 struct LayoutPart *parts[3]; 2759 struct LayoutPart partArrow, partImage, partText; 2760 int i, padList[4], widthList[3], n = 0; 2761 int iArrow = -1, iImage = -1, iText = -1; 2762 int left, right; 2763 int widthForText = 0; 2764#if defined(MAC_OSX_TK) 2765 int margins[4]; 2766 int arrow = column->arrow; 2767 int arrowSide = column->arrowSide; 2768 int arrowGravity = column->arrowGravity; 2769#endif 2770 2771#if defined(MAC_OSX_TK) 2772 /* Under Aqua, we let the Appearance Manager draw the sort arrow */ 2773 if (tree->useTheme) { 2774 column->arrow = ARROW_NONE; 2775 column->arrowSide = SIDE_RIGHT; 2776 column->arrowGravity = SIDE_RIGHT; 2777 } 2778#endif 2779 2780 padList[0] = 0; 2781 2782 if (column->arrow != ARROW_NONE) { 2783 Column_GetArrowSize(column, &partArrow.width, &partArrow.height); 2784 partArrow.padX[PAD_TOP_LEFT] = column->arrowPadX[PAD_TOP_LEFT]; 2785 partArrow.padX[PAD_BOTTOM_RIGHT] = column->arrowPadX[PAD_BOTTOM_RIGHT]; 2786 partArrow.padY[PAD_TOP_LEFT] = column->arrowPadY[PAD_TOP_LEFT]; 2787 partArrow.padY[PAD_BOTTOM_RIGHT] = column->arrowPadY[PAD_BOTTOM_RIGHT]; 2788 } 2789 if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) { 2790 parts[n] = &partArrow; 2791 padList[n] = partArrow.padX[PAD_TOP_LEFT]; 2792 padList[n + 1] = partArrow.padX[PAD_BOTTOM_RIGHT]; 2793 iArrow = n++; 2794 } 2795 if ((column->image != NULL) || (column->bitmap != None)) { 2796 if (column->image != NULL) 2797 Tk_SizeOfImage(column->image, &partImage.width, &partImage.height); 2798 else 2799 Tk_SizeOfBitmap(column->tree->display, column->bitmap, &partImage.width, &partImage.height); 2800 partImage.padX[PAD_TOP_LEFT] = column->imagePadX[PAD_TOP_LEFT]; 2801 partImage.padX[PAD_BOTTOM_RIGHT] = column->imagePadX[PAD_BOTTOM_RIGHT]; 2802 partImage.padY[PAD_TOP_LEFT] = column->imagePadY[PAD_TOP_LEFT]; 2803 partImage.padY[PAD_BOTTOM_RIGHT] = column->imagePadY[PAD_BOTTOM_RIGHT]; 2804 parts[n] = &partImage; 2805 padList[n] = MAX(partImage.padX[PAD_TOP_LEFT], padList[n]); 2806 padList[n + 1] = partImage.padX[PAD_BOTTOM_RIGHT]; 2807 iImage = n++; 2808 } 2809 if (column->textLen > 0) { 2810 struct LayoutPart *parts2[3]; 2811 int n2 = 0; 2812 2813 partText.padX[PAD_TOP_LEFT] = column->textPadX[PAD_TOP_LEFT]; 2814 partText.padX[PAD_BOTTOM_RIGHT] = column->textPadX[PAD_BOTTOM_RIGHT]; 2815 partText.padY[PAD_TOP_LEFT] = column->textPadY[PAD_TOP_LEFT]; 2816 partText.padY[PAD_BOTTOM_RIGHT] = column->textPadY[PAD_BOTTOM_RIGHT]; 2817 2818 /* Calculate space for the text */ 2819 if (iArrow != -1) 2820 parts2[n2++] = &partArrow; 2821 if (iImage != -1) 2822 parts2[n2++] = &partImage; 2823 parts2[n2++] = &partText; 2824 if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) 2825 parts2[n2++] = &partArrow; 2826 widthForText = layout->width; 2827 for (i = 0; i < n2; i++) { 2828 if (i) 2829 widthForText -= MAX(parts2[i]->padX[0], parts2[i-1]->padX[1]); 2830 else 2831 widthForText -= parts2[i]->padX[0]; 2832 if (parts2[i] != &partText) 2833 widthForText -= parts2[i]->width; 2834 } 2835 widthForText -= parts2[n2-1]->padX[1]; 2836 } 2837 layout->bytesThatFit = 0; 2838 if (widthForText > 0) { 2839 if (column->textLayoutInvalid || (column->textLayoutWidth != widthForText)) { 2840 Column_UpdateTextLayout(column, widthForText); 2841 column->textLayoutInvalid = FALSE; 2842 column->textLayoutWidth = widthForText; 2843 } 2844 if (column->textLayout != NULL) { 2845 TextLayout_Size(column->textLayout, &partText.width, &partText.height); 2846 parts[n] = &partText; 2847 padList[n] = MAX(partText.padX[PAD_TOP_LEFT], padList[n]); 2848 padList[n + 1] = partText.padX[PAD_BOTTOM_RIGHT]; 2849 iText = n++; 2850 } else { 2851 layout->tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; 2852 Tk_GetFontMetrics(layout->tkfont, &layout->fm); 2853 if (widthForText >= column->textWidth) { 2854 partText.width = column->textWidth; 2855 partText.height = layout->fm.linespace; 2856 layout->bytesThatFit = column->textLen; 2857 } else { 2858 partText.width = widthForText; 2859 partText.height = layout->fm.linespace; 2860 layout->bytesThatFit = Tree_Ellipsis(layout->tkfont, 2861 column->text, column->textLen, &partText.width, 2862 "...", FALSE); 2863 } 2864 parts[n] = &partText; 2865 padList[n] = MAX(partText.padX[PAD_TOP_LEFT], padList[n]); 2866 padList[n + 1] = partText.padX[PAD_BOTTOM_RIGHT]; 2867 iText = n++; 2868 } 2869 } 2870 if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) { 2871 parts[n] = &partArrow; 2872 padList[n] = MAX(partArrow.padX[PAD_TOP_LEFT], padList[n]); 2873 padList[n + 1] = partArrow.padX[PAD_BOTTOM_RIGHT]; 2874 iArrow = n++; 2875 } 2876 2877#if defined(MAC_OSX_TK) 2878 /* Under Aqua, we let the Appearance Manager draw the sort arrow */ 2879 /* This code assumes the arrow is on the right */ 2880 if (tree->useTheme && (arrow != ARROW_NONE)) { 2881 if (TreeTheme_GetHeaderContentMargins(tree, column->state, 2882 arrow, margins) == TCL_OK) { 2883 parts[n] = &partArrow; 2884 partArrow.width = margins[2]; 2885 padList[n] = MAX(0, padList[n]); /* ignore -arrowpadx */ 2886 padList[n + 1] = 0; 2887 iArrow = n++; 2888 } 2889 } 2890 if (n == 0) { 2891 column->arrow = arrow; 2892 column->arrowSide = arrowSide; 2893 column->arrowGravity = arrowGravity; 2894 } 2895#endif 2896 2897 if (n == 0) 2898 return; 2899 2900 for (i = 0; i < n; i++) { 2901 padList[i] = parts[i]->padX[0]; 2902 if (i) 2903 padList[i] = MAX(padList[i], parts[i-1]->padX[1]); 2904 padList[i + 1] = parts[i]->padX[1]; 2905 widthList[i] = parts[i]->width; 2906 } 2907 if (iText != -1) { 2908 switch (column->justify) { 2909 case TK_JUSTIFY_LEFT: 2910 partText.left = 0; 2911 break; 2912 case TK_JUSTIFY_RIGHT: 2913 partText.left = layout->width; 2914 break; 2915 case TK_JUSTIFY_CENTER: 2916 if (iImage == -1) 2917 partText.left = (layout->width - partText.width) / 2; 2918 else 2919 partText.left = (layout->width - partImage.width - 2920 padList[iText] - partText.width) / 2 + partImage.width + 2921 padList[iText]; 2922 break; 2923 } 2924 } 2925 2926 if (iImage != -1) { 2927 switch (column->justify) { 2928 case TK_JUSTIFY_LEFT: 2929 partImage.left = 0; 2930 break; 2931 case TK_JUSTIFY_RIGHT: 2932 partImage.left = layout->width; 2933 break; 2934 case TK_JUSTIFY_CENTER: 2935 if (iText == -1) 2936 partImage.left = (layout->width - partImage.width) / 2; 2937 else 2938 partImage.left = (layout->width - partImage.width - 2939 padList[iText] - partText.width) / 2; 2940 break; 2941 } 2942 } 2943 2944 if (iArrow == -1) 2945 goto finish; 2946 2947 switch (column->justify) { 2948 case TK_JUSTIFY_LEFT: 2949 switch (column->arrowSide) { 2950 case SIDE_LEFT: 2951 partArrow.left = 0; 2952 break; 2953 case SIDE_RIGHT: 2954 switch (column->arrowGravity) { 2955 case SIDE_LEFT: 2956 partArrow.left = 0; 2957 break; 2958 case SIDE_RIGHT: 2959 partArrow.left = layout->width; 2960 break; 2961 } 2962 break; 2963 } 2964 break; 2965 case TK_JUSTIFY_RIGHT: 2966 switch (column->arrowSide) { 2967 case SIDE_LEFT: 2968 switch (column->arrowGravity) { 2969 case SIDE_LEFT: 2970 partArrow.left = 0; 2971 break; 2972 case SIDE_RIGHT: 2973 partArrow.left = layout->width; 2974 break; 2975 } 2976 break; 2977 case SIDE_RIGHT: 2978 partArrow.left = layout->width; 2979 break; 2980 } 2981 break; 2982 case TK_JUSTIFY_CENTER: 2983 switch (column->arrowSide) { 2984 case SIDE_LEFT: 2985 switch (column->arrowGravity) { 2986 case SIDE_LEFT: 2987 partArrow.left = 0; 2988 break; 2989 case SIDE_RIGHT: 2990 if (n == 3) 2991 partArrow.left = 2992 (layout->width - widthList[1] - padList[2] - 2993 widthList[2]) / 2 - padList[1] - widthList[0]; 2994 else if (n == 2) 2995 partArrow.left = 2996 (layout->width - widthList[1]) / 2 - 2997 padList[1] - widthList[0]; 2998 else 2999 partArrow.left = layout->width; 3000 break; 3001 } 3002 break; 3003 case SIDE_RIGHT: 3004 switch (column->arrowGravity) { 3005 case SIDE_LEFT: 3006 if (n == 3) 3007 partArrow.left = 3008 (layout->width - widthList[0] - padList[1] - 3009 widthList[1]) / 2 + widthList[0] + padList[1] + 3010 widthList[1] + padList[2]; 3011 else if (n == 2) 3012 partArrow.left = 3013 (layout->width - widthList[0]) / 2 + 3014 widthList[0] + padList[1]; 3015 else 3016 partArrow.left = 0; 3017 break; 3018 case SIDE_RIGHT: 3019 partArrow.left = layout->width; 3020 break; 3021 } 3022 break; 3023 } 3024 break; 3025 } 3026 3027finish: 3028 right = layout->width - padList[n]; 3029 for (i = n - 1; i >= 0; i--) { 3030 if (parts[i]->left + parts[i]->width > right) 3031 parts[i]->left = right - parts[i]->width; 3032 right -= parts[i]->width + padList[i]; 3033 } 3034 left = padList[0]; 3035 for (i = 0; i < n; i++) { 3036 if (parts[i]->left < left) 3037 parts[i]->left = left; 3038 left += parts[i]->width + padList[i + 1]; 3039 } 3040 3041 if (iArrow != -1) { 3042 layout->arrowLeft = partArrow.left; 3043 layout->arrowWidth = partArrow.width; 3044 layout->arrowHeight = partArrow.height; 3045 } 3046 if (iImage != -1) { 3047 layout->imageLeft = partImage.left; 3048 layout->imageWidth = partImage.width; 3049 } 3050 if (iText != -1) { 3051 layout->textLeft = partText.left; 3052 layout->textWidth = partText.width; 3053 } 3054 3055#if defined(MAC_OSX_TK) 3056 /* Under Aqua, we let the Appearance Manager draw the sort arrow */ 3057 column->arrow = arrow; 3058 column->arrowSide = arrowSide; 3059 column->arrowGravity = arrowGravity; 3060#endif 3061} 3062 3063/* 3064 *---------------------------------------------------------------------- 3065 * 3066 * TreeColumn_NeededWidth -- 3067 * 3068 * Return the total width requested by all the graphical elements 3069 * that make up a column header. The width is recalculated if it 3070 * is marked out-of-date. 3071 * 3072 * Results: 3073 * The width needed by the current arrangement of the 3074 * bitmap/image/text/arrow. 3075 * 3076 * Side effects: 3077 * None. 3078 * 3079 *---------------------------------------------------------------------- 3080 */ 3081 3082int 3083TreeColumn_NeededWidth( 3084 TreeColumn column /* Column token. */ 3085 ) 3086{ 3087 TreeCtrl *tree = column->tree; 3088 int i, widthList[3], padList[4], n = 0; 3089 int arrowWidth, arrowHeight; 3090#if defined(MAC_OSX_TK) 3091 int margins[4]; 3092 int arrow = column->arrow; 3093#endif 3094 3095 if (!tree->showHeader) 3096 return 0; 3097 3098 if (column->neededWidth >= 0) 3099 return column->neededWidth; 3100 3101 for (i = 0; i < 3; i++) widthList[i] = 0; 3102 for (i = 0; i < 4; i++) padList[i] = 0; 3103 3104#if defined(MAC_OSX_TK) 3105 /* Under OSX we let the Appearance Manager draw the sort arrow. */ 3106 if (tree->useTheme) 3107 column->arrow = ARROW_NONE; 3108#endif 3109 3110 if (column->arrow != ARROW_NONE) 3111 Column_GetArrowSize(column, &arrowWidth, &arrowHeight); 3112 if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) { 3113 widthList[n] = arrowWidth; 3114 padList[n] = column->arrowPadX[PAD_TOP_LEFT]; 3115 padList[n + 1] = column->arrowPadX[PAD_BOTTOM_RIGHT]; 3116 n++; 3117 } 3118 if ((column->image != NULL) || (column->bitmap != None)) { 3119 int imgWidth, imgHeight; 3120 if (column->image != NULL) 3121 Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); 3122 else 3123 Tk_SizeOfBitmap(tree->display, column->bitmap, &imgWidth, &imgHeight); 3124 padList[n] = MAX(column->imagePadX[PAD_TOP_LEFT], padList[n]); 3125 padList[n + 1] = column->imagePadX[PAD_BOTTOM_RIGHT]; 3126 widthList[n] = imgWidth; 3127 n++; 3128 } 3129 if (column->textLen > 0) { 3130 padList[n] = MAX(column->textPadX[PAD_TOP_LEFT], padList[n]); 3131 padList[n + 1] = column->textPadX[PAD_BOTTOM_RIGHT]; 3132 if (column->textLayoutInvalid || (column->textLayoutWidth != 0)) { 3133 Column_UpdateTextLayout(column, 0); 3134 column->textLayoutInvalid = FALSE; 3135 column->textLayoutWidth = 0; 3136 } 3137 if (column->textLayout != NULL) 3138 TextLayout_Size(column->textLayout, &widthList[n], NULL); 3139 else 3140 widthList[n] = column->textWidth; 3141 n++; 3142 } 3143 if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) { 3144 widthList[n] = arrowWidth; 3145 padList[n] = MAX(column->arrowPadX[PAD_TOP_LEFT], padList[n]); 3146 padList[n + 1] = column->arrowPadX[PAD_BOTTOM_RIGHT]; 3147 n++; 3148 } 3149 3150 column->neededWidth = 0; 3151 for (i = 0; i < n; i++) 3152 column->neededWidth += widthList[i] + padList[i]; 3153 column->neededWidth += padList[n]; 3154 3155#if defined(MAC_OSX_TK) 3156 if (tree->useTheme) 3157 column->arrow = arrow; 3158 3159 /* Under OSX we let the Appearance Manager draw the sort arrow. This code 3160 * assumes the arrow is on the right. */ 3161 if (tree->useTheme && 3162 (TreeTheme_GetHeaderContentMargins(tree, column->state, column->arrow, 3163 margins) == TCL_OK)) { 3164 column->neededWidth += margins[2]; 3165 } 3166#endif 3167 3168 /* Notice I'm not considering column->borderWidth. */ 3169 3170 return column->neededWidth; 3171} 3172 3173/* 3174 *---------------------------------------------------------------------- 3175 * 3176 * TreeColumn_NeededHeight -- 3177 * 3178 * Return the total height requested by all the graphical elements 3179 * that make up a column header. The height is recalculated if it 3180 * is marked out-of-date. 3181 * 3182 * Results: 3183 * The height needed by the current arrangement of the 3184 * bitmap/image/text/arrow. 3185 * 3186 * Side effects: 3187 * None. 3188 * 3189 *---------------------------------------------------------------------- 3190 */ 3191 3192int 3193TreeColumn_NeededHeight( 3194 TreeColumn column /* Column token. */ 3195 ) 3196{ 3197 TreeCtrl *tree = column->tree; 3198 int margins[4]; 3199 3200 if (column->neededHeight >= 0) 3201 return column->neededHeight; 3202 3203#if defined(MAC_OSX_TK) 3204 /* List headers are a fixed height on Aqua */ 3205 if (tree->useTheme && 3206 (TreeTheme_GetHeaderFixedHeight(tree, &column->neededHeight) == TCL_OK)) { 3207 return column->neededHeight; 3208 } 3209#endif 3210 3211 column->neededHeight = 0; 3212 if (column->arrow != ARROW_NONE) { 3213 int arrowWidth, arrowHeight; 3214 Column_GetArrowSize(column, &arrowWidth, &arrowHeight); 3215 arrowHeight += column->arrowPadY[PAD_TOP_LEFT] 3216 + column->arrowPadY[PAD_BOTTOM_RIGHT]; 3217 column->neededHeight = MAX(column->neededHeight, arrowHeight); 3218 } 3219 if ((column->image != NULL) || (column->bitmap != None)) { 3220 int imgWidth, imgHeight; 3221 if (column->image != NULL) 3222 Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); 3223 else 3224 Tk_SizeOfBitmap(tree->display, column->bitmap, &imgWidth, &imgHeight); 3225 imgHeight += column->imagePadY[PAD_TOP_LEFT] 3226 + column->imagePadY[PAD_BOTTOM_RIGHT]; 3227 column->neededHeight = MAX(column->neededHeight, imgHeight); 3228 } 3229 if (column->text != NULL) { 3230 struct Layout layout; 3231 layout.width = TreeColumn_UseWidth(column); 3232 layout.height = -1; 3233 Column_DoLayout(column, &layout); 3234 if (column->textLayout != NULL) { 3235 int height; 3236 TextLayout_Size(column->textLayout, NULL, &height); 3237 height += column->textPadY[PAD_TOP_LEFT] 3238 + column->textPadY[PAD_BOTTOM_RIGHT]; 3239 column->neededHeight = MAX(column->neededHeight, height); 3240 } else { 3241 Tk_Font tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; 3242 Tk_FontMetrics fm; 3243 Tk_GetFontMetrics(tkfont, &fm); 3244 fm.linespace += column->textPadY[PAD_TOP_LEFT] 3245 + column->textPadY[PAD_BOTTOM_RIGHT]; 3246 column->neededHeight = MAX(column->neededHeight, fm.linespace); 3247 } 3248 } 3249 if (column->tree->useTheme && 3250 (TreeTheme_GetHeaderContentMargins(tree, column->state, 3251 column->arrow, margins) == TCL_OK)) { 3252#ifdef WIN32 3253 /* I'm hacking these margins since the default XP theme does not give 3254 * reasonable ContentMargins for HP_HEADERITEM */ 3255 int bw = MAX(column->borderWidth, 3); 3256 margins[1] = MAX(margins[1], bw); 3257 margins[3] = MAX(margins[3], bw); 3258#endif /* WIN32 */ 3259 column->neededHeight += margins[1] + margins[3]; 3260 } else { 3261 column->neededHeight += column->borderWidth * 2; 3262 } 3263 3264 return column->neededHeight; 3265} 3266 3267/* 3268 *---------------------------------------------------------------------- 3269 * 3270 * TreeColumn_UseWidth -- 3271 * 3272 * Return the actual display width of a column. 3273 * 3274 * Results: 3275 * Pixel width. 3276 * 3277 * Side effects: 3278 * The size of any column that is marked out-of-date is 3279 * recalculated. This could involve recalculating the size of 3280 * every element and style in the column in all items. 3281 * 3282 *---------------------------------------------------------------------- 3283 */ 3284 3285int 3286TreeColumn_UseWidth( 3287 TreeColumn column /* Column token. */ 3288 ) 3289{ 3290 /* Update layout if needed */ 3291 (void) Tree_WidthOfColumns(column->tree); 3292 3293 return column->useWidth; 3294} 3295 3296/* 3297 *---------------------------------------------------------------------- 3298 * 3299 * TreeColumn_Offset -- 3300 * 3301 * Return the x-offset of a column. 3302 * 3303 * Results: 3304 * Pixel offset. 3305 * 3306 * Side effects: 3307 * Column layout is updated if needed. 3308 * 3309 *---------------------------------------------------------------------- 3310 */ 3311 3312int 3313TreeColumn_Offset( 3314 TreeColumn column /* Column token. */ 3315 ) 3316{ 3317 /* Update layout if needed */ 3318 (void) Tree_WidthOfColumns(column->tree); 3319 3320 return column->offset; 3321} 3322 3323/* 3324 *---------------------------------------------------------------------- 3325 * 3326 * TreeColumn_ItemJustify -- 3327 * 3328 * Return the value of the -itemjustify config option for a column. 3329 * If -itemjustify is unspecified, then return the value of the 3330 * -justify option. 3331 * 3332 * Results: 3333 * TK_JUSTIFY_xxx constant. 3334 * 3335 * Side effects: 3336 * None. 3337 * 3338 *---------------------------------------------------------------------- 3339 */ 3340 3341Tk_Justify 3342TreeColumn_ItemJustify( 3343 TreeColumn column /* Column token. */ 3344 ) 3345{ 3346 return (column->itemJustify != -1) ? column->itemJustify : column->justify; 3347} 3348 3349#ifdef DEPRECATED 3350/* 3351 *---------------------------------------------------------------------- 3352 * 3353 * TreeColumn_WidthHack -- 3354 * 3355 * Return the value of the -widthhack config option for a column. 3356 * NOTE: -widthhack is deprecated. 3357 * 3358 * Results: 3359 * Boolean value. 3360 * 3361 * Side effects: 3362 * None. 3363 * 3364 *---------------------------------------------------------------------- 3365 */ 3366 3367int 3368TreeColumn_WidthHack( 3369 TreeColumn column /* Column token. */ 3370 ) 3371{ 3372 return column->widthHack; 3373} 3374#endif /* DEPRECATED */ 3375 3376/* 3377 *---------------------------------------------------------------------- 3378 * 3379 * TreeColumn_Squeeze -- 3380 * 3381 * Return the value of the -squeeze config option for a column. 3382 * 3383 * Results: 3384 * Boolean value. 3385 * 3386 * Side effects: 3387 * None. 3388 * 3389 *---------------------------------------------------------------------- 3390 */ 3391 3392int 3393TreeColumn_Squeeze( 3394 TreeColumn column /* Column token. */ 3395 ) 3396{ 3397 return column->squeeze; 3398} 3399 3400/* 3401 *---------------------------------------------------------------------- 3402 * 3403 * TreeColumn_BackgroundCount -- 3404 * 3405 * Return the number of -itembackground colors for a column. 3406 * 3407 * Results: 3408 * column->itemBgCount. 3409 * 3410 * Side effects: 3411 * None. 3412 * 3413 *---------------------------------------------------------------------- 3414 */ 3415 3416int 3417TreeColumn_BackgroundCount( 3418 TreeColumn column /* Column token. */ 3419 ) 3420{ 3421 return column->itemBgCount; 3422} 3423 3424/* 3425 *---------------------------------------------------------------------- 3426 * 3427 * TreeColumn_BackgroundGC -- 3428 * 3429 * Return a graphics context for one color of the -itembackground 3430 * config option for a column. 3431 * 3432 * Results: 3433 * A graphics context, or None. 3434 * 3435 * Side effects: 3436 * Might allocate a new graphics context? But the GC is freed 3437 * when the last reference to the color is lost, so the caller 3438 * need not worry about it. 3439 * 3440 *---------------------------------------------------------------------- 3441 */ 3442 3443GC 3444TreeColumn_BackgroundGC( 3445 TreeColumn column, /* Column token. */ 3446 int index /* This number is determined by the display 3447 * code. */ 3448 ) 3449{ 3450 XColor *color; 3451 3452 if ((index < 0) || (column->itemBgCount == 0)) 3453 return None; 3454 color = column->itemBgColor[index % column->itemBgCount]; 3455 if (color == NULL) 3456 return None; 3457 return Tk_GCForColor(color, Tk_WindowId(column->tree->tkwin)); 3458} 3459 3460/* 3461 *---------------------------------------------------------------------- 3462 * 3463 * TreeColumn_ItemStyle -- 3464 * 3465 * Return the value of the -itemstyle config option for a column. 3466 * 3467 * Results: 3468 * TreeStyle or NULL. 3469 * 3470 * Side effects: 3471 * None. 3472 * 3473 *---------------------------------------------------------------------- 3474 */ 3475 3476TreeStyle 3477TreeColumn_ItemStyle( 3478 TreeColumn column /* Column token. */ 3479 ) 3480{ 3481 return column->itemStyle; 3482} 3483 3484/* 3485 *---------------------------------------------------------------------- 3486 * 3487 * TreeColumn_StyleDeleted -- 3488 * 3489 * Called when a master style is deleted. 3490 * 3491 * Results: 3492 * Clear the column's -itemstyle option if it is the style being 3493 * deleted. 3494 * 3495 * Side effects: 3496 * None. 3497 * 3498 *---------------------------------------------------------------------- 3499 */ 3500 3501void 3502TreeColumn_StyleDeleted( 3503 TreeColumn column, /* Column token. */ 3504 TreeStyle style /* Style that was deleted. */ 3505 ) 3506{ 3507 if (column->itemStyle == style) 3508 column->itemStyle = NULL; 3509} 3510 3511/* 3512 *---------------------------------------------------------------------- 3513 * 3514 * TreeColumn_Visible -- 3515 * 3516 * Return the value of the -visible config option for a column. 3517 * 3518 * Results: 3519 * Boolean value. 3520 * 3521 * Side effects: 3522 * None. 3523 * 3524 *---------------------------------------------------------------------- 3525 */ 3526 3527int 3528TreeColumn_Visible( 3529 TreeColumn column /* Column token. */ 3530 ) 3531{ 3532 return column->visible; 3533} 3534 3535/* 3536 *---------------------------------------------------------------------- 3537 * 3538 * TreeColumn_GetID -- 3539 * 3540 * Return the unique identifier for a column. 3541 * 3542 * Results: 3543 * Unique integer id. 3544 * 3545 * Side effects: 3546 * None. 3547 * 3548 *---------------------------------------------------------------------- 3549 */ 3550 3551int TreeColumn_GetID( 3552 TreeColumn column /* Column token. */ 3553 ) 3554{ 3555 return column->id; 3556} 3557 3558/* 3559 *---------------------------------------------------------------------- 3560 * 3561 * TreeColumn_Lock -- 3562 * 3563 * Return the value of the -lock option for a column. 3564 * 3565 * Results: 3566 * One of the COLUMN_LOCK_xxx constants. 3567 * 3568 * Side effects: 3569 * None. 3570 * 3571 *---------------------------------------------------------------------- 3572 */ 3573 3574int TreeColumn_Lock( 3575 TreeColumn column /* Column token. */ 3576 ) 3577{ 3578 return column->lock; 3579} 3580 3581/* 3582 *---------------------------------------------------------------------- 3583 * 3584 * TreeColumn_Index -- 3585 * 3586 * Return the 0-based index for a column. 3587 * 3588 * Results: 3589 * Position of the column in the list of columns. 3590 * 3591 * Side effects: 3592 * None. 3593 * 3594 *---------------------------------------------------------------------- 3595 */ 3596 3597int 3598TreeColumn_Index( 3599 TreeColumn column /* Column token. */ 3600 ) 3601{ 3602 return column->index; 3603} 3604 3605/* 3606 *---------------------------------------------------------------------- 3607 * 3608 * ColumnTagCmd -- 3609 * 3610 * This procedure is invoked to process the [column tag] widget 3611 * command. See the user documentation for details on what 3612 * it does. 3613 * 3614 * Results: 3615 * A standard Tcl result. 3616 * 3617 * Side effects: 3618 * See the user documentation. 3619 * 3620 *---------------------------------------------------------------------- 3621 */ 3622 3623static int 3624ColumnTagCmd( 3625 ClientData clientData, /* Widget info. */ 3626 Tcl_Interp *interp, /* Current interpreter. */ 3627 int objc, /* Number of arguments. */ 3628 Tcl_Obj *CONST objv[] /* Argument values. */ 3629 ) 3630{ 3631 TreeCtrl *tree = clientData; 3632 static CONST char *commandNames[] = { 3633 "add", "expr", "names", "remove", (char *) NULL 3634 }; 3635 enum { 3636 COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE 3637 }; 3638 int index; 3639 ColumnForEach iter; 3640 TreeColumnList columns; 3641 TreeColumn column; 3642 int result = TCL_OK; 3643 3644 if (objc < 4) { 3645 Tcl_WrongNumArgs(interp, 3, objv, "command ?arg arg ...?"); 3646 return TCL_ERROR; 3647 } 3648 3649 if (Tcl_GetIndexFromObj(interp, objv[3], commandNames, "command", 0, 3650 &index) != TCL_OK) { 3651 return TCL_ERROR; 3652 } 3653 3654 switch (index) { 3655 /* T column tag add C tagList */ 3656 case COMMAND_ADD: { 3657 int i, numTags; 3658 Tcl_Obj **listObjv; 3659 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; 3660 3661 if (objc != 6) { 3662 Tcl_WrongNumArgs(interp, 4, objv, "column tagList"); 3663 return TCL_ERROR; 3664 } 3665 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) { 3666 return TCL_ERROR; 3667 } 3668 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { 3669 result = TCL_ERROR; 3670 break; 3671 } 3672 STATIC_ALLOC(tags, Tk_Uid, numTags); 3673 for (i = 0; i < numTags; i++) { 3674 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); 3675 } 3676 COLUMN_FOR_EACH(column, &columns, NULL, &iter) { 3677 column->tagInfo = TagInfo_Add(tree, column->tagInfo, tags, numTags); 3678 } 3679 STATIC_FREE(tags, Tk_Uid, numTags); 3680 break; 3681 } 3682 3683 /* T column tag expr C tagExpr */ 3684 case COMMAND_EXPR: { 3685 TagExpr expr; 3686 int ok = TRUE; 3687 3688 if (objc != 6) { 3689 Tcl_WrongNumArgs(interp, 4, objv, "column tagExpr"); 3690 return TCL_ERROR; 3691 } 3692 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) { 3693 return TCL_ERROR; 3694 } 3695 if (TagExpr_Init(tree, objv[5], &expr) != TCL_OK) { 3696 result = TCL_ERROR; 3697 break; 3698 } 3699 COLUMN_FOR_EACH(column, &columns, NULL, &iter) { 3700 if (!TagExpr_Eval(&expr, column->tagInfo)) { 3701 ok = FALSE; 3702 break; 3703 } 3704 } 3705 TagExpr_Free(&expr); 3706 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok)); 3707 break; 3708 } 3709 3710 /* T column tag names C */ 3711 case COMMAND_NAMES: { 3712 Tcl_Obj *listObj; 3713 Tk_Uid *tags = NULL; 3714 int i, tagSpace, numTags = 0; 3715 3716 if (objc != 5) { 3717 Tcl_WrongNumArgs(interp, 4, objv, "column"); 3718 return TCL_ERROR; 3719 } 3720 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) { 3721 return TCL_ERROR; 3722 } 3723 COLUMN_FOR_EACH(column, &columns, NULL, &iter) { 3724 tags = TagInfo_Names(tree, column->tagInfo, tags, &numTags, &tagSpace); 3725 } 3726 if (numTags) { 3727 listObj = Tcl_NewListObj(0, NULL); 3728 for (i = 0; i < numTags; i++) { 3729 Tcl_ListObjAppendElement(NULL, listObj, 3730 Tcl_NewStringObj((char *) tags[i], -1)); 3731 } 3732 Tcl_SetObjResult(interp, listObj); 3733 ckfree((char *) tags); 3734 } 3735 break; 3736 } 3737 3738 /* T column tag remove C tagList */ 3739 case COMMAND_REMOVE: { 3740 int i, numTags; 3741 Tcl_Obj **listObjv; 3742 Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags; 3743 3744 if (objc != 6) { 3745 Tcl_WrongNumArgs(interp, 4, objv, "column tagList"); 3746 return TCL_ERROR; 3747 } 3748 if (TreeColumnList_FromObj(tree, objv[4], &columns, 0) != TCL_OK) { 3749 return TCL_ERROR; 3750 } 3751 if (Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) { 3752 result = TCL_ERROR; 3753 break; 3754 } 3755 STATIC_ALLOC(tags, Tk_Uid, numTags); 3756 for (i = 0; i < numTags; i++) { 3757 tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i])); 3758 } 3759 COLUMN_FOR_EACH(column, &columns, NULL, &iter) { 3760 column->tagInfo = TagInfo_Remove(tree, column->tagInfo, tags, numTags); 3761 } 3762 STATIC_FREE(tags, Tk_Uid, numTags); 3763 break; 3764 } 3765 } 3766 3767 TreeColumnList_Free(&columns); 3768 return result; 3769} 3770 3771/* 3772 *---------------------------------------------------------------------- 3773 * 3774 * TreeColumnCmd -- 3775 * 3776 * This procedure is invoked to process the [column] widget 3777 * command. See the user documentation for details on what it 3778 * does. 3779 * 3780 * Results: 3781 * A standard Tcl result. 3782 * 3783 * Side effects: 3784 * See the user documentation. 3785 * 3786 *---------------------------------------------------------------------- 3787 */ 3788 3789int 3790TreeColumnCmd( 3791 ClientData clientData, /* Widget info. */ 3792 Tcl_Interp *interp, /* Current interpreter. */ 3793 int objc, /* Number of arguments. */ 3794 Tcl_Obj *CONST objv[] /* Argument values. */ 3795 ) 3796{ 3797 TreeCtrl *tree = clientData; 3798 static CONST char *commandNames[] = { 3799 "bbox", "cget", "compare", "configure", "count", "create", "delete", 3800 "dragcget", "dragconfigure", "id", 3801#ifdef DEPRECATED 3802 "index", 3803#endif 3804 "list", "move", "neededwidth", "order", "tag", "width", (char *) NULL 3805 }; 3806 enum { 3807 COMMAND_BBOX, COMMAND_CGET, COMMAND_COMPARE, COMMAND_CONFIGURE, 3808 COMMAND_COUNT, COMMAND_CREATE, COMMAND_DELETE, COMMAND_DRAGCGET, 3809 COMMAND_DRAGCONF, COMMAND_ID, 3810#ifdef DEPRECATED 3811 COMMAND_INDEX, 3812#endif 3813 COMMAND_LIST, COMMAND_MOVE, COMMAND_NEEDEDWIDTH, COMMAND_ORDER, 3814 COMMAND_TAG, COMMAND_WIDTH 3815 }; 3816 int index; 3817 TreeColumnList columns; 3818 TreeColumn column; 3819 ColumnForEach citer; 3820 3821 if (objc < 3) { 3822 Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?"); 3823 return TCL_ERROR; 3824 } 3825 3826 if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0, 3827 &index) != TCL_OK) { 3828 return TCL_ERROR; 3829 } 3830 3831 TreeColumnList_Init(tree, &columns, 0); 3832 3833 switch (index) { 3834 case COMMAND_BBOX: { 3835 int left, top, width, height; 3836 3837 if (objc != 4) { 3838 Tcl_WrongNumArgs(interp, 3, objv, "column"); 3839 return TCL_ERROR; 3840 } 3841 if (TreeColumn_FromObj(tree, objv[3], &column, 3842 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 3843 return TCL_ERROR; 3844 if (TreeColumn_Bbox(column, &left, &top, &width, &height) < 0) 3845 break; 3846 FormatResult(interp, "%d %d %d %d", 3847 left, top, left + width, top + height); 3848 break; 3849 } 3850 3851 case COMMAND_CGET: { 3852 TreeColumn column; 3853 Tcl_Obj *resultObjPtr; 3854 3855 if (objc != 5) { 3856 Tcl_WrongNumArgs(interp, 3, objv, "column option"); 3857 return TCL_ERROR; 3858 } 3859 if (TreeColumn_FromObj(tree, objv[3], &column, 3860 CFO_NOT_NULL) != TCL_OK) 3861 return TCL_ERROR; 3862 resultObjPtr = Tk_GetOptionValue(interp, (char *) column, 3863 column->optionTable, objv[4], tree->tkwin); 3864 if (resultObjPtr == NULL) 3865 return TCL_ERROR; 3866 Tcl_SetObjResult(interp, resultObjPtr); 3867 break; 3868 } 3869 3870 /* T column compare C op C */ 3871 case COMMAND_COMPARE: { 3872 TreeColumn column1, column2; 3873 static CONST char *opName[] = { "<", "<=", "==", ">=", ">", "!=", NULL }; 3874 int op, compare = 0, index1, index2; 3875 3876 if (objc != 6) { 3877 Tcl_WrongNumArgs(interp, 3, objv, "column1 op column2"); 3878 return TCL_ERROR; 3879 } 3880 if (TreeColumn_FromObj(tree, objv[3], &column1, 3881 CFO_NOT_NULL) != TCL_OK) 3882 return TCL_ERROR; 3883 if (Tcl_GetIndexFromObj(interp, objv[4], opName, 3884 "comparison operator", 0, &op) != TCL_OK) 3885 return TCL_ERROR; 3886 if (TreeColumn_FromObj(tree, objv[5], &column2, 3887 CFO_NOT_NULL) != TCL_OK) 3888 return TCL_ERROR; 3889 index1 = TreeColumn_Index(column1); 3890 index2 = TreeColumn_Index(column2); 3891 switch (op) { 3892 case 0: compare = index1 < index2; break; 3893 case 1: compare = index1 <= index2; break; 3894 case 2: compare = index1 == index2; break; 3895 case 3: compare = index1 >= index2; break; 3896 case 4: compare = index1 > index2; break; 3897 case 5: compare = index1 != index2; break; 3898 } 3899 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare)); 3900 break; 3901 } 3902 3903 case COMMAND_CONFIGURE: { 3904 if (objc < 4) { 3905 Tcl_WrongNumArgs(interp, 3, objv, "column ?option? ?value? ?option value ...?"); 3906 return TCL_ERROR; 3907 } 3908 if (objc <= 5) { 3909 Tcl_Obj *resultObjPtr; 3910 if (TreeColumn_FromObj(tree, objv[3], &column, 3911 CFO_NOT_NULL) != TCL_OK) 3912 return TCL_ERROR; 3913 resultObjPtr = Tk_GetOptionInfo(interp, (char *) column, 3914 column->optionTable, 3915 (objc == 4) ? (Tcl_Obj *) NULL : objv[4], 3916 tree->tkwin); 3917 if (resultObjPtr == NULL) 3918 goto errorExit; 3919 Tcl_SetObjResult(interp, resultObjPtr); 3920 break; 3921 } 3922 /* If "all" is specified, get a list of columns instead of 3923 * COLUMN_ALL, since changing the -lock option of a column 3924 * may reorder columns. */ 3925 if (TreeColumnList_FromObj(tree, objv[3], &columns, 3926 CFO_LIST_ALL | CFO_NOT_NULL) != TCL_OK) 3927 return TCL_ERROR; 3928 COLUMN_FOR_EACH(column, &columns, NULL, &citer) { 3929 if (Column_Config(column, objc - 4, objv + 4, FALSE) != TCL_OK) 3930 goto errorExit; 3931 } 3932 break; 3933 } 3934 3935 case COMMAND_CREATE: { 3936 TreeColumn column, last = tree->columnLast; 3937 3938 /* FIXME: -count N -tags $tags */ 3939 column = Column_Alloc(tree); 3940 if (Column_Config(column, objc - 3, objv + 3, TRUE) != TCL_OK) { 3941 Column_Free(column); 3942 return TCL_ERROR; 3943 } 3944 3945 if (tree->columns == NULL) { 3946 column->index = 0; 3947 tree->columns = column; 3948 } else { 3949 last->next = column; 3950 column->prev = last; 3951 column->index = last->index + 1; 3952 } 3953 tree->columnLast = column; 3954 tree->columnTail->index++; 3955 3956 { 3957 TreeColumn before = NULL; 3958 switch (column->lock) { 3959 case COLUMN_LOCK_LEFT: 3960 before = tree->columnLockNone; 3961 if (before == NULL) 3962 before = tree->columnLockRight; 3963 break; 3964 case COLUMN_LOCK_NONE: 3965 before = tree->columnLockRight; 3966 break; 3967 case COLUMN_LOCK_RIGHT: 3968 before = NULL; 3969 break; 3970 } 3971 if (before == NULL) 3972 before = tree->columnTail; 3973 Column_Move(column, before); 3974 } 3975 3976 /* Indicate that all items must recalculate their list of spans. */ 3977 TreeItem_SpansInvalidate(tree, NULL); 3978 3979 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH); 3980 Tcl_SetObjResult(interp, TreeColumn_ToObj(tree, column)); 3981 break; 3982 } 3983 3984 /* T column delete first ?last? */ 3985 case COMMAND_DELETE: { 3986 TreeColumnList column2s; 3987 TreeColumn prev, next; 3988 int flags = CFO_NOT_NULL | CFO_NOT_TAIL; 3989 TreeItem item; 3990 Tcl_HashEntry *hPtr; 3991 Tcl_HashSearch search; 3992 int index; 3993 3994 if (objc < 4 || objc > 5) { 3995 Tcl_WrongNumArgs(interp, 3, objv, "first ?last?"); 3996 return TCL_ERROR; 3997 } 3998 if (objc == 5) 3999 flags |= CFO_NOT_MANY; 4000 if (TreeColumnList_FromObj(tree, objv[3], &columns, 4001 flags) != TCL_OK) 4002 goto errorExit; 4003 if (objc == 5) { 4004 if (TreeColumnList_FromObj(tree, objv[4], &column2s, 4005 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 4006 goto errorExit; 4007 } 4008 COLUMN_FOR_EACH(column, &columns, (objc == 5) ? &column2s : NULL, 4009 &citer) { 4010 /* T column delete "all" */ 4011 if (citer.all) { 4012 column = tree->columns; 4013 while (column != NULL) { 4014 TreeDisplay_ColumnDeleted(tree, column); 4015 column = Column_Free(column); 4016 } 4017 tree->columnTail->index = 0; 4018 tree->columns = NULL; 4019 tree->columnLast = NULL; 4020 tree->columnLockLeft = NULL; 4021 tree->columnLockNone = NULL; 4022 tree->columnLockRight = NULL; 4023 4024 /* Delete all TreeItemColumns */ 4025 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 4026 while (hPtr != NULL) { 4027 item = (TreeItem) Tcl_GetHashValue(hPtr); 4028 TreeItem_RemoveAllColumns(tree, item); 4029 hPtr = Tcl_NextHashEntry(&search); 4030 } 4031 4032 tree->columnTree = NULL; 4033 tree->columnDrag.column = tree->columnDrag.indColumn = NULL; 4034 tree->widthOfColumns = tree->headerHeight = -1; 4035 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 4036 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH); 4037 goto doneDELETE; 4038 } 4039 4040 /* Delete all TreeItemColumns */ 4041 hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search); 4042 while (hPtr != NULL) { 4043 item = (TreeItem) Tcl_GetHashValue(hPtr); 4044 TreeItem_RemoveColumns(tree, item, column->index, 4045 column->index); 4046 hPtr = Tcl_NextHashEntry(&search); 4047 } 4048 4049 TreeDisplay_ColumnDeleted(tree, column); 4050 4051 /* Unlink. */ 4052 prev = column->prev; 4053 next = column->next; 4054 if (prev == NULL) 4055 tree->columns = next; 4056 else 4057 prev->next = next; 4058 if (next == NULL) 4059 tree->columnLast = prev; 4060 else 4061 next->prev = prev; 4062 4063 if (column == tree->columnTree) 4064 tree->columnTree = NULL; 4065 if (column == tree->columnDrag.column) 4066 tree->columnDrag.column = NULL; 4067 if (column == tree->columnDrag.indColumn) 4068 tree->columnDrag.indColumn = NULL; 4069 4070 (void) Column_Free(column); 4071 4072 /* Renumber trailing columns */ 4073 column = next; 4074 while (column != NULL) { 4075 column->index--; 4076 column = column->next; 4077 } 4078 } 4079 4080 tree->columnLockLeft = NULL; 4081 tree->columnLockNone = NULL; 4082 tree->columnLockRight = NULL; 4083 4084 index = 0; 4085 column = tree->columns; 4086 while (column != NULL) { 4087 column->index = index++; 4088 if (column->lock == COLUMN_LOCK_LEFT && tree->columnLockLeft == NULL) 4089 tree->columnLockLeft = column; 4090 if (column->lock == COLUMN_LOCK_NONE && tree->columnLockNone == NULL) 4091 tree->columnLockNone = column; 4092 if (column->lock == COLUMN_LOCK_RIGHT && tree->columnLockRight == NULL) 4093 tree->columnLockRight = column; 4094 column = column->next; 4095 } 4096 tree->columnTail->index = index; 4097 4098 tree->widthOfColumns = tree->headerHeight = -1; 4099 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 4100 Tree_DInfoChanged(tree, DINFO_REDO_COLUMN_WIDTH); 4101 4102doneDELETE: 4103 /* Indicate that all items must recalculate their list of spans. */ 4104 TreeItem_SpansInvalidate(tree, NULL); 4105 4106 if (objc == 5) 4107 TreeColumnList_Free(&column2s); 4108 break; 4109 } 4110 4111 /* T column dragcget option */ 4112 case COMMAND_DRAGCGET: { 4113 Tcl_Obj *resultObjPtr; 4114 4115 if (objc != 4) { 4116 Tcl_WrongNumArgs(interp, 3, objv, "option"); 4117 return TCL_ERROR; 4118 } 4119 resultObjPtr = Tk_GetOptionValue(interp, (char *) tree, 4120 tree->columnDrag.optionTable, objv[3], tree->tkwin); 4121 if (resultObjPtr == NULL) 4122 return TCL_ERROR; 4123 Tcl_SetObjResult(interp, resultObjPtr); 4124 break; 4125 } 4126 4127 /* T column dragconfigure ?option? ?value? ?option value ...? */ 4128 case COMMAND_DRAGCONF: { 4129 Tcl_Obj *resultObjPtr; 4130 Tk_SavedOptions savedOptions; 4131 int mask, result; 4132 4133 if (objc < 3) { 4134 Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?"); 4135 return TCL_ERROR; 4136 } 4137 if (objc <= 4) { 4138 resultObjPtr = Tk_GetOptionInfo(interp, (char *) tree, 4139 tree->columnDrag.optionTable, 4140 (objc == 3) ? (Tcl_Obj *) NULL : objv[3], 4141 tree->tkwin); 4142 if (resultObjPtr == NULL) 4143 return TCL_ERROR; 4144 Tcl_SetObjResult(interp, resultObjPtr); 4145 break; 4146 } 4147 result = Tk_SetOptions(interp, (char *) tree, 4148 tree->columnDrag.optionTable, objc - 3, objv + 3, tree->tkwin, 4149 &savedOptions, &mask); 4150 if (result != TCL_OK) { 4151 Tk_RestoreSavedOptions(&savedOptions); 4152 return TCL_ERROR; 4153 } 4154 Tk_FreeSavedOptions(&savedOptions); 4155 4156 if (tree->columnDrag.alpha < 0) 4157 tree->columnDrag.alpha = 0; 4158 if (tree->columnDrag.alpha > 255) 4159 tree->columnDrag.alpha = 255; 4160 4161 Tree_DInfoChanged(tree, DINFO_DRAW_HEADER); 4162 break; 4163 } 4164 4165 case COMMAND_COUNT: { 4166 int count = tree->columnCount; 4167 4168 if (objc < 3 || objc > 4) { 4169 Tcl_WrongNumArgs(interp, 3, objv, "?columnDesc?"); 4170 return TCL_ERROR; 4171 } 4172 if (objc == 4) { 4173 if (TreeColumnList_FromObj(tree, objv[3], &columns, 0) 4174 != TCL_OK) 4175 return TCL_ERROR; 4176 count = 0; 4177 COLUMN_FOR_EACH(column, &columns, NULL, &citer) { 4178 count++; 4179 } 4180 } 4181 Tcl_SetObjResult(interp, Tcl_NewIntObj(count)); 4182 break; 4183 } 4184 4185 case COMMAND_WIDTH: { 4186 if (objc != 4) { 4187 Tcl_WrongNumArgs(interp, 3, objv, "column"); 4188 return TCL_ERROR; 4189 } 4190 if (TreeColumn_FromObj(tree, objv[3], &column, 4191 CFO_NOT_NULL) != TCL_OK) 4192 return TCL_ERROR; 4193 4194 /* Update layout if needed */ 4195 (void) Tree_WidthOfColumns(tree); 4196 Tcl_SetObjResult(interp, Tcl_NewIntObj(column->useWidth)); 4197 break; 4198 } 4199 4200 case COMMAND_ID: 4201#ifdef DEPRECATED 4202 case COMMAND_INDEX: 4203#endif 4204 { 4205 Tcl_Obj *listObj; 4206 4207 if (objc != 4) { 4208 Tcl_WrongNumArgs(interp, 3, objv, "column"); 4209 return TCL_ERROR; 4210 } 4211 if (TreeColumnList_FromObj(tree, objv[3], &columns, 0) != TCL_OK) 4212 return TCL_ERROR; 4213 listObj = Tcl_NewListObj(0, NULL); 4214 COLUMN_FOR_EACH(column, &columns, NULL, &citer) { 4215 Tcl_ListObjAppendElement(interp, listObj, 4216 TreeColumn_ToObj(tree, column)); 4217 } 4218 Tcl_SetObjResult(interp, listObj); 4219 break; 4220 } 4221 4222 /* T column list ?-visible? */ 4223 case COMMAND_LIST: { 4224 TreeColumn column = tree->columns; 4225 Tcl_Obj *listObj; 4226 int visible = FALSE; 4227 4228 if (objc > 4) { 4229 Tcl_WrongNumArgs(interp, 3, objv, "?-visible?"); 4230 return TCL_ERROR; 4231 } 4232 if (objc == 4) { 4233 int len; 4234 char *s = Tcl_GetStringFromObj(objv[3], &len); 4235 if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0)) 4236 visible = TRUE; 4237 else { 4238 FormatResult(interp, "bad switch \"%s\": must be -visible", 4239 s); 4240 return TCL_ERROR; 4241 } 4242 } 4243 listObj = Tcl_NewListObj(0, NULL); 4244 while (column != NULL) { 4245 if (!visible || column->visible) 4246 Tcl_ListObjAppendElement(interp, listObj, 4247 TreeColumn_ToObj(tree, column)); 4248 column = column->next; 4249 } 4250 Tcl_SetObjResult(interp, listObj); 4251 break; 4252 } 4253 4254 /* T column move C before */ 4255 case COMMAND_MOVE: { 4256 TreeColumn move, before; 4257 TreeColumn first = NULL, last = tree->columnTail; 4258 4259 if (objc != 5) { 4260 Tcl_WrongNumArgs(interp, 3, objv, "column before"); 4261 return TCL_ERROR; 4262 } 4263 if (TreeColumn_FromObj(tree, objv[3], &move, 4264 CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) 4265 return TCL_ERROR; 4266 if (TreeColumn_FromObj(tree, objv[4], &before, 4267 CFO_NOT_NULL) != TCL_OK) 4268 return TCL_ERROR; 4269 4270 if ((move == before) || (move->index == before->index - 1)) 4271 break; 4272 switch (move->lock) { 4273 case COLUMN_LOCK_LEFT: 4274 first = tree->columnLockLeft; 4275 if (tree->columnLockNone != NULL) 4276 last = tree->columnLockNone; 4277 else if (tree->columnLockRight != NULL) 4278 last = tree->columnLockRight; 4279 break; 4280 case COLUMN_LOCK_NONE: 4281 first = tree->columnLockNone; 4282 if (tree->columnLockRight != NULL) 4283 last = tree->columnLockRight; 4284 break; 4285 case COLUMN_LOCK_RIGHT: 4286 first = tree->columnLockRight; 4287 break; 4288 } 4289 if (before->index < first->index || before->index > last->index) { 4290 FormatResult(tree->interp, 4291 "column %s%d and column %s%d -lock options conflict", 4292 tree->columnPrefix, move->id, 4293 tree->columnPrefix, before->id); 4294 return TCL_ERROR; 4295 } 4296 Column_Move(move, before); 4297 4298 /* Indicate that all items must recalculate their list of spans. */ 4299 TreeItem_SpansInvalidate(tree, NULL); 4300 break; 4301 } 4302 4303 case COMMAND_NEEDEDWIDTH: { 4304 TreeColumn column; 4305 int width; 4306 4307 if (objc != 4) { 4308 Tcl_WrongNumArgs(interp, 3, objv, "column"); 4309 return TCL_ERROR; 4310 } 4311 if (TreeColumn_FromObj(tree, objv[3], &column, 4312 CFO_NOT_NULL) != TCL_OK) 4313 return TCL_ERROR; 4314 4315 /* Update layout if needed */ 4316 (void) Tree_TotalWidth(tree); 4317 width = TreeColumn_WidthOfItems(column); 4318 width = MAX(width, TreeColumn_NeededWidth(column)); 4319 Tcl_SetObjResult(interp, Tcl_NewIntObj(width)); 4320 break; 4321 } 4322 4323 /* T column order C ?-visible? */ 4324 case COMMAND_ORDER: { 4325 TreeColumn column; 4326 int visible = FALSE; 4327 int index = 0; 4328 4329 if (objc < 4 || objc > 5) { 4330 Tcl_WrongNumArgs(interp, 3, objv, "column ?-visible?"); 4331 return TCL_ERROR; 4332 } 4333 if (objc == 5) { 4334 int len; 4335 char *s = Tcl_GetStringFromObj(objv[4], &len); 4336 if ((s[0] == '-') && (strncmp(s, "-visible", len) == 0)) 4337 visible = TRUE; 4338 else { 4339 FormatResult(interp, "bad switch \"%s\": must be -visible", 4340 s); 4341 return TCL_ERROR; 4342 } 4343 } 4344 if (TreeColumn_FromObj(tree, objv[3], &column, 4345 CFO_NOT_NULL) != TCL_OK) 4346 return TCL_ERROR; 4347 if (visible) { 4348 TreeColumn walk = tree->columns; 4349 while (walk != NULL) { 4350 if (walk == column) 4351 break; 4352 if (walk->visible) 4353 index++; 4354 walk = walk->next; 4355 } 4356 if (!column->visible) 4357 index = -1; 4358 } else { 4359 index = column->index; 4360 } 4361 Tcl_SetObjResult(interp, Tcl_NewIntObj(index)); 4362 break; 4363 } 4364 4365 case COMMAND_TAG: { 4366 return ColumnTagCmd(clientData, interp, objc, objv); 4367 } 4368 } 4369 4370 TreeColumnList_Free(&columns); 4371 return TCL_OK; 4372 4373errorExit: 4374 TreeColumnList_Free(&columns); 4375 return TCL_ERROR; 4376} 4377 4378/* 4379 *---------------------------------------------------------------------- 4380 * 4381 * Column_DrawArrow -- 4382 * 4383 * Draw the sort arrow for a column. 4384 * 4385 * Results: 4386 * None. 4387 * 4388 * Side effects: 4389 * Stuff is drawn in a drawable. 4390 * 4391 *---------------------------------------------------------------------- 4392 */ 4393 4394static void 4395Column_DrawArrow( 4396 TreeColumn column, /* Column record. */ 4397 TreeDrawable td, /* Where to draw. */ 4398 int x, int y, /* Top-left corner of the column's header. */ 4399 struct Layout layout /* Size/position info. */ 4400 ) 4401{ 4402 TreeCtrl *tree = column->tree; 4403 int height = tree->headerHeight; 4404 int sunken = column->state == COLUMN_STATE_PRESSED; 4405 Tk_Image image = NULL; 4406 Pixmap bitmap; 4407 Tk_3DBorder border; 4408 int state = Column_MakeState(column); 4409 int arrowPadY = column->arrowPadY[PAD_TOP_LEFT] + 4410 column->arrowPadY[PAD_BOTTOM_RIGHT]; 4411 4412 if (column->arrow == ARROW_NONE) 4413 return; 4414 4415 image = PerStateImage_ForState(tree, &column->arrowImage, state, NULL); 4416 if (image != NULL) { 4417 Tree_RedrawImage(image, 0, 0, layout.arrowWidth, layout.arrowHeight, 4418 td, 4419 x + layout.arrowLeft + sunken, 4420 y + (height - (layout.arrowHeight + arrowPadY)) / 2 + sunken); 4421 return; 4422 } 4423 4424 bitmap = PerStateBitmap_ForState(tree, &column->arrowBitmap, state, NULL); 4425 if (bitmap != None) { 4426 int bx, by; 4427 bx = x + layout.arrowLeft + sunken; 4428 by = y + (height - (layout.arrowHeight + arrowPadY)) / 2 + sunken; 4429 Tree_DrawBitmap(tree, bitmap, td.drawable, NULL, NULL, 4430 0, 0, 4431 (unsigned int) layout.arrowWidth, (unsigned int) layout.arrowHeight, 4432 bx, by); 4433 return; 4434 } 4435 4436 if (tree->useTheme) { 4437 if (TreeTheme_DrawHeaderArrow(tree, td.drawable, 4438 column->arrow == ARROW_UP, x + layout.arrowLeft + sunken, 4439 y + (height - (layout.arrowHeight + arrowPadY)) / 2 + sunken, 4440 layout.arrowWidth, layout.arrowHeight) == TCL_OK) 4441 return; 4442 } 4443 4444 if (1) { 4445 int arrowWidth = layout.arrowWidth; 4446 int arrowHeight = layout.arrowHeight; 4447 int arrowTop = y + (height - (layout.arrowHeight + arrowPadY)) / 2 + column->arrowPadY[PAD_TOP_LEFT]; 4448 int arrowBottom = arrowTop + arrowHeight; 4449 XPoint points[5]; 4450 int color1 = 0, color2 = 0; 4451 int i; 4452 4453 switch (column->arrow) { 4454 case ARROW_UP: 4455 points[0].x = x + layout.arrowLeft; 4456 points[0].y = arrowBottom - 1; 4457 points[1].x = x + layout.arrowLeft + arrowWidth / 2; 4458 points[1].y = arrowTop - 1; 4459 color1 = TK_3D_DARK_GC; 4460 points[4].x = x + layout.arrowLeft + arrowWidth / 2; 4461 points[4].y = arrowTop - 1; 4462 points[3].x = x + layout.arrowLeft + arrowWidth - 1; 4463 points[3].y = arrowBottom - 1; 4464 points[2].x = x + layout.arrowLeft; 4465 points[2].y = arrowBottom - 1; 4466 color2 = TK_3D_LIGHT_GC; 4467 break; 4468 case ARROW_DOWN: 4469 points[0].x = x + layout.arrowLeft + arrowWidth - 1; 4470 points[0].y = arrowTop; 4471 points[1].x = x + layout.arrowLeft + arrowWidth / 2; 4472 points[1].y = arrowBottom; 4473 color1 = TK_3D_LIGHT_GC; 4474 points[2].x = x + layout.arrowLeft + arrowWidth - 1; 4475 points[2].y = arrowTop; 4476 points[3].x = x + layout.arrowLeft; 4477 points[3].y = arrowTop; 4478 points[4].x = x + layout.arrowLeft + arrowWidth / 2; 4479 points[4].y = arrowBottom; 4480 color2 = TK_3D_DARK_GC; 4481 break; 4482 } 4483 for (i = 0; i < 5; i++) { 4484 points[i].x += sunken; 4485 points[i].y += sunken; 4486 } 4487 4488 border = PerStateBorder_ForState(tree, &column->border, state, NULL); 4489 if (border == NULL) 4490 border = tree->border; 4491 XDrawLines(tree->display, td.drawable, 4492 Tk_3DBorderGC(tree->tkwin, border, color2), 4493 points + 2, 3, CoordModeOrigin); 4494 XDrawLines(tree->display, td.drawable, 4495 Tk_3DBorderGC(tree->tkwin, border, color1), 4496 points, 2, CoordModeOrigin); 4497 } 4498} 4499 4500/* 4501 *---------------------------------------------------------------------- 4502 * 4503 * Column_Draw -- 4504 * 4505 * Draw the header for a column. 4506 * 4507 * Results: 4508 * None. 4509 * 4510 * Side effects: 4511 * Stuff is drawn in a drawable. 4512 * 4513 *---------------------------------------------------------------------- 4514 */ 4515 4516static void 4517Column_Draw( 4518 TreeColumn column, /* Column record. */ 4519 TreeDrawable td, /* Where to draw. */ 4520 int x, int y, /* Top-left corner of the column's header. */ 4521 int visIndex, /* 0-based index in the list of visible 4522 * columns. */ 4523 int dragImage /* TRUE if we are creating a transparent 4524 * drag image for this header. */ 4525 ) 4526{ 4527 TreeCtrl *tree = column->tree; 4528 int height = tree->headerHeight; 4529 struct Layout layout; 4530 int width = column->useWidth; 4531 int sunken = column->state == COLUMN_STATE_PRESSED; 4532 int relief = sunken ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED; 4533 Tk_3DBorder border; 4534 int theme = TCL_ERROR; 4535 4536 layout.width = width; 4537 layout.height = height; 4538 Column_DoLayout(column, &layout); 4539 4540 border = PerStateBorder_ForState(tree, &column->border, 4541 Column_MakeState(column), NULL); 4542 if (border == NULL) 4543 border = tree->border; 4544 4545 if (dragImage) { 4546 GC gc = Tk_GCForColor(tree->columnDrag.color, Tk_WindowId(tree->tkwin)); 4547 XFillRectangle(tree->display, td.drawable, gc, x, y, width, height); 4548 } else { 4549 if (tree->useTheme) { 4550 theme = TreeTheme_DrawHeaderItem(tree, td.drawable, column->state, 4551 column->arrow, visIndex, x, y, width, height); 4552 } 4553 if (theme != TCL_OK) 4554 Tk_Fill3DRectangle(tree->tkwin, td.drawable, border, 4555 x, y, width, height, 0, TK_RELIEF_FLAT); 4556 } 4557 4558 if (column->image != NULL) { 4559 int imgW, imgH, ix, iy, h; 4560 Tk_SizeOfImage(column->image, &imgW, &imgH); 4561 ix = x + layout.imageLeft + sunken; 4562 h = column->imagePadY[PAD_TOP_LEFT] + imgH 4563 + column->imagePadY[PAD_BOTTOM_RIGHT]; 4564 iy = y + (height - h) / 2 + sunken; 4565 iy += column->imagePadY[PAD_TOP_LEFT]; 4566 Tree_RedrawImage(column->image, 0, 0, imgW, imgH, td, ix, iy); 4567 } else if (column->bitmap != None) { 4568 int imgW, imgH, bx, by, h; 4569 4570 Tk_SizeOfBitmap(tree->display, column->bitmap, &imgW, &imgH); 4571 bx = x + layout.imageLeft + sunken; 4572 h = column->imagePadY[PAD_TOP_LEFT] + imgH 4573 + column->imagePadY[PAD_BOTTOM_RIGHT]; 4574 by = y + (height - h) / 2 + sunken; 4575 by += column->imagePadY[PAD_TOP_LEFT]; 4576 Tree_DrawBitmapWithGC(tree, column->bitmap, td.drawable, column->bitmapGC, 4577 0, 0, (unsigned int) imgW, (unsigned int) imgH, 4578 bx, by); 4579 } 4580 4581 if ((column->text != NULL) && (column->textLayout != NULL)) { 4582 int h; 4583 XGCValues gcValues; 4584 GC gc; 4585 unsigned long mask; 4586 TextLayout_Size(column->textLayout, NULL, &h); 4587 h += column->textPadY[PAD_TOP_LEFT] + column->textPadY[PAD_BOTTOM_RIGHT]; 4588 gcValues.font = Tk_FontId(column->tkfont ? column->tkfont : tree->tkfont); 4589 gcValues.foreground = column->textColor->pixel; 4590 gcValues.graphics_exposures = False; 4591 mask = GCFont | GCForeground | GCGraphicsExposures; 4592 gc = Tree_GetGC(tree, mask, &gcValues); 4593 TextLayout_Draw(tree->display, td.drawable, gc, 4594 column->textLayout, 4595 x + layout.textLeft + sunken, 4596 y + (height - h) / 2 + column->textPadY[PAD_TOP_LEFT] + sunken, 4597 0, -1, -1); 4598 } else if ((column->text != NULL) && (layout.bytesThatFit != 0)) { 4599 XGCValues gcValues; 4600 GC gc; 4601 unsigned long mask; 4602 char staticStr[256], *text = staticStr; 4603 int textLen = column->textLen; 4604 char *ellipsis = "..."; 4605 int ellipsisLen = strlen(ellipsis); 4606 int tx, ty, h; 4607 4608 if (textLen + ellipsisLen > sizeof(staticStr)) 4609 text = ckalloc(textLen + ellipsisLen); 4610 memcpy(text, column->text, textLen); 4611 if (layout.bytesThatFit != textLen) { 4612 textLen = abs(layout.bytesThatFit); 4613 if (layout.bytesThatFit > 0) { 4614 memcpy(text + layout.bytesThatFit, ellipsis, ellipsisLen); 4615 textLen += ellipsisLen; 4616 } 4617 } 4618 4619 gcValues.font = Tk_FontId(layout.tkfont); 4620 gcValues.foreground = column->textColor->pixel; 4621 gcValues.graphics_exposures = False; 4622 mask = GCFont | GCForeground | GCGraphicsExposures; 4623 gc = Tree_GetGC(tree, mask, &gcValues); 4624 tx = x + layout.textLeft + sunken; 4625 h = column->textPadY[PAD_TOP_LEFT] + layout.fm.linespace 4626 + column->textPadY[PAD_BOTTOM_RIGHT]; 4627 ty = y + (height - h) / 2 + layout.fm.ascent + sunken; 4628 ty += column->textPadY[PAD_TOP_LEFT]; 4629 Tk_DrawChars(tree->display, td.drawable, gc, 4630 layout.tkfont, text, textLen, tx, ty); 4631 if (text != staticStr) 4632 ckfree(text); 4633 } 4634 4635 if (dragImage) 4636 return; 4637 4638#if defined(MAC_OSX_TK) 4639 /* Under Aqua, we let the Appearance Manager draw the sort arrow */ 4640 if (theme != TCL_OK) 4641#endif 4642 Column_DrawArrow(column, td, x, y, layout); 4643 4644 if (theme != TCL_OK) 4645 Tk_Draw3DRectangle(tree->tkwin, td.drawable, border, 4646 x, y, width, height, column->borderWidth, relief); 4647} 4648 4649/* 4650 *---------------------------------------------------------------------- 4651 * 4652 * SetImageForColumn -- 4653 * 4654 * Set a photo image containing a simplified picture of the header 4655 * of a column. This image is used when dragging and dropping a column 4656 * header. 4657 * 4658 * Results: 4659 * Token for a photo image, or NULL if the image could not be 4660 * created. 4661 * 4662 * Side effects: 4663 * A photo image called "::TreeCtrl::ImageColumn" will be created if 4664 * it doesn't exist. The image is set to contain a picture of the 4665 * column header. 4666 * 4667 *---------------------------------------------------------------------- 4668 */ 4669 4670static Tk_Image 4671SetImageForColumn( 4672 TreeCtrl *tree, /* Widget info. */ 4673 TreeColumn column /* Column record. */ 4674 ) 4675{ 4676 Tk_PhotoHandle photoH; 4677 TreeDrawable td; 4678 int width = column->useWidth; /* the entire column, not just what is visible */ 4679 int height = tree->headerHeight; 4680 XImage *ximage; 4681 4682 photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageColumn"); 4683 if (photoH == NULL) { 4684 Tcl_GlobalEval(tree->interp, "image create photo ::TreeCtrl::ImageColumn"); 4685 photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageColumn"); 4686 if (photoH == NULL) 4687 return NULL; 4688 } 4689 4690 td.width = width; 4691 td.height = height; 4692 td.drawable = Tk_GetPixmap(tree->display, Tk_WindowId(tree->tkwin), 4693 width, height, Tk_Depth(tree->tkwin)); 4694 4695 Column_Draw(column, td, 0, 0, 0, TRUE); 4696 4697 /* Pixmap -> XImage */ 4698 ximage = XGetImage(tree->display, td.drawable, 0, 0, 4699 (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); 4700 if (ximage == NULL) 4701 panic("tkTreeColumn.c:SetImageForColumn() ximage is NULL"); 4702 4703 /* XImage -> Tk_Image */ 4704 Tree_XImage2Photo(tree->interp, photoH, ximage, 0, tree->columnDrag.alpha); 4705 4706 XDestroyImage(ximage); 4707 Tk_FreePixmap(tree->display, td.drawable); 4708 4709 return Tk_GetImage(tree->interp, tree->tkwin, "::TreeCtrl::ImageColumn", 4710 NULL, (ClientData) NULL); 4711} 4712 4713static void 4714DrawDragIndicator( 4715 TreeCtrl *tree, /* Widget info. */ 4716 Drawable drawable, /* Where to draw. */ 4717 int lock 4718 ) 4719{ 4720 TreeColumn column = tree->columnDrag.indColumn; 4721 int x, y, w, h; 4722 int minX = 0, maxX = 0; 4723 GC gc; 4724 4725 if ((column == NULL) || (column->lock != lock)) 4726 return; 4727 4728 switch (lock) { 4729 case COLUMN_LOCK_LEFT: 4730 minX = Tree_HeaderLeft(tree); 4731 maxX = Tree_ContentLeft(tree); 4732 break; 4733 case COLUMN_LOCK_NONE: 4734 minX = Tree_ContentLeft(tree); 4735 maxX = Tree_ContentRight(tree); 4736 break; 4737 case COLUMN_LOCK_RIGHT: 4738 minX = Tree_ContentRight(tree); 4739 maxX = Tree_HeaderRight(tree); 4740 break; 4741 } 4742 4743 if (TreeColumn_Bbox(column, &x, &y, &w, &h) == 0) { 4744 if (tree->columnDrag.indSide == SIDE_LEFT) { 4745 x -= 1; 4746 if (x == minX - 1) 4747 x += 1; 4748 } else { 4749 x += w - 1; 4750 if (x == maxX - 1) 4751 x -= 1; 4752 } 4753 gc = Tk_GCForColor(tree->columnDrag.indColor, Tk_WindowId(tree->tkwin)); 4754 XFillRectangle(tree->display, drawable, gc, 4755 x, y, 2, tree->headerHeight); 4756 } 4757} 4758 4759static void 4760DrawHeaderLeft( 4761 TreeCtrl *tree, /* Widget info. */ 4762 TreeDrawable td /* Where to draw. */ 4763 ) 4764{ 4765 TreeColumn column = tree->columnLockLeft; 4766 Tk_Window tkwin = tree->tkwin; 4767 int x = Tree_HeaderLeft(tree), y = Tree_HeaderTop(tree); 4768 int height = tree->headerHeight; 4769 TreeDrawable td2; 4770 int visIndex = 0; 4771 4772 td2.width = Tk_Width(tkwin); 4773 td2.height = Tree_HeaderBottom(tree); 4774 td2.drawable = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), 4775 td2.width, td2.height, Tk_Depth(tkwin)); 4776 4777 while (column != NULL && column->lock == COLUMN_LOCK_LEFT) { 4778 if (column->visible) { 4779 Column_Draw(column, td2, x, y, visIndex++, FALSE); 4780 x += column->useWidth; 4781 } 4782 column = column->next; 4783 } 4784 4785 DrawDragIndicator(tree, td2.drawable, COLUMN_LOCK_LEFT); 4786 4787 height = MIN(height, Tree_BorderBottom(tree) - Tree_BorderTop(tree)); 4788 XCopyArea(tree->display, td2.drawable, td.drawable, 4789 tree->copyGC, Tree_HeaderLeft(tree), y, 4790 x - Tree_HeaderLeft(tree), height, 4791 Tree_HeaderLeft(tree), y); 4792 4793 Tk_FreePixmap(tree->display, td2.drawable); 4794} 4795 4796static void 4797DrawHeaderRight( 4798 TreeCtrl *tree, /* Widget info. */ 4799 TreeDrawable td /* Where to draw. */ 4800 ) 4801{ 4802 TreeColumn column = tree->columnLockRight; 4803 Tk_Window tkwin = tree->tkwin; 4804 int x = Tree_ContentRight(tree), y = Tree_HeaderTop(tree); 4805 int height = tree->headerHeight; 4806 TreeDrawable td2; 4807 int visIndex = 0; 4808 4809 td2.width = Tk_Width(tkwin); 4810 td2.height = Tree_HeaderBottom(tree); 4811 td2.drawable = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), 4812 td2.width, td2.height, Tk_Depth(tkwin)); 4813 4814 while (column != NULL && column->lock == COLUMN_LOCK_RIGHT) { 4815 if (column->visible) { 4816 Column_Draw(column, td2, x, y, visIndex++, FALSE); 4817 x += column->useWidth; 4818 } 4819 column = column->next; 4820 } 4821 4822 DrawDragIndicator(tree, td2.drawable, COLUMN_LOCK_RIGHT); 4823 4824 height = MIN(height, Tree_BorderBottom(tree) - Tree_BorderTop(tree)); 4825 XCopyArea(tree->display, td2.drawable, td.drawable, 4826 tree->copyGC, Tree_ContentRight(tree), y, 4827 x - Tree_ContentRight(tree), height, 4828 Tree_ContentRight(tree), y); 4829 4830 Tk_FreePixmap(tree->display, td2.drawable); 4831} 4832 4833/* 4834 *---------------------------------------------------------------------- 4835 * 4836 * Tree_DrawHeader -- 4837 * 4838 * Draw the header of every column. 4839 * 4840 * Results: 4841 * None. 4842 * 4843 * Side effects: 4844 * Stuff is drawn in a drawable. 4845 * 4846 *---------------------------------------------------------------------- 4847 */ 4848 4849void 4850Tree_DrawHeader( 4851 TreeCtrl *tree, /* Widget info. */ 4852 TreeDrawable td, /* Where to draw. */ 4853 int x, int y /* Top-left corner of the header. */ 4854 ) 4855{ 4856 TreeColumn column = tree->columns; 4857 Tk_Window tkwin = tree->tkwin; 4858 int minX, maxX, width, height; 4859 Drawable drawable = td.drawable; 4860 TreeDrawable tp; 4861 Drawable pixmap; 4862 int visIndex = 0; 4863 4864 /* Update layout if needed */ 4865 (void) Tree_HeaderHeight(tree); 4866 (void) Tree_WidthOfColumns(tree); 4867 4868 minX = Tree_ContentLeft(tree); 4869 maxX = Tree_ContentRight(tree); 4870 4871 if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) { 4872 tp.width = Tk_Width(tkwin); 4873 tp.height = Tree_HeaderBottom(tree); 4874 tp.drawable = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin), 4875 tp.width, tp.height, Tk_Depth(tkwin)); 4876 } else { 4877 tp = td; 4878 } 4879 pixmap = tp.drawable; 4880 4881 column = tree->columnLockNone; 4882 while (column != NULL && column->lock == COLUMN_LOCK_NONE) { 4883 if (column->visible) { 4884 if ((x < maxX) && (x + column->useWidth > minX)) 4885 Column_Draw(column, tp, x, y, visIndex++, FALSE); 4886 x += column->useWidth; 4887 } 4888 column = column->next; 4889 } 4890 4891 /* Draw "tail" column */ 4892 if (x < maxX) { 4893 column = tree->columnTail; 4894 width = maxX - x + column->borderWidth; 4895 height = tree->headerHeight; 4896 if (!column->visible) { 4897 Tk_Fill3DRectangle(tkwin, pixmap, tree->border, 4898 x, y, width, height, 0, TK_RELIEF_FLAT); 4899 } else if (tree->useTheme && 4900 (TreeTheme_DrawHeaderItem(tree, pixmap, 0, 0, tree->columnCountVis, 4901 x, y, width, height) == TCL_OK)) { 4902 } else { 4903 Tk_3DBorder border; 4904 border = PerStateBorder_ForState(tree, &column->border, 4905 Column_MakeState(column), NULL); 4906 if (border == NULL) 4907 border = tree->border; 4908 Tk_Fill3DRectangle(tkwin, pixmap, border, 4909 x, y, width, height, column->borderWidth, TK_RELIEF_RAISED); 4910 } 4911 } 4912 4913 if (minX < maxX) 4914 DrawDragIndicator(tree, pixmap, COLUMN_LOCK_NONE); 4915 4916 if (Tree_WidthOfLeftColumns(tree) > 0) 4917 DrawHeaderLeft(tree, tp); 4918 if (Tree_WidthOfRightColumns(tree) > 0) 4919 DrawHeaderRight(tree, tp); 4920 4921 if (tree->columnDrag.column != NULL) { 4922 Tk_Image image; 4923 int x, y, w, h; 4924 4925 if (TreeColumn_Bbox(tree->columnDrag.column, &x, &y, &w, &h) == 0) { 4926 int ix = 0, iy = 0, iw = w, ih = tree->headerHeight; 4927 4928 image = SetImageForColumn(tree, tree->columnDrag.column); 4929 x += tree->columnDrag.offset; 4930 Tree_RedrawImage(image, ix, iy, iw, ih, tp, x, y); 4931 Tk_FreeImage(image); 4932 } 4933 } 4934 4935 if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) { 4936 height = MIN(tree->headerHeight, Tree_BorderBottom(tree) - Tree_BorderTop(tree)); 4937 XCopyArea(tree->display, pixmap, drawable, 4938 tree->copyGC, Tree_HeaderLeft(tree), y, 4939 Tree_HeaderWidth(tree), height, 4940 Tree_HeaderLeft(tree), y); 4941 4942 Tk_FreePixmap(tree->display, pixmap); 4943 } 4944} 4945 4946/* 4947 *---------------------------------------------------------------------- 4948 * 4949 * TreeColumn_WidthOfItems -- 4950 * 4951 * Calculate the maximum needed width of the styles in every 4952 * ReallyVisible() item for a particular column. The width will 4953 * only be recalculated if it is marked out-of-date. 4954 * 4955 * Results: 4956 * Pixel width. 4957 * 4958 * Side effects: 4959 * The size of elements and styles will be updated if they are 4960 * marked out-of-date. 4961 * 4962 *---------------------------------------------------------------------- 4963 */ 4964 4965int 4966TreeColumn_WidthOfItems( 4967 TreeColumn column /* Column token. */ 4968 ) 4969{ 4970 TreeCtrl *tree = column->tree; 4971 TreeItem item; 4972 TreeItemColumn itemColumn; 4973 int width; 4974 4975 if (column->widthOfItems >= 0) 4976 return column->widthOfItems; 4977 4978 column->widthOfItems = 0; 4979 item = tree->root; 4980 if (!TreeItem_ReallyVisible(tree, item)) 4981 item = TreeItem_NextVisible(tree, item); 4982 while (item != NULL) { 4983#ifdef EXPENSIVE_SPAN_WIDTH /* NOT USED */ 4984 width = TreeItem_NeededWidthOfColumn(tree, item, column->index); 4985 if (column == tree->columnTree) 4986 width += TreeItem_Indent(tree, item); 4987 column->widthOfItems = MAX(column->widthOfItems, width); 4988#else 4989 itemColumn = TreeItem_FindColumn(tree, item, column->index); 4990 if (itemColumn != NULL) { 4991 width = TreeItemColumn_NeededWidth(tree, item, itemColumn); 4992 if (column == tree->columnTree) 4993 width += TreeItem_Indent(tree, item); 4994 column->widthOfItems = MAX(column->widthOfItems, width); 4995 } 4996#endif 4997 item = TreeItem_NextVisible(tree, item); 4998 } 4999 5000 return column->widthOfItems; 5001} 5002 5003/* 5004 *---------------------------------------------------------------------- 5005 * 5006 * Tree_InvalidateColumnWidth -- 5007 * 5008 * Marks the width of zero or more columns as out-of-date. 5009 * Schedules a redisplay to check the widths of columns which 5010 * will perform any relayout necessary. 5011 * 5012 * Results: 5013 * None. 5014 * 5015 * Side effects: 5016 * Idle task may be scheduled. 5017 * 5018 *---------------------------------------------------------------------- 5019 */ 5020 5021void 5022Tree_InvalidateColumnWidth( 5023 TreeCtrl *tree, /* Widget info. */ 5024 TreeColumn column /* Column to modify. NULL means 5025 * modify every column. */ 5026 ) 5027{ 5028#ifdef COLUMN_SPANxxx 5029 /* It may be necessary to recalculate the width of other columns as 5030 * well when column-spanning is in effect. */ 5031 column = NULL; 5032#endif 5033 5034 if (column == NULL) { 5035 column = tree->columns; 5036 while (column != NULL) { 5037 column->widthOfItems = -1; 5038 column = column->next; 5039 } 5040 } else { 5041 column->widthOfItems = -1; 5042 } 5043 tree->widthOfColumns = -1; 5044 tree->widthOfColumnsLeft = tree->widthOfColumnsRight = -1; 5045 Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH); 5046} 5047 5048/* 5049 *---------------------------------------------------------------------- 5050 * 5051 * Tree_InvalidateColumnHeight -- 5052 * 5053 * Marks the height of zero or more column headers as out-of-date. 5054 * 5055 * Results: 5056 * None. 5057 * 5058 * Side effects: 5059 * None. 5060 * 5061 *---------------------------------------------------------------------- 5062 */ 5063 5064void 5065Tree_InvalidateColumnHeight( 5066 TreeCtrl *tree, /* Widget info. */ 5067 TreeColumn column /* Column to modify. NULL means 5068 * modify every column. */ 5069 ) 5070{ 5071 if (column == NULL) { 5072 column = tree->columns; 5073 while (column != NULL) { 5074 column->neededHeight = -1; 5075 column = column->next; 5076 } 5077 } else { 5078 column->neededHeight = -1; 5079 } 5080 tree->headerHeight = -1; 5081} 5082 5083/* 5084 *---------------------------------------------------------------------- 5085 * 5086 * TreeColumn_TreeChanged -- 5087 * 5088 * Called when a TreeCtrl is configured. Performs any relayout 5089 * necessary on column headers. 5090 * 5091 * Results: 5092 * None. 5093 * 5094 * Side effects: 5095 * None. 5096 * 5097 *---------------------------------------------------------------------- 5098 */ 5099 5100void 5101TreeColumn_TreeChanged( 5102 TreeCtrl *tree, /* Widget info. */ 5103 int flagT /* TREE_CONF_xxx flags. */ 5104 ) 5105{ 5106 TreeColumn column; 5107 5108 /* Column widths are invalidated elsewhere */ 5109 if (flagT & TREE_CONF_FONT) { 5110 column = tree->columns; 5111 while (column != NULL) { 5112 if ((column->tkfont == NULL) && (column->textLen > 0)) { 5113 column->textWidth = Tk_TextWidth(tree->tkfont, column->text, 5114 column->textLen); 5115 column->neededWidth = column->neededHeight = -1; 5116 column->textLayoutInvalid = TRUE; 5117 } 5118 column = column->next; 5119 } 5120 tree->headerHeight = -1; 5121 } 5122} 5123 5124/* 5125 *---------------------------------------------------------------------- 5126 * 5127 * Tree_HeaderHeight -- 5128 * 5129 * Return the total height of the column header area. The height 5130 * is only recalculated if it is marked out-of-date. 5131 * 5132 * Results: 5133 * Pixel height. Will be zero if the -showheader option is FALSE. 5134 * 5135 * Side effects: 5136 * None. 5137 * 5138 *---------------------------------------------------------------------- 5139 */ 5140 5141int 5142Tree_HeaderHeight( 5143 TreeCtrl *tree /* Widget info. */ 5144 ) 5145{ 5146 TreeColumn column; 5147 int height; 5148 5149 if (!tree->showHeader) 5150 return 0; 5151 5152 if (tree->headerHeight >= 0) 5153 return tree->headerHeight; 5154 5155 height = 0; 5156 column = tree->columns; 5157 while (column != NULL) { 5158 if (column->visible) 5159 height = MAX(height, TreeColumn_NeededHeight(column)); 5160 column = column->next; 5161 } 5162 return tree->headerHeight = height; 5163} 5164 5165/* 5166 *-------------------------------------------------------------- 5167 * 5168 * TreeColumn_Bbox -- 5169 * 5170 * Return the bounding box for a column header. 5171 * 5172 * Results: 5173 * Return value is -1 if the item is not visible. 5174 * 5175 * Side effects: 5176 * Column layout will be updated if needed. 5177 * 5178 *-------------------------------------------------------------- 5179 */ 5180 5181int 5182TreeColumn_Bbox( 5183 TreeColumn column, /* Column token. */ 5184 int *x, int *y, /* Out: window coordinates. */ 5185 int *w, int *h /* Out: width and height. */ 5186 ) 5187{ 5188 TreeCtrl *tree = column->tree; 5189 int left = 0 - tree->xOrigin; 5190 5191 if (!tree->showHeader || !TreeColumn_Visible(column)) 5192 return -1; 5193 5194 *y = Tree_HeaderTop(tree); 5195 *h = Tree_HeaderHeight(tree); 5196 5197 if (column == tree->columnTail) { 5198 *x = Tree_WidthOfColumns(tree) - tree->xOrigin; 5199 *w = 1; /* xxx */ 5200 return 0; 5201 } 5202 5203 /* Get width (and update column layout) */ 5204 *w = TreeColumn_UseWidth(column); 5205 5206 switch (TreeColumn_Lock(column)) { 5207 case COLUMN_LOCK_LEFT: 5208 left = Tree_BorderLeft(tree); 5209 break; 5210 case COLUMN_LOCK_NONE: 5211 break; 5212 case COLUMN_LOCK_RIGHT: 5213 left = Tree_ContentRight(tree); 5214 break; 5215 } 5216 5217 *x = left + TreeColumn_Offset(column); 5218 return 0; 5219} 5220 5221/* 5222 *-------------------------------------------------------------- 5223 * 5224 * Tree_HeaderUnderPoint -- 5225 * 5226 * Return a TreeColumn whose header contains the given coordinates. 5227 * 5228 * Results: 5229 * TreeColumn token or NULL if no column contains the point. 5230 * 5231 * Side effects: 5232 * Column layout will be updated if needed. 5233 * 5234 *-------------------------------------------------------------- 5235 */ 5236 5237TreeColumn 5238Tree_HeaderUnderPoint( 5239 TreeCtrl *tree, /* Widget info. */ 5240 int *x_, int *y_, /* In: window coordinates. 5241 * Out: coordinates relative to top-left 5242 * corner of the returned column. */ 5243 int *w, int *h, /* Returned width and height. */ 5244 int nearest /* TRUE if the column nearest the coordinates 5245 * should be returned. */ 5246 ) 5247{ 5248 Tk_Window tkwin = tree->tkwin; 5249 int x = *x_, y = *y_; 5250 int left, top, width, height; 5251 TreeColumn column = tree->columns; 5252 int hit; 5253 5254 hit = Tree_HitTest(tree, x, y); 5255 if (!nearest && (hit != TREE_AREA_HEADER)) 5256 return NULL; 5257 5258 if (nearest) { 5259 if (x < Tree_BorderLeft(tree)) 5260 x = Tree_BorderLeft(tree); 5261 if (x >= Tree_BorderRight(tree)) 5262 x = Tree_BorderRight(tree) - 1; 5263 if (y < Tree_BorderTop(tree)) 5264 y = Tree_BorderTop(tree); 5265 if (y >= Tree_ContentTop(tree)) 5266 y = Tree_ContentTop(tree) - 1; 5267 } 5268 5269 /* Test the columns in reverse of drawing order. */ 5270 column = tree->columnLockRight; 5271 while ((column != NULL) && (TreeColumn_Lock(column) == COLUMN_LOCK_RIGHT)) { 5272 if (TreeColumn_Bbox(column, &left, &top, &width, &height) == 0) { 5273 if ((x >= left) && (x < left + width)) { 5274 goto done; 5275 } 5276 } 5277 column = TreeColumn_Next(column); 5278 } 5279 5280 column = tree->columnLockLeft; 5281 while ((column != NULL) && (TreeColumn_Lock(column) == COLUMN_LOCK_LEFT)) { 5282 if (TreeColumn_Bbox(column, &left, &top, &width, &height) == 0) { 5283 if ((x >= left) && (x < left + width)) { 5284 goto done; 5285 } 5286 } 5287 column = TreeColumn_Next(column); 5288 } 5289 5290 column = tree->columnLockNone; 5291 while ((column != NULL) && (TreeColumn_Lock(column) == COLUMN_LOCK_NONE)) { 5292 if (TreeColumn_Bbox(column, &left, &top, &width, &height) == 0) { 5293 if ((x >= left) && (x < left + width)) { 5294 goto done; 5295 } 5296 } 5297 column = TreeColumn_Next(column); 5298 } 5299 5300 column = tree->columnTail; 5301 left = Tree_WidthOfColumns(tree) - tree->xOrigin; 5302 width = Tk_Width(tkwin) - left; 5303done: 5304 (*x_) = x - left; 5305 (*y_) = y - Tree_HeaderTop(tree); 5306 (*w) = width; 5307 (*h) = Tree_HeaderHeight(tree); 5308 return column; 5309} 5310 5311/* 5312 *---------------------------------------------------------------------- 5313 * 5314 * LayoutColumns -- 5315 * 5316 * Calculates the display width and horizontal offset of a range 5317 * of columns. 5318 * 5319 * Results: 5320 * The .useWidth and .offset fields of every column in the range 5321 * are updated. 5322 * The result is the sum of the widths of all visible columns in the 5323 * range. 5324 * 5325 * Side effects: 5326 * The size of elements and styles may be updated if they are 5327 * marked out-of-date. 5328 * 5329 *---------------------------------------------------------------------- 5330 */ 5331 5332static int 5333LayoutColumns( 5334 TreeColumn first, /* First column to update. All columns 5335 * with the same -lock value are updated. */ 5336 TreeColumn *visPtr, /* Out: first visible column. */ 5337 int *countVisPtr /* Out: number of visible columns. */ 5338 ) 5339{ 5340 TreeCtrl *tree; 5341 TreeColumn column; 5342 int width, visWidth, totalWidth = 0; 5343 int numExpand = 0, numSqueeze = 0; 5344#ifdef UNIFORM_GROUP 5345 Tcl_HashEntry *hPtr; 5346 Tcl_HashSearch search; 5347 UniformGroup *uniform; 5348 int uniformCount = 0; 5349#endif 5350 5351 if (visPtr != NULL) 5352 (*visPtr) = NULL; 5353 (*countVisPtr) = 0; 5354 5355 if (first == NULL) 5356 return 0; 5357 5358 tree = first->tree; 5359 5360#ifdef UNIFORM_GROUP 5361 /* Initialize the .minSize field of every uniform group. */ 5362 hPtr = Tcl_FirstHashEntry(&tree->uniformGroupHash, &search); 5363 while (hPtr != NULL) { 5364 uniform = (UniformGroup *) Tcl_GetHashValue(hPtr); 5365 uniform->minSize = 0; 5366 hPtr = Tcl_NextHashEntry(&search); 5367 } 5368#endif 5369 5370 /* 5371 * Determine the initial display width of each column. This will be: 5372 * a) the column's -width option (a fixed width), or 5373 * b) the maximum of: 5374 * 1) the width requested by the column's header 5375 * 2) the width requested by each item style in that column 5376 * For b) the width is clipped to -minwidth and -maxwidth. 5377 */ 5378 column = first; 5379 while (column != NULL && column->lock == first->lock) { 5380 if (column->visible) { 5381 if (column->widthObj != NULL) 5382 width = column->width; 5383 else { 5384 width = TreeColumn_WidthOfItems(column); 5385 width = MAX(width, TreeColumn_NeededWidth(column)); 5386 width = MAX(width, TreeColumn_MinWidth(column)); 5387 if (TreeColumn_MaxWidth(column) != -1) 5388 width = MIN(width, TreeColumn_MaxWidth(column)); 5389#ifdef UNIFORM_GROUP 5390 /* Track the maximum requested width of every column in this 5391 * column's uniform group considering -weight. */ 5392 if (column->uniform != NULL) { 5393 int weight = MAX(column->weight, 1); 5394 int minSize = (width + weight - 1) / weight; 5395 if (minSize > column->uniform->minSize) 5396 column->uniform->minSize = minSize; 5397 uniformCount++; 5398 } 5399 if (column->expand) 5400 numExpand += MAX(column->weight, 0); 5401 if (column->squeeze) 5402 numSqueeze += MAX(column->weight, 0); 5403#else 5404 if (column->expand) 5405 numExpand++; 5406 if (column->squeeze) 5407 numSqueeze++; 5408#endif 5409 } 5410 if (visPtr != NULL && (*visPtr) == NULL) 5411 (*visPtr) = column; 5412 (*countVisPtr)++; 5413 } else 5414 width = 0; 5415 column->useWidth = width; 5416 totalWidth += width; 5417 column = column->next; 5418 } 5419 5420#ifdef UNIFORM_GROUP 5421 /* Apply the -uniform and -weight options. */ 5422 if (uniformCount > 0) { 5423 column = first; 5424 while (column != NULL && column->lock == first->lock) { 5425 if (column->visible && 5426 column->widthObj == NULL && 5427 column->uniform != NULL) { 5428 int weight = MAX(column->weight, 1); 5429 width = column->uniform->minSize * weight; 5430 if (column->maxWidthObj != NULL) 5431 width = MIN(width, column->maxWidth); 5432 totalWidth -= column->useWidth; 5433 column->useWidth = width; 5434 totalWidth += width; 5435 } 5436 column = column->next; 5437 } 5438 } 5439#endif /* UNIFORM_GROUP */ 5440 5441 /* Locked columns don't squeeze or expand. */ 5442 if (first->lock != COLUMN_LOCK_NONE) 5443 goto doOffsets; 5444 5445 visWidth = Tree_ContentWidth(tree); 5446 if (visWidth <= 0) 5447 goto doOffsets; 5448 5449 /* Squeeze columns */ 5450 if ((visWidth < totalWidth) && (numSqueeze > 0)) { 5451 int spaceRemaining = totalWidth - visWidth; 5452 while ((spaceRemaining > 0) && (numSqueeze > 0)) { 5453 int each = (spaceRemaining >= numSqueeze) ? 5454 spaceRemaining / numSqueeze : 1; 5455 numSqueeze = 0; 5456 column = first; 5457 while (column != NULL && column->lock == first->lock) { 5458 if (column->visible && 5459 column->squeeze && 5460 (column->widthObj == NULL)) { 5461 int min = MAX(0, TreeColumn_MinWidth(column)); 5462 if (column->useWidth > min) { 5463 int sub = MIN(each, column->useWidth - min); 5464 column->useWidth -= sub; 5465 spaceRemaining -= sub; 5466 if (!spaceRemaining) break; 5467 if (column->useWidth > min) 5468 numSqueeze++; 5469 } 5470 } 5471 column = column->next; 5472 } 5473 } 5474 } 5475 5476 /* Expand columns */ 5477 if ((visWidth > totalWidth) && (numExpand > 0)) { 5478 int spaceRemaining = visWidth - totalWidth; 5479 while ((spaceRemaining > 0) && (numExpand > 0)) { 5480 int each = (spaceRemaining >= numExpand) ? 5481 spaceRemaining / numExpand : 1; 5482 numExpand = 0; 5483 column = first; 5484 while (column != NULL && column->lock == first->lock) { 5485#ifdef UNIFORM_GROUP 5486 int weight = MAX(column->weight, 0); 5487 if (column->visible && 5488 column->expand && weight && 5489 (column->widthObj == NULL)) { 5490 int max = TreeColumn_MaxWidth(column); 5491 if ((max == -1) || (column->useWidth < max)) { 5492 int eachW = MIN(each * weight, spaceRemaining); 5493 int add = (max == -1) ? eachW : MIN(eachW, max - column->useWidth); 5494 column->useWidth += add; 5495 spaceRemaining -= add; 5496 if (!spaceRemaining) break; 5497 if ((max == -1) || (column->useWidth < max)) 5498 numExpand += weight; 5499#else 5500 if (column->visible && 5501 column->expand && 5502 (column->widthObj == NULL)) { 5503 int max = TreeColumn_MaxWidth(column); 5504 if ((max == -1) || (column->useWidth < max)) { 5505 int add = (max == -1) ? each : MIN(each, max - column->useWidth); 5506 column->useWidth += add; 5507 spaceRemaining -= add; 5508 if (!spaceRemaining) break; 5509 if ((max == -1) || (column->useWidth < max)) 5510 numExpand++; 5511#endif 5512 } 5513 } 5514 column = column->next; 5515 } 5516 } 5517 } 5518 5519doOffsets: 5520 5521 /* Calculate the horizontal offset of each column in the range. 5522 * The total width is recalculated as well (needed anyway if any 5523 * columns were expanded or squeezed). */ 5524 totalWidth = 0; 5525 column = first; 5526 while (column != NULL && column->lock == first->lock) { 5527 column->offset = totalWidth; 5528 totalWidth += column->useWidth; 5529 column = column->next; 5530 } 5531 return totalWidth; 5532} 5533 5534/* 5535 *---------------------------------------------------------------------- 5536 * 5537 * Tree_WidthOfColumns -- 5538 * 5539 * Return the total display width of all non-locked columns (except 5540 * the tail). 5541 * The width is only recalculated if it is marked out-of-date. 5542 * Other fields of the TreeCtrl are updated to reflect the current 5543 * arrangement of columns. 5544 * 5545 * Results: 5546 * Pixel width. 5547 * 5548 * Side effects: 5549 * The size of elements and styles may be updated if they are 5550 * marked out-of-date. 5551 * 5552 *---------------------------------------------------------------------- 5553 */ 5554 5555int 5556Tree_WidthOfColumns( 5557 TreeCtrl *tree /* Widget info. */ 5558 ) 5559{ 5560 /* This gets called when the layout of all columns needs to be current. 5561 * So update the layout of the left- and right-locked columns too. */ 5562 (void) Tree_WidthOfLeftColumns(tree); 5563 (void) Tree_WidthOfRightColumns(tree); 5564 5565 if (tree->widthOfColumns >= 0) 5566 return tree->widthOfColumns; 5567 5568 tree->widthOfColumns = LayoutColumns( 5569 tree->columnLockNone, 5570 &tree->columnVis, 5571 &tree->columnCountVis); 5572 5573 if (tree->columnTree != NULL && TreeColumn_Visible(tree->columnTree)) { 5574 tree->columnTreeLeft = tree->columnTree->offset; 5575 tree->columnTreeVis = TRUE; 5576 } else { 5577 tree->columnTreeLeft = 0; 5578 tree->columnTreeVis = FALSE; 5579 } 5580 5581 return tree->widthOfColumns; 5582} 5583 5584/* 5585 *---------------------------------------------------------------------- 5586 * 5587 * Tree_WidthOfLeftColumns -- 5588 * 5589 * Return the total display width of all left-locked columns. 5590 * The width is only recalculated if it is marked out-of-date. 5591 * Other fields of the TreeCtrl are updated to reflect the current 5592 * arrangement of columns. 5593 * 5594 * Results: 5595 * Pixel width. 5596 * 5597 * Side effects: 5598 * The size of elements and styles may be updated if they are 5599 * marked out-of-date. 5600 * 5601 *---------------------------------------------------------------------- 5602 */ 5603 5604int 5605Tree_WidthOfLeftColumns( 5606 TreeCtrl *tree /* Widget info. */ 5607 ) 5608{ 5609 if (tree->widthOfColumnsLeft >= 0) 5610 return tree->widthOfColumnsLeft; 5611 5612 if (!Tree_ShouldDisplayLockedColumns(tree)) { 5613 TreeColumn column = tree->columnLockLeft; 5614 while (column != NULL && column->lock == COLUMN_LOCK_LEFT) { 5615 column->useWidth = 0; 5616 column = column->next; 5617 } 5618 tree->columnCountVisLeft = 0; 5619 tree->widthOfColumnsLeft = 0; 5620 return 0; 5621 } 5622 5623 tree->widthOfColumnsLeft = LayoutColumns( 5624 tree->columnLockLeft, 5625 NULL, 5626 &tree->columnCountVisLeft); 5627 5628 return tree->widthOfColumnsLeft; 5629} 5630 5631/* 5632 *---------------------------------------------------------------------- 5633 * 5634 * Tree_WidthOfRightColumns -- 5635 * 5636 * Return the total display width of all right-locked columns. 5637 * The width is only recalculated if it is marked out-of-date. 5638 * Other fields of the TreeCtrl are updated to reflect the current 5639 * arrangement of columns. 5640 * 5641 * Results: 5642 * Pixel width. 5643 * 5644 * Side effects: 5645 * The size of elements and styles may be updated if they are 5646 * marked out-of-date. 5647 * 5648 *---------------------------------------------------------------------- 5649 */ 5650 5651int 5652Tree_WidthOfRightColumns( 5653 TreeCtrl *tree /* Widget info. */ 5654 ) 5655{ 5656 if (tree->widthOfColumnsRight >= 0) 5657 return tree->widthOfColumnsRight; 5658 5659 if (!Tree_ShouldDisplayLockedColumns(tree)) { 5660 TreeColumn column = tree->columnLockRight; 5661 while (column != NULL && column->lock == COLUMN_LOCK_RIGHT) { 5662 column->useWidth = 0; 5663 column = column->next; 5664 } 5665 tree->columnCountVisRight = 0; 5666 tree->widthOfColumnsRight = 0; 5667 return 0; 5668 } 5669 5670 tree->widthOfColumnsRight = LayoutColumns( 5671 tree->columnLockRight, 5672 NULL, 5673 &tree->columnCountVisRight); 5674 5675 return tree->widthOfColumnsRight; 5676} 5677 5678/* 5679 *---------------------------------------------------------------------- 5680 * 5681 * Tree_InitColumns -- 5682 * 5683 * Perform column-related initialization when a new TreeCtrl is 5684 * created. 5685 * 5686 * Results: 5687 * A standard Tcl result. 5688 * 5689 * Side effects: 5690 * Memory is allocated. 5691 * 5692 *---------------------------------------------------------------------- 5693 */ 5694 5695void 5696Tree_InitColumns( 5697 TreeCtrl *tree /* Widget info. */ 5698 ) 5699{ 5700 TreeColumn column; 5701 5702 column = Column_Alloc(tree); 5703 column->id = -1; 5704 tree->columnTail = column; 5705 tree->nextColumnId = 0; 5706 tree->columnCount = 0; 5707 Column_Config(column, 0, NULL, TRUE); 5708 5709 tree->columnDrag.optionTable = Tk_CreateOptionTable(tree->interp, dragSpecs); 5710 (void) Tk_InitOptions(tree->interp, (char *) tree, 5711 tree->columnDrag.optionTable, tree->tkwin); 5712 5713#ifdef UNIFORM_GROUP 5714 Tcl_InitHashTable(&tree->uniformGroupHash, TCL_STRING_KEYS); 5715#endif 5716} 5717 5718/* 5719 *---------------------------------------------------------------------- 5720 * 5721 * Tree_FreeColumns -- 5722 * 5723 * Free column-related resources for a deleted TreeCtrl. 5724 * 5725 * Results: 5726 * None. 5727 * 5728 * Side effects: 5729 * Memory is deallocated. 5730 * 5731 *---------------------------------------------------------------------- 5732 */ 5733 5734void Tree_FreeColumns( 5735 TreeCtrl *tree /* Widget info. */ 5736 ) 5737{ 5738 TreeColumn column = tree->columns; 5739 5740 while (column != NULL) { 5741 column = Column_Free(column); 5742 } 5743 5744 Column_Free(tree->columnTail); 5745 tree->columnCount = 0; 5746 5747#ifdef UNIFORM_GROUP 5748 Tcl_DeleteHashTable(&tree->uniformGroupHash); 5749#endif 5750} 5751 5752int 5753TreeColumn_InitInterp( 5754 Tcl_Interp *interp /* Current interpreter. */ 5755 ) 5756{ 5757 Tk_OptionSpec *specPtr; 5758 Tcl_DString dString; 5759 5760 specPtr = Tree_FindOptionSpec(columnSpecs, "-background"); 5761 if (specPtr->defValue == NULL) { 5762 Tcl_DStringInit(&dString); 5763 Tcl_DStringAppendElement(&dString, DEF_BUTTON_BG_COLOR); 5764 Tcl_DStringAppendElement(&dString, "normal"); 5765 Tcl_DStringAppendElement(&dString, DEF_BUTTON_ACTIVE_BG_COLOR); 5766 Tcl_DStringAppendElement(&dString, ""); 5767 specPtr->defValue = ckalloc(Tcl_DStringLength(&dString) + 1); 5768 strcpy((char *)specPtr->defValue, Tcl_DStringValue(&dString)); 5769 Tcl_DStringFree(&dString); 5770 } 5771 5772 PerStateCO_Init(columnSpecs, "-arrowbitmap", &pstBitmap, ColumnStateFromObj); 5773 PerStateCO_Init(columnSpecs, "-arrowimage", &pstImage, ColumnStateFromObj); 5774 PerStateCO_Init(columnSpecs, "-background", &pstBorder, ColumnStateFromObj); 5775 StringTableCO_Init(columnSpecs, "-itemjustify", justifyStrings); 5776 5777 return TCL_OK; 5778} 5779