1 /*
2 * tkTreeUtils.c --
3 *
4 * This module implements misc routines for treectrl widgets.
5 *
6 * Copyright (c) 2002-2011 Tim Baker
7 */
8
9 #include "tkTreeCtrl.h"
10
11 struct dbwinterps {
12 int count;
13 #define DBWIN_MAX_INTERPS 16
14 Tcl_Interp *interps[DBWIN_MAX_INTERPS];
15 };
16
17 static Tcl_ThreadDataKey dbwinTDK;
18 static CONST char *DBWIN_VAR_NAME = "dbwin";
19
dbwin_forget_interp(ClientData clientData,Tcl_Interp * interp)20 static void dbwin_forget_interp(ClientData clientData, Tcl_Interp *interp)
21 {
22 struct dbwinterps *dbwinterps =
23 Tcl_GetThreadData(&dbwinTDK, sizeof(struct dbwinterps));
24 int i;
25
26 for (i = 0; i < dbwinterps->count; i++) {
27 if (dbwinterps->interps[i] == interp) {
28 for (; i < dbwinterps->count - 1; i++) {
29 dbwinterps->interps[i] = dbwinterps->interps[i + 1];
30 }
31 dbwinterps->count--;
32 break;
33 }
34 }
35 }
36
dbwin_add_interp(Tcl_Interp * interp)37 void dbwin_add_interp(Tcl_Interp *interp)
38 {
39 struct dbwinterps *dbwinterps =
40 Tcl_GetThreadData(&dbwinTDK, sizeof(struct dbwinterps));
41
42 if (dbwinterps->count < DBWIN_MAX_INTERPS) {
43 dbwinterps->interps[dbwinterps->count++] = interp;
44
45 Tcl_SetAssocData(interp,
46 DBWIN_VAR_NAME,
47 dbwin_forget_interp,
48 NULL);
49 }
50 }
51
dbwin(char * fmt,...)52 void dbwin(char *fmt, ...)
53 {
54 struct dbwinterps *dbwinterps =
55 Tcl_GetThreadData(&dbwinTDK, sizeof(struct dbwinterps));
56 char buf[512];
57 va_list args;
58 int i;
59
60 if (dbwinterps->count <= 0)
61 return;
62
63 va_start(args, fmt);
64 vsnprintf(buf, 512, fmt, args);
65 va_end(args);
66
67 buf[511] = '\0';
68
69 for (i = 0; i < dbwinterps->count; i++) {
70 /* All sorts of nasty stuff could happen here. */
71 Tcl_SetVar2(dbwinterps->interps[i],
72 DBWIN_VAR_NAME,
73 NULL,
74 buf,
75 TCL_GLOBAL_ONLY);
76 }
77 }
78
79 #if defined(TREECTRL_DEBUG)
80 #ifdef WIN32
81 #include <windows.h>
82 #endif
83 /*
84 *----------------------------------------------------------------------
85 *
86 * TreeCtrl_BreakIntoDebugger --
87 *
88 * If running in a debugger, call DebugBreak(), otherwise
89 * panic().
90 *
91 * Results:
92 * None.
93 *
94 * Side effects:
95 * None.
96 *
97 *----------------------------------------------------------------------
98 */
99
100 void
TreeCtrl_BreakIntoDebugger(const char * file,int line)101 TreeCtrl_BreakIntoDebugger(
102 const char *file,
103 int line
104 )
105 {
106 #if defined(WIN32)
107 if (IsDebuggerPresent())
108 DebugBreak();
109 else
110 #endif
111 panic("Debugger call at %s:%d", file, line);
112 }
113 #endif /* TREECTRL_DEBUG */
114
115 /*
116 * Forward declarations for procedures defined later in this file:
117 */
118
119 static int PadAmountOptionSet _ANSI_ARGS_((ClientData clientData,
120 Tcl_Interp *interp, Tk_Window tkwin,
121 Tcl_Obj **value, char *recordPtr, int internalOffset,
122 char *saveInternalPtr, int flags));
123 static Tcl_Obj *PadAmountOptionGet _ANSI_ARGS_((ClientData clientData,
124 Tk_Window tkwin, char *recordPtr, int internalOffset));
125 static void PadAmountOptionRestore _ANSI_ARGS_((ClientData clientData,
126 Tk_Window tkwin, char *internalPtr,
127 char *saveInternalPtr));
128 static void PadAmountOptionFree _ANSI_ARGS_((ClientData clientData,
129 Tk_Window tkwin, char *internalPtr));
130
131 /*
132 * The following Tk_ObjCustomOption structure can be used as clientData entry
133 * of a Tk_OptionSpec record with a TK_OPTION_CUSTOM type in the form
134 * "(ClientData) &TreeCtrlCO_pad"; the option will then parse list with
135 * one or two screen distances.
136 */
137
138 Tk_ObjCustomOption TreeCtrlCO_pad = {
139 "pad amount",
140 PadAmountOptionSet,
141 PadAmountOptionGet,
142 PadAmountOptionRestore,
143 PadAmountOptionFree
144 };
145
146 /*
147 *----------------------------------------------------------------------
148 *
149 * FormatResult --
150 *
151 * Set the interpreter's result to a formatted string.
152 *
153 * Results:
154 * None.
155 *
156 * Side effects:
157 * Interpreter's result is modified.
158 *
159 *----------------------------------------------------------------------
160 */
161
162 void
FormatResult(Tcl_Interp * interp,char * fmt,...)163 FormatResult(
164 Tcl_Interp *interp, /* Current interpreter. */
165 char *fmt, ... /* Format string and varargs. */
166 )
167 {
168 va_list ap;
169 char buf[256];
170
171 va_start(ap, fmt);
172 vsprintf(buf, fmt, ap);
173 va_end(ap);
174 Tcl_SetResult(interp, buf, TCL_VOLATILE);
175 }
176
177 /*
178 *----------------------------------------------------------------------
179 *
180 * DStringAppendf --
181 *
182 * Format a string and append it to a Tcl_DString.
183 *
184 * Results:
185 * None.
186 *
187 * Side effects:
188 * None.
189 *
190 *----------------------------------------------------------------------
191 */
192
193 void
DStringAppendf(Tcl_DString * dString,char * fmt,...)194 DStringAppendf(
195 Tcl_DString *dString, /* Initialized dynamic string. */
196 char *fmt, ... /* Format string and varargs. */
197 )
198 {
199 va_list ap;
200 char buf[256];
201
202 va_start(ap, fmt);
203 vsprintf(buf, fmt, ap);
204 va_end(ap);
205 Tcl_DStringAppend(dString, buf, -1);
206 }
207
208 /*
209 *----------------------------------------------------------------------
210 *
211 * Tree_Ellipsis --
212 *
213 * Determine the number of bytes from the string that will fit
214 * in the given horizontal span. If the entire string does not
215 * fit then determine the largest number of bytes of a substring
216 * with an ellipsis "..." appended that will fit.
217 *
218 * Results:
219 * When the return value is equal to numBytes the caller should
220 * not add the ellipsis to the string (unless force is TRUE). In
221 * this case maxPixels contains the number of pixels for the entire
222 * string (plus ellipsis if force is TRUE).
223 *
224 * When the return value is less than numBytes the caller should add
225 * the ellipsis because only a substring fits. In this case
226 * maxPixels contains the number of pixels for the substring
227 * plus ellipsis. The substring has a minimum of one character.
228 *
229 * Side effects:
230 * None.
231 *
232 *----------------------------------------------------------------------
233 */
234
235 int
Tree_Ellipsis(Tk_Font tkfont,char * string,int numBytes,int * maxPixels,char * ellipsis,int force)236 Tree_Ellipsis(
237 Tk_Font tkfont, /* The font used to display the string. */
238 char *string, /* UTF-8 string, need not be NULL-terminated. */
239 int numBytes, /* Number of bytes to consider. */
240 int *maxPixels, /* In: maximum line length allowed.
241 * Out: length of string that fits (with
242 * ellipsis added if needed). */
243 char *ellipsis, /* NULL-terminated "..." */
244 int force /* TRUE if ellipsis should always be added
245 * even if the whole string fits in
246 * maxPixels. */
247 )
248 {
249 char staticStr[256], *tmpStr = staticStr;
250 int pixels, pixelsTest, bytesThatFit, bytesTest;
251 int ellipsisNumBytes = (int) strlen(ellipsis);
252 int bytesInFirstCh;
253 Tcl_UniChar uniCh;
254
255 bytesThatFit = Tk_MeasureChars(tkfont, string, numBytes, *maxPixels, 0,
256 &pixels);
257
258 /* The whole string fits. No ellipsis needed (unless forced) */
259 if ((bytesThatFit == numBytes) && !force) {
260 (*maxPixels) = pixels;
261 return numBytes;
262 }
263
264 bytesInFirstCh = Tcl_UtfToUniChar(string, &uniCh);
265 if (bytesThatFit <= bytesInFirstCh) {
266 goto singleChar;
267 }
268
269 /* Strip off one character at a time, adding ellipsis, until it fits */
270 if (force)
271 bytesTest = bytesThatFit;
272 else
273 bytesTest = (int) (Tcl_UtfPrev(string + bytesThatFit, string) - string);
274 if (bytesTest + ellipsisNumBytes > sizeof(staticStr))
275 tmpStr = ckalloc(bytesTest + ellipsisNumBytes);
276 memcpy(tmpStr, string, bytesTest);
277 while (bytesTest > 0) {
278 memcpy(tmpStr + bytesTest, ellipsis, ellipsisNumBytes);
279 numBytes = Tk_MeasureChars(tkfont, tmpStr,
280 bytesTest + ellipsisNumBytes,
281 *maxPixels, 0, &pixelsTest);
282 if (numBytes == bytesTest + ellipsisNumBytes) {
283 (*maxPixels) = pixelsTest;
284 if (tmpStr != staticStr)
285 ckfree(tmpStr);
286 return bytesTest;
287 }
288 bytesTest = (int) (Tcl_UtfPrev(string + bytesTest, string) - string);
289 }
290
291 singleChar:
292 /* No single char + ellipsis fits. Return the number of bytes for
293 * the first character. The returned pixel width is the width of the
294 * first character plus ellipsis. */
295 bytesThatFit = bytesInFirstCh;
296 memcpy(tmpStr, string, bytesThatFit);
297 memcpy(tmpStr + bytesThatFit, ellipsis, ellipsisNumBytes);
298 (void) Tk_MeasureChars(tkfont, tmpStr, bytesThatFit + ellipsisNumBytes,
299 -1, 0, &pixels);
300 (*maxPixels) = pixels;
301 if (tmpStr != staticStr)
302 ckfree(tmpStr);
303 return bytesThatFit;
304 }
305
306 /*
307 *----------------------------------------------------------------------
308 *
309 * Tree_GetRegion --
310 *
311 * Return a pre-allocated TkRegion or create a new one.
312 *
313 * Results:
314 * None.
315 *
316 * Side effects:
317 * None.
318 *
319 *----------------------------------------------------------------------
320 */
321
322 TkRegion
Tree_GetRegion(TreeCtrl * tree)323 Tree_GetRegion(
324 TreeCtrl *tree /* Widget info. */
325 )
326 {
327 TkRegion region;
328
329 if (tree->regionStackLen == 0) {
330 return TkCreateRegion();
331 }
332 region = tree->regionStack[--tree->regionStackLen];
333 Tree_SetEmptyRegion(region);
334 return region;
335 }
336
337 /*
338 *----------------------------------------------------------------------
339 *
340 * Tree_FreeRegion --
341 *
342 * Push a region onto the free stack.
343 *
344 * Results:
345 * None.
346 *
347 * Side effects:
348 * None.
349 *
350 *----------------------------------------------------------------------
351 */
352
353 void
Tree_FreeRegion(TreeCtrl * tree,TkRegion region)354 Tree_FreeRegion(
355 TreeCtrl *tree, /* Widget info. */
356 TkRegion region /* Region being released. */
357 )
358 {
359 if (tree->regionStackLen == sizeof(tree->regionStack) / sizeof(TkRegion))
360 panic("Tree_FreeRegion: the stack is full");
361 tree->regionStack[tree->regionStackLen++] = region;
362 }
363
364 /*
365 *----------------------------------------------------------------------
366 *
367 * Tree_SetEmptyRegion --
368 *
369 * Set a region to empty.
370 *
371 * Results:
372 * None.
373 *
374 * Side effects:
375 * None.
376 *
377 *----------------------------------------------------------------------
378 */
379
380 void
Tree_SetEmptyRegion(TkRegion region)381 Tree_SetEmptyRegion(
382 TkRegion region /* Region to modify. */
383 )
384 {
385 TkSubtractRegion(region, region, region);
386 }
387
388 /*
389 *----------------------------------------------------------------------
390 *
391 * Tree_GetRectRegion --
392 *
393 * Allocate a region and set it to a single rectangle.
394 *
395 * Results:
396 * Changes a region.
397 *
398 * Side effects:
399 * None.
400 *
401 *----------------------------------------------------------------------
402 */
403
404 TkRegion
Tree_GetRectRegion(TreeCtrl * tree,const TreeRectangle * rect)405 Tree_GetRectRegion(
406 TreeCtrl *tree, /* Widget info. */
407 const TreeRectangle *rect /* Rectangle */
408 )
409 {
410 XRectangle xr;
411 TkRegion region;
412
413 region = Tree_GetRegion(tree);
414 TreeRect_ToXRect(*rect, &xr);
415 TkUnionRectWithRegion(&xr, region, region);
416 return region;
417 }
418
419 /*
420 *----------------------------------------------------------------------
421 *
422 * Tree_SetRectRegion --
423 *
424 * Set a region to a single rectangle.
425 *
426 * Results:
427 * Changes a region.
428 *
429 * Side effects:
430 * None.
431 *
432 *----------------------------------------------------------------------
433 */
434
435 void
Tree_SetRectRegion(TkRegion region,const TreeRectangle * rect)436 Tree_SetRectRegion(
437 TkRegion region, /* Region to modify. */
438 const TreeRectangle *rect /* Rectangle */
439 )
440 {
441 XRectangle xr;
442 Tree_SetEmptyRegion(region);
443 TreeRect_ToXRect(*rect, &xr);
444 TkUnionRectWithRegion(&xr, region, region);
445 }
446
447 /*
448 *----------------------------------------------------------------------
449 *
450 * Tree_GetRegionBounds --
451 *
452 * Return the bounding rectangle of a region.
453 *
454 * Results:
455 * Result rect is filled in with the bounds of the given region.
456 *
457 * Side effects:
458 * None.
459 *
460 *----------------------------------------------------------------------
461 */
462
463 void
Tree_GetRegionBounds(TkRegion region,TreeRectangle * rect)464 Tree_GetRegionBounds(
465 TkRegion region, /* Region to modify. */
466 TreeRectangle *rect /* Rectangle */
467 )
468 {
469 XRectangle xr;
470 TkClipBox(region, &xr);
471 TreeRect_FromXRect(xr, rect);
472 }
473
474 /*
475 *----------------------------------------------------------------------
476 *
477 * Tree_RedrawImage --
478 *
479 * Wrapper around Tk_RedrawImage to clip the drawing to the actual
480 * area of the drawable. If you try to draw a transparent photo
481 * image outside the bounds of a drawable, X11 will silently fail
482 * and nothing will be drawn. See tkImgPhoto.c:ImgPhotoDisplay.
483 *
484 * Results:
485 * None.
486 *
487 * Side effects:
488 * Stuff is drawn.
489 *
490 *----------------------------------------------------------------------
491 */
Tree_RedrawImage(Tk_Image image,int imageX,int imageY,int width,int height,TreeDrawable td,int drawableX,int drawableY)492 void Tree_RedrawImage(
493 Tk_Image image,
494 int imageX,
495 int imageY,
496 int width,
497 int height,
498 TreeDrawable td,
499 int drawableX,
500 int drawableY
501 )
502 {
503 #if 0
504 int ix = imageX, iy = imageY, iw = width, ih = height;
505 #endif
506 if (drawableX < 0) {
507 imageX = 0 - drawableX;
508 width -= imageX;
509 drawableX = 0;
510 }
511 if (drawableX + width > td.width) {
512 width -= (drawableX + width) - td.width;
513 }
514 if (drawableY < 0) {
515 imageY = 0 - drawableY;
516 height -= imageY;
517 drawableY = 0;
518 }
519 if (drawableY + height > td.height) {
520 height -= (drawableY + height) - td.height;
521 }
522 #if 0
523 if (ix != imageX || iy != imageY || iw != width || ih != height)
524 dbwin("Tree_RedrawImage clipped %d,%d,%d,%d -> %d,%d,%d,%d\n", ix,iy,iw,ih, imageX, imageY, width, height);
525 #endif
526 if (width > 0 && height > 0) {
527 Tk_RedrawImage(image, imageX, imageY, width, height, td.drawable,
528 drawableX, drawableY);
529 }
530 }
531
532 /*
533 *----------------------------------------------------------------------
534 *
535 * Tree_DrawBitmap --
536 *
537 * Draw part of a bitmap.
538 *
539 * Results:
540 * None.
541 *
542 * Side effects:
543 * Stuff is drawn.
544 *
545 *----------------------------------------------------------------------
546 */
547
548 void
Tree_DrawBitmap(TreeCtrl * tree,Pixmap bitmap,Drawable drawable,XColor * fg,XColor * bg,int src_x,int src_y,int width,int height,int dest_x,int dest_y)549 Tree_DrawBitmap(
550 TreeCtrl *tree, /* Widget info. */
551 Pixmap bitmap, /* Bitmap to draw. */
552 Drawable drawable, /* Where to draw. */
553 XColor *fg, XColor *bg, /* Foreground and background colors.
554 * May be NULL. */
555 int src_x, int src_y, /* Left and top of part of bitmap to copy. */
556 int width, int height, /* Width and height of part of bitmap to
557 * copy. */
558 int dest_x, int dest_y /* Left and top coordinates to copy part of
559 * the bitmap to. */
560 )
561 {
562 XGCValues gcValues;
563 GC gc;
564 unsigned long mask = 0;
565
566 if (fg != NULL) {
567 gcValues.foreground = fg->pixel;
568 mask |= GCForeground;
569 }
570 if (bg != NULL) {
571 gcValues.background = bg->pixel;
572 mask |= GCBackground;
573 } else {
574 gcValues.clip_mask = bitmap;
575 mask |= GCClipMask;
576 }
577 gcValues.graphics_exposures = False;
578 mask |= GCGraphicsExposures;
579 gc = Tk_GetGC(tree->tkwin, mask, &gcValues);
580 Tree_DrawBitmapWithGC(tree, bitmap, drawable, gc,
581 src_x, src_y, width, height, dest_x, dest_y);
582 Tk_FreeGC(tree->display, gc);
583 }
584
585 /*
586 * Replacement for Tk_TextLayout stuff. Allows the caller to break lines
587 * on character boundaries (as well as word boundaries). Allows the caller
588 * to specify the maximum number of lines to display. Will add ellipsis "..."
589 * to the end of text that is too long to fit (when max lines specified).
590 */
591
592 #define TEXTLAYOUT_ELLIPSIS
593
594 typedef struct LayoutChunk
595 {
596 CONST char *start; /* Pointer to simple string to be displayed.
597 * * This is a pointer into the TkTextLayout's
598 * * string. */
599 int numBytes; /* The number of bytes in this chunk. */
600 int numChars; /* The number of characters in this chunk. */
601 int numDisplayChars; /* The number of characters to display when
602 * * this chunk is displayed. Can be less than
603 * * numChars if extra space characters were
604 * * absorbed by the end of the chunk. This
605 * * will be < 0 if this is a chunk that is
606 * * holding a tab or newline. */
607 int x, y; /* The origin of the first character in this
608 * * chunk with respect to the upper-left hand
609 * * corner of the TextLayout. */
610 int totalWidth; /* Width in pixels of this chunk. Used
611 * * when hit testing the invisible spaces at
612 * * the end of a chunk. */
613 int displayWidth; /* Width in pixels of the displayable
614 * * characters in this chunk. Can be less than
615 * * width if extra space characters were
616 * * absorbed by the end of the chunk. */
617 #ifdef TEXTLAYOUT_ELLIPSIS
618 int ellipsis; /* TRUE if adding "..." */
619 #endif
620 } LayoutChunk;
621
622 typedef struct LayoutInfo
623 {
624 Tk_Font tkfont; /* The font used when laying out the text. */
625 CONST char *string; /* The string that was layed out. */
626 int numLines; /* Number of lines */
627 int width; /* The maximum width of all lines in the
628 * * text layout. */
629 int height;
630 int numChunks; /* Number of chunks actually used in
631 * * following array. */
632 int totalWidth;
633 #define TEXTLAYOUT_ALLOCHAX
634 #ifdef TEXTLAYOUT_ALLOCHAX
635 int maxChunks;
636 struct LayoutInfo *nextFree;
637 #endif
638 LayoutChunk chunks[1]; /* Array of chunks. The actual size will
639 * * be maxChunks. THIS FIELD MUST BE THE LAST
640 * * IN THE STRUCTURE. */
641 } LayoutInfo;
642
643 #ifdef TEXTLAYOUT_ALLOCHAX
644 TCL_DECLARE_MUTEX(textLayoutMutex)
645 /* FIXME: memory leak, list is never freed. */
646 static LayoutInfo *freeLayoutInfo = NULL;
647 #endif
648
649 #ifdef TEXTLAYOUT_ALLOCHAX
NewChunk(LayoutInfo ** layoutPtrPtr,CONST char * start,int numBytes,int curX,int newX,int y)650 static LayoutChunk *NewChunk(LayoutInfo **layoutPtrPtr,
651 #else
652 static LayoutChunk *NewChunk(LayoutInfo **layoutPtrPtr, int *maxPtr,
653 #endif
654 CONST char *start, int numBytes, int curX, int newX, int y)
655 {
656 LayoutInfo *layoutPtr;
657 LayoutChunk *chunkPtr;
658 #ifdef TEXTLAYOUT_ALLOCHAX
659 int numChars;
660 #else
661 int maxChunks, numChars;
662 #endif
663 size_t s;
664
665 layoutPtr = *layoutPtrPtr;
666 #ifdef TEXTLAYOUT_ALLOCHAX
667 if (layoutPtr->numChunks == layoutPtr->maxChunks) {
668 layoutPtr->maxChunks *= 2;
669 s = sizeof(LayoutInfo) + ((layoutPtr->maxChunks - 1) * sizeof(LayoutChunk));
670 layoutPtr = (LayoutInfo *) ckrealloc((char *) layoutPtr, (int) s);
671
672 *layoutPtrPtr = layoutPtr;
673 }
674 #else
675 maxChunks = *maxPtr;
676 if (layoutPtr->numChunks == maxChunks) {
677 maxChunks *= 2;
678 s = sizeof(LayoutInfo) + ((maxChunks - 1) * sizeof(LayoutChunk));
679 layoutPtr = (LayoutInfo *) ckrealloc((char *) layoutPtr, s);
680
681 *layoutPtrPtr = layoutPtr;
682 *maxPtr = maxChunks;
683 }
684 #endif
685 numChars = Tcl_NumUtfChars(start, numBytes);
686 chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
687 chunkPtr->start = start;
688 chunkPtr->numBytes = numBytes;
689 chunkPtr->numChars = numChars;
690 chunkPtr->numDisplayChars = numChars;
691 chunkPtr->x = curX;
692 chunkPtr->y = y;
693 chunkPtr->totalWidth = newX - curX;
694 chunkPtr->displayWidth = newX - curX;
695 chunkPtr->ellipsis = FALSE;
696 layoutPtr->numChunks++;
697
698 return chunkPtr;
699 }
700
TextLayout_Compute(Tk_Font tkfont,CONST char * string,int numChars,int wrapLength,Tk_Justify justify,int maxLines,int lMargin1,int lMargin2,int flags)701 TextLayout TextLayout_Compute(
702 Tk_Font tkfont, /* Font that will be used to display text. */
703 CONST char *string, /* String whose dimensions are to be
704 ** computed. */
705 int numChars, /* Number of characters to consider from
706 ** string, or < 0 for strlen(). */
707 int wrapLength, /* Longest permissible line length, in
708 ** pixels. <= 0 means no automatic wrapping:
709 ** just let lines get as long as needed. */
710 Tk_Justify justify, /* How to justify lines. */
711 int maxLines,
712 int lMargin1, int lMargin2, /* Extra indentation or zero */
713 int flags /* Flag bits OR-ed together.
714 ** TK_IGNORE_TABS means that tab characters
715 ** should not be expanded. TK_IGNORE_NEWLINES
716 ** means that newline characters should not
717 ** cause a line break. */
718 )
719 {
720 CONST char *start, *end, *special;
721 int n, y, bytesThisChunk, maxChunks;
722 int baseline, height, curX, newX, maxWidth;
723 LayoutInfo *layoutPtr;
724 LayoutChunk *chunkPtr;
725 Tk_FontMetrics fm;
726 Tcl_DString lineBuffer;
727 int *lineLengths;
728 int curLine;
729 int tabWidth = 20; /* FIXME */
730
731 Tcl_DStringInit(&lineBuffer);
732
733 Tk_GetFontMetrics(tkfont, &fm);
734 height = fm.ascent + fm.descent;
735
736 if (numChars < 0)
737 numChars = Tcl_NumUtfChars(string, -1);
738 if (wrapLength == 0)
739 wrapLength = -1;
740
741 #ifdef TEXTLAYOUT_ALLOCHAX
742 Tcl_MutexLock(&textLayoutMutex);
743 if (freeLayoutInfo != NULL) {
744 layoutPtr = freeLayoutInfo;
745 freeLayoutInfo = layoutPtr->nextFree;
746 } else {
747 maxChunks = 1;
748 layoutPtr = (LayoutInfo *) ckalloc(sizeof(LayoutInfo) +
749 (maxChunks - 1) * sizeof(LayoutChunk));
750 layoutPtr->maxChunks = maxChunks;
751 }
752 Tcl_MutexUnlock(&textLayoutMutex);
753 #else
754 maxChunks = 1;
755
756 layoutPtr = (LayoutInfo *) ckalloc(sizeof(LayoutInfo) + (maxChunks -
757 1) * sizeof(LayoutChunk));
758 #endif
759 layoutPtr->tkfont = tkfont;
760 layoutPtr->string = string;
761 layoutPtr->numChunks = 0;
762 layoutPtr->numLines = 0;
763
764 baseline = fm.ascent;
765 maxWidth = 0;
766
767 curX = lMargin1;
768 end = Tcl_UtfAtIndex(string, numChars);
769 special = string;
770
771 flags &= TK_WHOLE_WORDS | TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
772 flags |= TK_AT_LEAST_ONE;
773 for (start = string; start < end;) {
774 if (start >= special) {
775 for (special = start; special < end; special++) {
776 if (!(flags & TK_IGNORE_NEWLINES)) {
777 if ((*special == '\n') || (*special == '\r'))
778 break;
779 }
780 if (!(flags & TK_IGNORE_TABS)) {
781 if (*special == '\t')
782 break;
783 }
784 }
785 }
786
787 chunkPtr = NULL;
788 if (start < special) {
789 bytesThisChunk = Tk_MeasureChars(tkfont, start,
790 (int) (special - start),
791 wrapLength - curX, flags, &newX);
792 newX += curX;
793 flags &= ~TK_AT_LEAST_ONE;
794 if (bytesThisChunk > 0) {
795 #ifdef TEXTLAYOUT_ALLOCHAX
796 chunkPtr = NewChunk(&layoutPtr, start,
797 #else
798 chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
799 #endif
800 bytesThisChunk, curX, newX, baseline);
801 start += bytesThisChunk;
802 curX = newX;
803 }
804 }
805
806 if ((start == special) && (special < end)) {
807 chunkPtr = NULL;
808 if (*special == '\t') {
809 newX = curX + tabWidth;
810 newX -= newX % tabWidth;
811 #ifdef TEXTLAYOUT_ALLOCHAX
812 NewChunk(&layoutPtr, start, 1, curX, newX,
813 #else
814 NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
815 #endif
816 baseline)->numDisplayChars = -1;
817 start++;
818 if ((start < end) && ((wrapLength <= 0) ||
819 (newX <= wrapLength))) {
820 curX = newX;
821 flags &= ~TK_AT_LEAST_ONE;
822 continue;
823 }
824 } else {
825 #ifdef TEXTLAYOUT_ALLOCHAX
826 NewChunk(&layoutPtr, start, 1, curX, curX,
827 #else
828 NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
829 #endif
830 baseline)->numDisplayChars = -1;
831 start++;
832 goto wrapLine;
833 }
834 }
835
836 while ((start < end) && isspace(UCHAR(*start))) {
837 if (!(flags & TK_IGNORE_NEWLINES)) {
838 if ((*start == '\n') || (*start == '\r'))
839 break;
840 }
841 if (!(flags & TK_IGNORE_TABS)) {
842 if (*start == '\t')
843 break;
844 }
845 start++;
846 }
847 if (chunkPtr != NULL) {
848 CONST char *end;
849
850 end = chunkPtr->start + chunkPtr->numBytes;
851 bytesThisChunk = (int) (start - end);
852 if (bytesThisChunk > 0) {
853 bytesThisChunk =
854 Tk_MeasureChars(tkfont, end, bytesThisChunk, -1, 0,
855 &chunkPtr->totalWidth);
856 chunkPtr->numBytes += bytesThisChunk;
857 chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
858 chunkPtr->totalWidth += curX;
859 }
860 }
861
862 wrapLine:
863 flags |= TK_AT_LEAST_ONE;
864
865 if (curX > maxWidth)
866 maxWidth = curX;
867
868 Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
869
870 chunkPtr = layoutPtr->numChunks ?
871 &layoutPtr->chunks[layoutPtr->numChunks - 1] : NULL;
872 if ((chunkPtr != NULL) && !(flags & TK_IGNORE_NEWLINES) &&
873 (chunkPtr->start[0] == '\n'))
874 curX = lMargin1;
875 else
876 curX = lMargin2;
877
878 baseline += height;
879 layoutPtr->numLines++;
880
881 if ((maxLines > 0) && (layoutPtr->numLines >= maxLines))
882 break;
883 }
884
885 if ((start >= end) && (layoutPtr->numChunks > 0) &&
886 !(flags & TK_IGNORE_NEWLINES)) {
887 if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n') {
888 chunkPtr =
889 #ifdef TEXTLAYOUT_ALLOCHAX
890 NewChunk(&layoutPtr, start, 0, curX, curX,
891 #else
892 NewChunk(&layoutPtr, &maxChunks, start, 0, curX, curX,
893 #endif
894 baseline);
895 chunkPtr->numDisplayChars = -1;
896 Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
897 baseline += height;
898 }
899 }
900
901 #ifdef TEXTLAYOUT_ELLIPSIS
902 /* Fiddle with chunks on the last line to add ellipsis if there is some
903 * text remaining */
904 if ((start < end) && (layoutPtr->numChunks > 0)) {
905 char *ellipsis = "...";
906 int ellipsisLen = (int) strlen(ellipsis);
907 char staticStr[256], *buf = staticStr;
908 int pixelsForText;
909
910 chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 1];
911 if (wrapLength > 0) {
912 y = chunkPtr->y;
913 for (n = layoutPtr->numChunks - 1; n >= 0; n--) {
914 chunkPtr = &layoutPtr->chunks[n];
915
916 /* Only consider the last line */
917 if (chunkPtr->y != y)
918 break;
919
920 if (chunkPtr->start[0] == '\n')
921 continue;
922
923 if (chunkPtr->x + chunkPtr->totalWidth < wrapLength)
924 pixelsForText = wrapLength - chunkPtr->x;
925 else
926 pixelsForText = chunkPtr->totalWidth - 1;
927 bytesThisChunk = Tree_Ellipsis(tkfont,
928 (char *) chunkPtr->start, chunkPtr->numBytes,
929 &pixelsForText, ellipsis, TRUE);
930 if (pixelsForText > wrapLength - chunkPtr->x)
931 pixelsForText = wrapLength - chunkPtr->x;
932 if (bytesThisChunk > 0) {
933 chunkPtr->numBytes = bytesThisChunk;
934 chunkPtr->numChars = Tcl_NumUtfChars(chunkPtr->start, bytesThisChunk);
935 chunkPtr->numDisplayChars = chunkPtr->numChars;
936 chunkPtr->ellipsis = TRUE;
937 chunkPtr->displayWidth = pixelsForText;
938 chunkPtr->totalWidth = pixelsForText;
939 lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
940 lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + pixelsForText;
941 if (chunkPtr->x + pixelsForText > maxWidth)
942 maxWidth = chunkPtr->x + pixelsForText;
943 break;
944 }
945 }
946 } else {
947 if (chunkPtr->start[0] == '\n') {
948 if (layoutPtr->numChunks == 1)
949 goto finish;
950 if (layoutPtr->chunks[layoutPtr->numChunks - 2].y != chunkPtr->y)
951 goto finish;
952 chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 2];
953 }
954
955 if (chunkPtr->numBytes + ellipsisLen > sizeof(staticStr))
956 buf = ckalloc(chunkPtr->numBytes + ellipsisLen);
957 memcpy(buf, chunkPtr->start, chunkPtr->numBytes);
958 memcpy(buf + chunkPtr->numBytes, ellipsis, ellipsisLen);
959 Tk_MeasureChars(tkfont, buf,
960 chunkPtr->numBytes + ellipsisLen, -1, 0,
961 &chunkPtr->displayWidth);
962 chunkPtr->totalWidth = chunkPtr->displayWidth;
963 chunkPtr->ellipsis = TRUE;
964 lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
965 lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + chunkPtr->displayWidth;
966 if (chunkPtr->x + chunkPtr->displayWidth > maxWidth)
967 maxWidth = chunkPtr->x + chunkPtr->displayWidth;
968 if (buf != staticStr)
969 ckfree(buf);
970 }
971 }
972 finish:
973 #endif
974
975 layoutPtr->width = maxWidth;
976 layoutPtr->height = baseline - fm.ascent;
977 layoutPtr->totalWidth = 0;
978 if (layoutPtr->numChunks == 0) {
979 layoutPtr->height = height;
980
981 layoutPtr->numChunks = 1;
982 layoutPtr->chunks[0].start = string;
983 layoutPtr->chunks[0].numBytes = 0;
984 layoutPtr->chunks[0].numChars = 0;
985 layoutPtr->chunks[0].numDisplayChars = -1;
986 layoutPtr->chunks[0].x = 0;
987 layoutPtr->chunks[0].y = fm.ascent;
988 layoutPtr->chunks[0].totalWidth = 0;
989 layoutPtr->chunks[0].displayWidth = 0;
990 } else {
991 curLine = 0;
992 chunkPtr = layoutPtr->chunks;
993 y = chunkPtr->y;
994 lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
995 for (n = 0; n < layoutPtr->numChunks; n++) {
996 int extra;
997
998 if (chunkPtr->y != y) {
999 curLine++;
1000 y = chunkPtr->y;
1001 }
1002 extra = maxWidth - lineLengths[curLine];
1003 if (justify == TK_JUSTIFY_CENTER) {
1004 chunkPtr->x += extra / 2;
1005 }
1006 else if (justify == TK_JUSTIFY_RIGHT) {
1007 chunkPtr->x += extra;
1008 }
1009 if (chunkPtr->x + chunkPtr->totalWidth > layoutPtr->totalWidth)
1010 layoutPtr->totalWidth = chunkPtr->x + chunkPtr->totalWidth;
1011 chunkPtr++;
1012 }
1013 /* dbwin("totalWidth %d displayWidth %d\n", layoutPtr->totalWidth, maxWidth); */
1014 }
1015
1016 Tcl_DStringFree(&lineBuffer);
1017
1018 /* We don't want single-line text layouts for text elements, but it happens for column titles */
1019 /* if (layoutPtr->numLines == 1)
1020 dbwin("WARNING: single-line TextLayout created\n"); */
1021
1022 return (TextLayout) layoutPtr;
1023 }
1024
TextLayout_Free(TextLayout textLayout)1025 void TextLayout_Free(TextLayout textLayout)
1026 {
1027 LayoutInfo *layoutPtr = (LayoutInfo *) textLayout;
1028
1029 #ifdef TEXTLAYOUT_ALLOCHAX
1030 Tcl_MutexLock(&textLayoutMutex);
1031 layoutPtr->nextFree = freeLayoutInfo;
1032 freeLayoutInfo = layoutPtr;
1033 Tcl_MutexUnlock(&textLayoutMutex);
1034 #else
1035 ckfree((char *) layoutPtr);
1036 #endif
1037 }
1038
TextLayout_Size(TextLayout textLayout,int * widthPtr,int * heightPtr)1039 void TextLayout_Size(TextLayout textLayout, int *widthPtr, int *heightPtr)
1040 {
1041 LayoutInfo *layoutPtr = (LayoutInfo *) textLayout;
1042
1043 if (widthPtr != NULL)
1044 (*widthPtr) = layoutPtr->width;
1045 if (heightPtr != NULL)
1046 (*heightPtr) = layoutPtr->height;
1047 }
1048
TextLayout_TotalWidth(TextLayout textLayout)1049 int TextLayout_TotalWidth(TextLayout textLayout)
1050 {
1051 LayoutInfo *layoutPtr = (LayoutInfo *) textLayout;
1052
1053 return layoutPtr->totalWidth;
1054 }
1055
TextLayout_Draw(Display * display,Drawable drawable,GC gc,TextLayout layout,int x,int y,int firstChar,int lastChar,int underline)1056 void TextLayout_Draw(
1057 Display *display, /* Display on which to draw. */
1058 Drawable drawable, /* Window or pixmap in which to draw. */
1059 GC gc, /* Graphics context to use for drawing text. */
1060 TextLayout layout, /* Layout information, from a previous call
1061 * * to Tk_ComputeTextLayout(). */
1062 int x, int y, /* Upper-left hand corner of rectangle in
1063 * * which to draw (pixels). */
1064 int firstChar, /* The index of the first character to draw
1065 * * from the given text item. 0 specfies the
1066 * * beginning. */
1067 int lastChar, /* The index just after the last character
1068 * * to draw from the given text item. A number
1069 * * < 0 means to draw all characters. */
1070 int underline /* Character index to underline, or < 0 for
1071 * no underline. */
1072 )
1073 {
1074 LayoutInfo *layoutPtr = (LayoutInfo *) layout;
1075 int i, numDisplayChars, drawX;
1076 CONST char *firstByte;
1077 CONST char *lastByte;
1078 LayoutChunk *chunkPtr;
1079
1080 if (lastChar < 0)
1081 lastChar = 100000000;
1082 chunkPtr = layoutPtr->chunks;
1083 for (i = 0; i < layoutPtr->numChunks; i++) {
1084 numDisplayChars = chunkPtr->numDisplayChars;
1085 if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
1086 if (firstChar <= 0) {
1087 drawX = 0;
1088 firstChar = 0;
1089 firstByte = chunkPtr->start;
1090 } else {
1091 firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar);
1092 Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start,
1093 (int) (firstByte - chunkPtr->start), -1, 0, &drawX);
1094 }
1095 if (lastChar < numDisplayChars)
1096 numDisplayChars = lastChar;
1097 lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars);
1098 #ifdef TEXTLAYOUT_ELLIPSIS
1099 if (chunkPtr->ellipsis) {
1100 char staticStr[256], *buf = staticStr;
1101 char *ellipsis = "...";
1102 int ellipsisLen = (int) strlen(ellipsis);
1103
1104 if ((lastByte - firstByte) + ellipsisLen > sizeof(staticStr))
1105 buf = ckalloc((int) (lastByte - firstByte) + ellipsisLen);
1106 memcpy(buf, firstByte, (lastByte - firstByte));
1107 memcpy(buf + (lastByte - firstByte), ellipsis, ellipsisLen);
1108 Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
1109 buf, (int) (lastByte - firstByte) + ellipsisLen,
1110 x + chunkPtr->x + drawX, y + chunkPtr->y);
1111 if (buf != staticStr)
1112 ckfree(buf);
1113 } else
1114 #endif
1115 Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
1116 firstByte, (int) (lastByte - firstByte),
1117 x + chunkPtr->x + drawX, y + chunkPtr->y);
1118
1119 if (underline >= firstChar && underline < numDisplayChars) {
1120 CONST char *fstBytePtr = Tcl_UtfAtIndex(chunkPtr->start, underline);
1121 CONST char *sndBytePtr = Tcl_UtfNext(fstBytePtr);
1122 Tk_UnderlineChars(display, drawable, gc,
1123 layoutPtr->tkfont, firstByte,
1124 x + chunkPtr->x + drawX, y + chunkPtr->y,
1125 (int) (fstBytePtr - chunkPtr->start),
1126 (int) (sndBytePtr - chunkPtr->start));
1127 }
1128 }
1129 firstChar -= chunkPtr->numChars;
1130 lastChar -= chunkPtr->numChars;
1131 underline -= chunkPtr->numChars;
1132
1133 if (lastChar <= 0)
1134 break;
1135 chunkPtr++;
1136 }
1137 }
1138
1139 /*
1140 *----------------------------------------------------------------------
1141 *
1142 * TreeCtrl_GetPadAmountFromObj --
1143 *
1144 * Parse a pad amount configuration option.
1145 * A pad amount (typically the value of an option -XXXpadx or
1146 * -XXXpady, where XXX may be a possibly empty string) can
1147 * be either a single pixel width, or a list of two pixel widths.
1148 * If a single pixel width, the amount specified is used for
1149 * padding on both sides. If two amounts are specified, then
1150 * they specify the left/right or top/bottom padding.
1151 *
1152 * Results:
1153 * Standard Tcl Result.
1154 *
1155 * Side effects:
1156 * Sets internal representation of the object. In case of an error
1157 * the result of the interpreter is modified.
1158 *
1159 *----------------------------------------------------------------------
1160 */
1161
1162 int
TreeCtrl_GetPadAmountFromObj(Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj * padObj,int * topLeftPtr,int * bottomRightPtr)1163 TreeCtrl_GetPadAmountFromObj(
1164 Tcl_Interp *interp, /* Interpreter for error reporting, or NULL,
1165 * if no error message is wanted. */
1166 Tk_Window tkwin, /* A window. Needed by Tk_GetPixels() */
1167 Tcl_Obj *padObj, /* Object containing a pad amount. */
1168 int *topLeftPtr, /* Pointer to the location, where to store the
1169 first component of the padding. */
1170 int *bottomRightPtr /* Pointer to the location, where to store the
1171 second component of the padding. */
1172 )
1173 {
1174 int padc; /* Number of element objects in padv. */
1175 Tcl_Obj **padv; /* Pointer to the element objects of the
1176 * parsed pad amount value. */
1177 int topLeft, bottomRight;
1178
1179 if (Tcl_ListObjGetElements(interp, padObj, &padc, &padv) != TCL_OK) {
1180 return TCL_ERROR;
1181 }
1182
1183 /*
1184 * The value specifies a non empty string.
1185 * Check that this string is indeed a valid pad amount.
1186 */
1187
1188 if (padc < 1 || padc > 2) {
1189 if (interp != NULL) {
1190 error:
1191 Tcl_ResetResult(interp);
1192 Tcl_AppendResult(interp, "bad pad amount \"",
1193 Tcl_GetString(padObj), "\": must be a list of ",
1194 "1 or 2 positive screen distances", (char *) NULL);
1195 }
1196 return TCL_ERROR;
1197 }
1198 if ((Tk_GetPixelsFromObj(interp, tkwin, padv[0], &topLeft)
1199 != TCL_OK) || (topLeft < 0)) {
1200 goto error;
1201 }
1202 if (padc == 2) {
1203 if ((Tk_GetPixelsFromObj(interp, tkwin, padv[1], &bottomRight)
1204 != TCL_OK) || (bottomRight < 0)) {
1205 goto error;
1206 }
1207 } else {
1208 bottomRight = topLeft;
1209 }
1210 (*topLeftPtr) = topLeft;
1211 (*bottomRightPtr) = bottomRight;
1212 return TCL_OK;
1213 }
1214
1215 /*
1216 *----------------------------------------------------------------------
1217 *
1218 * TreeCtrl_NewPadAmountObj --
1219 *
1220 * Create a Tcl object with an internal representation, that
1221 * corresponds to a pad amount, i.e. an integer Tcl_Obj or a
1222 * list Tcl_Obj with 2 elements.
1223 *
1224 * Results:
1225 * The created object.
1226 *
1227 * Side effects:
1228 * None.
1229 *
1230 *----------------------------------------------------------------------
1231 */
1232
1233 Tcl_Obj *
TreeCtrl_NewPadAmountObj(int * padAmounts)1234 TreeCtrl_NewPadAmountObj(
1235 int *padAmounts /* Internal form of a pad amount. */
1236 )
1237 {
1238 Tcl_Obj *newObj;
1239
1240 /*
1241 * If both values are the same, create a list with one value,
1242 * otherwise create a two element list with the top/left value
1243 * first followed by the bottom/right value.
1244 */
1245
1246 if (padAmounts[PAD_TOP_LEFT] == padAmounts[PAD_BOTTOM_RIGHT]) {
1247 newObj = Tcl_NewIntObj(padAmounts[PAD_TOP_LEFT]);
1248 } else {
1249 newObj = Tcl_NewObj();
1250 Tcl_ListObjAppendElement((Tcl_Interp *) NULL, newObj,
1251 Tcl_NewIntObj(padAmounts[PAD_TOP_LEFT]));
1252 Tcl_ListObjAppendElement((Tcl_Interp *) NULL, newObj,
1253 Tcl_NewIntObj(padAmounts[PAD_BOTTOM_RIGHT]));
1254 }
1255 return newObj;
1256 }
1257
1258 /*
1259 *----------------------------------------------------------------------
1260 *
1261 * PadAmountOptionSet --
1262 * PadAmountOptionGet --
1263 * PadAmountOptionRestore --
1264 * PadAmountOptionFree --
1265 *
1266 * Handlers for object-based pad amount configuration options.
1267 * A pad amount (typically the value of an option -XXXpadx or
1268 * -XXXpady, where XXX may be a possibly empty string) can
1269 * be either a single pixel width, or a list of two pixel widths.
1270 * If a single pixel width, the amount specified is used for
1271 * padding on both sides. If two amounts are specified, then
1272 * they specify the left/right or top/bottom padding.
1273 *
1274 * Results:
1275 * See user documentation for expected results from these functions.
1276 * PadAmountOptionSet Standard Tcl Result.
1277 * PadAmountOptionGet Tcl_Obj * containing a valid internal
1278 * representation of the pad amount.
1279 * PadAmountOptionRestore None.
1280 * PadAmountOptionFree None.
1281 *
1282 * Side effects:
1283 * Depends on the function.
1284 * PadAmountOptionSet Sets option value to new setting,
1285 * allocating a new integer array.
1286 * PadAmountOptionGet Creates a new Tcl_Obj.
1287 * PadAmountOptionRestore Resets option value to original value.
1288 * PadAmountOptionFree Free storage for internal rep.
1289 *
1290 *----------------------------------------------------------------------
1291 */
1292
1293 static int
PadAmountOptionSet(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)1294 PadAmountOptionSet(
1295 ClientData clientData, /* unused. */
1296 Tcl_Interp *interp, /* Interpreter for error reporting, or NULL,
1297 * if no error message is wanted. */
1298 Tk_Window tkwin, /* A window. Needed by Tk_GetPixels() */
1299 Tcl_Obj **valuePtr, /* The argument to "-padx", "-pady", "-ipadx",
1300 * or "-ipady". The thing to be parsed. */
1301 char *recordPtr, /* Pointer to start of widget record. */
1302 int internalOffset, /* Offset of internal representation or
1303 * -1, if no internal repr is wanted. */
1304 char *saveInternalPtr, /* Pointer to the place, where the saved
1305 * internal form (of type "int *") resides. */
1306 int flags /* Flags as specified in Tk_OptionSpec. */
1307 )
1308 {
1309 int objEmpty;
1310 int topLeft, bottomRight; /* The two components of the padding. */
1311 int *new; /* Pointer to the allocated array of integers
1312 * containing the parsed pad amounts. */
1313 int **internalPtr; /* Pointer to the place, where the internal
1314 * form (of type "int *") resides. */
1315
1316 objEmpty = ObjectIsEmpty((*valuePtr));
1317
1318 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
1319 (*valuePtr) = NULL;
1320 else {
1321 /*
1322 * Check that the given object indeed specifies a valid pad amount.
1323 */
1324
1325 if (TreeCtrl_GetPadAmountFromObj(interp, tkwin, *valuePtr,
1326 &topLeft, &bottomRight) != TCL_OK) {
1327 return TCL_ERROR;
1328 }
1329 }
1330
1331 /*
1332 * Store a pointer to an allocated array of the two padding values
1333 * into the widget record at the specified offset.
1334 */
1335
1336 if (internalOffset >= 0) {
1337 internalPtr = (int **) (recordPtr + internalOffset);
1338 *(int **) saveInternalPtr = *internalPtr;
1339 if (*valuePtr == NULL)
1340 new = NULL;
1341 else {
1342 new = (int *) ckalloc(2 * sizeof(int));
1343 new[PAD_TOP_LEFT] = topLeft;
1344 new[PAD_BOTTOM_RIGHT] = bottomRight;
1345 }
1346 *internalPtr = new;
1347 }
1348 return TCL_OK;
1349 }
1350
1351 static Tcl_Obj *
PadAmountOptionGet(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)1352 PadAmountOptionGet(
1353 ClientData clientData, /* unused. */
1354 Tk_Window tkwin, /* A window; unused. */
1355 char *recordPtr, /* Pointer to start of widget record. */
1356 int internalOffset /* Offset of internal representation. */
1357 )
1358 {
1359 int *padAmounts = *(int **)(recordPtr + internalOffset);
1360
1361 if (padAmounts == NULL)
1362 return NULL;
1363 return TreeCtrl_NewPadAmountObj(padAmounts);
1364 }
1365
1366 static void
PadAmountOptionRestore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)1367 PadAmountOptionRestore(
1368 ClientData clientData, /* unused. */
1369 Tk_Window tkwin, /* A window; unused. */
1370 char *internalPtr, /* Pointer to the place, where the internal
1371 * form (of type "int *") resides. */
1372 char *saveInternalPtr /* Pointer to the place, where the saved
1373 * internal form (of type "int *") resides. */
1374 )
1375 {
1376 *(int **) internalPtr = *(int **) saveInternalPtr;
1377 }
1378
1379 static void
PadAmountOptionFree(ClientData clientData,Tk_Window tkwin,char * internalPtr)1380 PadAmountOptionFree(
1381 ClientData clientData, /* unused. */
1382 Tk_Window tkwin, /* A window; unused */
1383 char *internalPtr /* Pointer to the place, where the internal
1384 * form (of type "int *") resides. */
1385 )
1386 {
1387 if (*(int **)internalPtr != NULL) {
1388 ckfree((char *) *(int **)internalPtr);
1389 }
1390 }
1391
1392 /*
1393 *----------------------------------------------------------------------
1394 *
1395 * ObjectIsEmpty --
1396 *
1397 * This procedure tests whether the string value of an object is
1398 * empty.
1399 *
1400 * Results:
1401 * The return value is 1 if the string value of objPtr has length
1402 * zero, and 0 otherwise.
1403 *
1404 * Side effects:
1405 * None.
1406 *
1407 *----------------------------------------------------------------------
1408 */
1409
1410 int
ObjectIsEmpty(Tcl_Obj * obj)1411 ObjectIsEmpty(
1412 Tcl_Obj *obj /* Object to test. May be NULL. */
1413 )
1414 {
1415 int length;
1416
1417 if (obj == NULL)
1418 return 1;
1419 if (obj->bytes != NULL)
1420 return (obj->length == 0);
1421 Tcl_GetStringFromObj(obj, &length);
1422 return (length == 0);
1423 }
1424
1425 #define PERSTATE_ROUNDUP 5
1426
1427 /*
1428 *----------------------------------------------------------------------
1429 *
1430 * PerStateInfo_Free --
1431 *
1432 * Frees memory and resources associated with a single per-state
1433 * option. pInfo is set to an empty state ready to be used again.
1434 *
1435 * Results:
1436 * None.
1437 *
1438 * Side effects:
1439 * Memory is deallocated. Colors, etc are freed.
1440 *
1441 *----------------------------------------------------------------------
1442 */
1443
1444 void
PerStateInfo_Free(TreeCtrl * tree,PerStateType * typePtr,PerStateInfo * pInfo)1445 PerStateInfo_Free(
1446 TreeCtrl *tree, /* Widget info. */
1447 PerStateType *typePtr, /* Type-specific functions and values. */
1448 PerStateInfo *pInfo /* Per-state info to free. */
1449 )
1450 {
1451 PerStateData *pData = pInfo->data;
1452 int i;
1453
1454 if (pInfo->data == NULL)
1455 return;
1456
1457 #ifdef TREECTRL_DEBUG
1458 if (pInfo->type != typePtr)
1459 panic("PerStateInfo_Free type mismatch: got %s expected %s",
1460 pInfo->type ? pInfo->type->name : "NULL", typePtr->name);
1461 #endif
1462 for (i = 0; i < pInfo->count; i++) {
1463 (*typePtr->freeProc)(tree, pData);
1464 pData = (PerStateData *) (((char *) pData) + typePtr->size);
1465 }
1466 #ifdef ALLOC_HAX
1467 TreeAlloc_CFree(tree->allocData, typePtr->name, (char *) pInfo->data,
1468 typePtr->size, pInfo->count, PERSTATE_ROUNDUP);
1469 #else
1470 WIPEFREE(pInfo->data, typePtr->size * pInfo->count);
1471 #endif
1472 pInfo->data = NULL;
1473 pInfo->count = 0;
1474 }
1475
1476 /*
1477 *----------------------------------------------------------------------
1478 *
1479 * PerStateInfo_FromObj --
1480 *
1481 * Parse a Tcl_Obj to an array of PerStateData. The current data
1482 * is freed (if any). If the Tcl_Obj is NULL then pInfo is left in
1483 * an empty state ready to be used again.
1484 *
1485 * Results:
1486 * A standard Tcl result.
1487 *
1488 * Side effects:
1489 * Memory is allocated/deallocated.
1490 *
1491 *----------------------------------------------------------------------
1492 */
1493
1494 int
PerStateInfo_FromObj(TreeCtrl * tree,int domain,StateFromObjProc proc,PerStateType * typePtr,PerStateInfo * pInfo)1495 PerStateInfo_FromObj(
1496 TreeCtrl *tree, /* Widget info. */
1497 int domain, /* STATE_DOMAIN_XXX index. */
1498 StateFromObjProc proc, /* Procedure used to turn a Tcl_Obj into
1499 * a state bit-flag. */
1500 PerStateType *typePtr, /* Type-specific functions and values. */
1501 PerStateInfo *pInfo /* Per-state info to return. pInfo->obj
1502 * must be NULL or point to a valid Tcl_Obj. */
1503 )
1504 {
1505 int i, j;
1506 int objc, objc2;
1507 Tcl_Obj **objv, **objv2;
1508 PerStateData *pData;
1509
1510 #ifdef TREECTRL_DEBUG
1511 pInfo->type = typePtr;
1512 #endif
1513
1514 PerStateInfo_Free(tree, typePtr, pInfo);
1515
1516 if (pInfo->obj == NULL)
1517 return TCL_OK;
1518
1519 if (Tcl_ListObjGetElements(tree->interp, pInfo->obj, &objc, &objv) != TCL_OK)
1520 return TCL_ERROR;
1521
1522 if (objc == 0)
1523 return TCL_OK;
1524
1525 if (objc == 1) {
1526 #ifdef ALLOC_HAX
1527 pData = (PerStateData *) TreeAlloc_CAlloc(tree->allocData,
1528 typePtr->name, typePtr->size, 1, PERSTATE_ROUNDUP);
1529 #else
1530 pData = (PerStateData *) ckalloc(typePtr->size);
1531 #endif
1532 pData->stateOff = pData->stateOn = 0; /* all states */
1533 if ((*typePtr->fromObjProc)(tree, objv[0], pData) != TCL_OK) {
1534 #ifdef ALLOC_HAX
1535 TreeAlloc_CFree(tree->allocData, typePtr->name, (char *) pData,
1536 typePtr->size, 1, PERSTATE_ROUNDUP);
1537 #else
1538 WIPEFREE(pData, typePtr->size);
1539 #endif
1540 return TCL_ERROR;
1541 }
1542 pInfo->data = pData;
1543 pInfo->count = 1;
1544 return TCL_OK;
1545 }
1546
1547 if (objc & 1) {
1548 FormatResult(tree->interp, "list must have even number of elements");
1549 return TCL_ERROR;
1550 }
1551
1552 #ifdef ALLOC_HAX
1553 pData = (PerStateData *) TreeAlloc_CAlloc(tree->allocData,
1554 typePtr->name, typePtr->size, objc / 2, PERSTATE_ROUNDUP);
1555 #else
1556 pData = (PerStateData *) ckalloc(typePtr->size * (objc / 2));
1557 #endif
1558 pInfo->data = pData;
1559 for (i = 0; i < objc; i += 2) {
1560 if ((*typePtr->fromObjProc)(tree, objv[i], pData) != TCL_OK) {
1561 goto freeIt;
1562 }
1563 pInfo->count++;
1564 if (Tcl_ListObjGetElements(tree->interp, objv[i + 1], &objc2, &objv2) != TCL_OK) {
1565 goto freeIt;
1566 }
1567 pData->stateOff = pData->stateOn = 0; /* all states */
1568 for (j = 0; j < objc2; j++) {
1569 if (proc(tree, domain, objv2[j], &pData->stateOff, &pData->stateOn) != TCL_OK) {
1570 goto freeIt;
1571 }
1572 }
1573 pData = (PerStateData *) (((char *) pData) + typePtr->size);
1574 }
1575 return TCL_OK;
1576
1577 freeIt:
1578 pData = pInfo->data;
1579 for (i = 0; i < pInfo->count; i++) {
1580 (*typePtr->freeProc)(tree, pData);
1581 pData = (PerStateData *) (((char *) pData) + typePtr->size);
1582 }
1583 #ifdef ALLOC_HAX
1584 TreeAlloc_CFree(tree->allocData, typePtr->name, (char *) pInfo->data,
1585 typePtr->size, objc / 2, PERSTATE_ROUNDUP);
1586 #else
1587 WIPEFREE(pInfo->data, typePtr->size * (objc / 2));
1588 #endif
1589 pInfo->data = NULL;
1590 pInfo->count = 0;
1591 return TCL_ERROR;
1592 }
1593
1594 /*
1595 *----------------------------------------------------------------------
1596 *
1597 * PerStateInfo_ForState --
1598 *
1599 * Return the best-matching PerStateData for a given state.
1600 *
1601 * Results:
1602 * The return value is a pointer to the best-matching PerStateData.
1603 * *match is set to a MATCH_xxx constant. NULL is returned if
1604 * no appropriate PerStateData was found.
1605 *
1606 * Side effects:
1607 * None.
1608 *
1609 *----------------------------------------------------------------------
1610 */
1611
1612 PerStateData *
PerStateInfo_ForState(TreeCtrl * tree,PerStateType * typePtr,PerStateInfo * pInfo,int state,int * match)1613 PerStateInfo_ForState(
1614 TreeCtrl *tree, /* Widget info. */
1615 PerStateType *typePtr, /* Type-specific functions and values. */
1616 PerStateInfo *pInfo, /* Per-state info to search. */
1617 int state, /* State bit-flags to compare. */
1618 int *match /* Returned MATCH_xxx constant. */
1619 )
1620 {
1621 PerStateData *pData = pInfo->data;
1622 int stateOff = ~state, stateOn = state;
1623 int i;
1624
1625 #ifdef TREECTRL_DEBUG
1626 if ((pInfo->data != NULL) && (pInfo->type != typePtr)) {
1627 panic("PerStateInfo_ForState type mismatch: got %s expected %s",
1628 pInfo->type ? pInfo->type->name : "NULL", typePtr->name);
1629 }
1630 #endif
1631
1632 for (i = 0; i < pInfo->count; i++) {
1633 /* Any state */
1634 if ((pData->stateOff == 0) &&
1635 (pData->stateOn == 0)) {
1636 if (match) (*match) = MATCH_ANY;
1637 return pData;
1638 }
1639
1640 /* Exact match */
1641 if ((pData->stateOff == stateOff) &&
1642 (pData->stateOn == stateOn)) {
1643 if (match) (*match) = MATCH_EXACT;
1644 return pData;
1645 }
1646
1647 /* Partial match */
1648 if (((pData->stateOff & stateOff) == pData->stateOff) &&
1649 ((pData->stateOn & stateOn) == pData->stateOn)) {
1650 if (match) (*match) = MATCH_PARTIAL;
1651 return pData;
1652 }
1653
1654 pData = (PerStateData *) (((char *) pData) + typePtr->size);
1655 }
1656
1657 if (match) (*match) = MATCH_NONE;
1658 return NULL;
1659 }
1660
1661 /*
1662 *----------------------------------------------------------------------
1663 *
1664 * PerStateInfo_ObjForState --
1665 *
1666 * Return a Tcl_Obj from the list object that was parsed by
1667 * PerStateInfo_FromObj(). pInfo is searched for the best-matching
1668 * PerStateData for the given state and the object used to
1669 * create that PerStateData is returned.
1670 *
1671 * Results:
1672 * *match is set to a MATCH_xxx constant. NULL is returned if
1673 * no appropriate PerStateData was found. The object should not
1674 * be modified by the caller.
1675 *
1676 * Side effects:
1677 * None.
1678 *
1679 *----------------------------------------------------------------------
1680 */
1681
1682 Tcl_Obj *
PerStateInfo_ObjForState(TreeCtrl * tree,PerStateType * typePtr,PerStateInfo * pInfo,int state,int * match)1683 PerStateInfo_ObjForState(
1684 TreeCtrl *tree, /* Widget info. */
1685 PerStateType *typePtr, /* Type-specific functions and values. */
1686 PerStateInfo *pInfo, /* Per-state info to search. */
1687 int state, /* State bit-flags to compare. */
1688 int *match /* Returned MATCH_xxx constant. */
1689 )
1690 {
1691 PerStateData *pData;
1692 Tcl_Obj *obj;
1693 int i;
1694
1695 #ifdef TREECTRL_DEBUG
1696 if ((pInfo->data != NULL) && (pInfo->type != typePtr))
1697 panic("PerStateInfo_ObjForState type mismatch: got %s expected %s",
1698 pInfo->type ? pInfo->type->name : "NULL", typePtr->name);
1699 #endif
1700
1701 pData = PerStateInfo_ForState(tree, typePtr, pInfo, state, match);
1702 if (pData != NULL) {
1703 i = (int) ((char *) pData - (char *) pInfo->data) / typePtr->size;
1704 Tcl_ListObjIndex(tree->interp, pInfo->obj, i * 2, &obj);
1705 return obj;
1706 }
1707
1708 return NULL;
1709 }
1710
1711 static Tcl_Obj *
DuplicateListObj(Tcl_Obj * objPtr)1712 DuplicateListObj(
1713 Tcl_Obj *objPtr
1714 )
1715 {
1716 int objc;
1717 Tcl_Obj **objv;
1718 int result;
1719
1720 /*
1721 * Comment from TclLsetFlat:
1722 * ... A plain Tcl_DuplicateObj
1723 * will just increase the intrep's refCount without upping the sublists'
1724 * refCount, so that their true shared status cannot be determined from
1725 * their refCount.
1726 */
1727
1728 result = Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv);
1729 return Tcl_NewListObj(objc, objv);
1730 }
1731
1732 /*
1733 *----------------------------------------------------------------------
1734 *
1735 * PerStateInfo_Undefine --
1736 *
1737 * Called when a user-defined state flag is undefined. The state
1738 * flag is cleared from every PerStateData using that flag. The
1739 * list object that was parsed by PerStateInfo_FromObj() is modified
1740 * by removing any reference to the undefined state.
1741 *
1742 * Results:
1743 * The return value is a boolean indicating whether or not pInfo
1744 * was modified.
1745 *
1746 * Side effects:
1747 * The list object pointed to by pInfo->obj may be recreated.
1748 *
1749 *----------------------------------------------------------------------
1750 */
1751
1752 int
PerStateInfo_Undefine(TreeCtrl * tree,PerStateType * typePtr,PerStateInfo * pInfo,int domain,int state)1753 PerStateInfo_Undefine(
1754 TreeCtrl *tree, /* Widget info. */
1755 PerStateType *typePtr, /* Type-specific functions and values. */
1756 PerStateInfo *pInfo, /* Per-state info to modify. */
1757 int domain, /* STATE_DOMAIN_XXX index. */
1758 int state /* State bit-flag that was undefined. */
1759 )
1760 {
1761 PerStateData *pData = pInfo->data;
1762 int i, j, numStates, stateOff, stateOn;
1763 Tcl_Obj *configObj = pInfo->obj, *listObj, *stateObj;
1764 int modified = 0;
1765
1766 #ifdef TREECTRL_DEBUG
1767 if ((pInfo->data != NULL) && (pInfo->type != typePtr))
1768 panic("PerStateInfo_Undefine type mismatch: got %s expected %s",
1769 pInfo->type ? pInfo->type->name : "NULL", typePtr->name);
1770 #endif
1771
1772 for (i = 0; i < pInfo->count; i++) {
1773 if ((pData->stateOff | pData->stateOn) & state) {
1774 pData->stateOff &= ~state;
1775 pData->stateOn &= ~state;
1776 if (Tcl_IsShared(configObj)) {
1777 configObj = DuplicateListObj(configObj);
1778 Tcl_DecrRefCount(pInfo->obj);
1779 Tcl_IncrRefCount(configObj);
1780 pInfo->obj = configObj;
1781 }
1782 Tcl_ListObjIndex(tree->interp, configObj, i * 2 + 1, &listObj);
1783 if (Tcl_IsShared(listObj)) {
1784 listObj = DuplicateListObj(listObj);
1785 Tcl_ListObjReplace(tree->interp, configObj, i * 2 + 1, 1, 1, &listObj);
1786 }
1787 Tcl_ListObjLength(tree->interp, listObj, &numStates);
1788 for (j = 0; j < numStates; ) {
1789 Tcl_ListObjIndex(tree->interp, listObj, j, &stateObj);
1790 stateOff = stateOn = 0;
1791 TreeStateFromObj(tree, domain, stateObj, &stateOff, &stateOn); /* FIXME: why this proc? */
1792 if ((stateOff | stateOn) & state) {
1793 Tcl_ListObjReplace(tree->interp, listObj, j, 1, 0, NULL);
1794 numStates--;
1795 } else
1796 j++;
1797 }
1798 /* Given {bitmap {state1 state2 state3}}, we just invalidated
1799 * the string rep of the sublist {state1 state2 state3}, but not
1800 * the parent list. */
1801 Tcl_InvalidateStringRep(configObj);
1802 modified = 1;
1803 }
1804 pData = (PerStateData *) (((char *) pData) + typePtr->size);
1805 }
1806
1807 return modified;
1808 }
1809
1810 /*****/
1811
1812 GC
Tree_GetGC(TreeCtrl * tree,unsigned long mask,XGCValues * gcValues)1813 Tree_GetGC(
1814 TreeCtrl *tree,
1815 unsigned long mask,
1816 XGCValues *gcValues)
1817 {
1818 GCCache *pGC;
1819 unsigned long valid = GCBackground | GCDashList | GCDashOffset | GCFont |
1820 GCForeground | GCFunction | GCGraphicsExposures | GCLineStyle;
1821
1822 if ((mask | valid) != valid)
1823 panic("Tree_GetGC: unsupported mask");
1824
1825 for (pGC = tree->gcCache; pGC != NULL; pGC = pGC->next) {
1826 if (mask != pGC->mask)
1827 continue;
1828 if ((mask & GCBackground) &&
1829 (pGC->gcValues.background != gcValues->background))
1830 continue;
1831 if ((mask & GCDashList) &&
1832 (pGC->gcValues.dashes != gcValues->dashes)) /* FIXME: single value */
1833 continue;
1834 if ((mask & GCDashOffset) &&
1835 (pGC->gcValues.dash_offset != gcValues->dash_offset))
1836 continue;
1837 if ((mask & GCFont) &&
1838 (pGC->gcValues.font != gcValues->font))
1839 continue;
1840 if ((mask & GCForeground) &&
1841 (pGC->gcValues.foreground != gcValues->foreground))
1842 continue;
1843 if ((mask & GCFunction) &&
1844 (pGC->gcValues.function != gcValues->function))
1845 continue;
1846 if ((mask & GCGraphicsExposures) &&
1847 (pGC->gcValues.graphics_exposures != gcValues->graphics_exposures))
1848 continue;
1849 return pGC->gc;
1850 }
1851
1852 pGC = (GCCache *) ckalloc(sizeof(*pGC));
1853 pGC->gcValues = (*gcValues);
1854 pGC->mask = mask;
1855 pGC->gc = Tk_GetGC(tree->tkwin, mask, gcValues);
1856 pGC->next = tree->gcCache;
1857 tree->gcCache = pGC;
1858
1859 return pGC->gc;
1860 }
1861
1862 void
Tree_FreeAllGC(TreeCtrl * tree)1863 Tree_FreeAllGC(
1864 TreeCtrl *tree)
1865 {
1866 GCCache *pGC = tree->gcCache, *next;
1867
1868 while (pGC != NULL) {
1869 next = pGC->next;
1870 Tk_FreeGC(tree->display, pGC->gc);
1871 WFREE(pGC, GCCache);
1872 pGC = next;
1873 }
1874 tree->gcCache = NULL;
1875 }
1876
1877 /*****/
1878
1879 typedef struct PerStateDataBitmap PerStateDataBitmap;
1880 struct PerStateDataBitmap
1881 {
1882 PerStateData header;
1883 Pixmap bitmap;
1884 };
1885
1886 static int
PSDBitmapFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataBitmap * pBitmap)1887 PSDBitmapFromObj(
1888 TreeCtrl *tree,
1889 Tcl_Obj *obj,
1890 PerStateDataBitmap *pBitmap)
1891 {
1892 if (ObjectIsEmpty(obj)) {
1893 /* Specify empty string to override masterX */
1894 pBitmap->bitmap = None;
1895 } else {
1896 pBitmap->bitmap = Tk_AllocBitmapFromObj(tree->interp, tree->tkwin, obj);
1897 if (pBitmap->bitmap == None)
1898 return TCL_ERROR;
1899 }
1900 return TCL_OK;
1901 }
1902
1903 static void
PSDBitmapFree(TreeCtrl * tree,PerStateDataBitmap * pBitmap)1904 PSDBitmapFree(
1905 TreeCtrl *tree,
1906 PerStateDataBitmap *pBitmap)
1907 {
1908 if (pBitmap->bitmap != None)
1909 Tk_FreeBitmap(tree->display, pBitmap->bitmap);
1910 }
1911
1912 PerStateType pstBitmap =
1913 {
1914 "pstBitmap",
1915 sizeof(PerStateDataBitmap),
1916 (PerStateType_FromObjProc) PSDBitmapFromObj,
1917 (PerStateType_FreeProc) PSDBitmapFree
1918 };
1919
1920 Pixmap
PerStateBitmap_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)1921 PerStateBitmap_ForState(
1922 TreeCtrl *tree,
1923 PerStateInfo *pInfo,
1924 int state,
1925 int *match)
1926 {
1927 PerStateDataBitmap *pData;
1928
1929 pData = (PerStateDataBitmap *) PerStateInfo_ForState(tree, &pstBitmap, pInfo, state, match);
1930 if (pData != NULL)
1931 return pData->bitmap;
1932 return None;
1933 }
1934
1935 void
PerStateBitmap_MaxSize(TreeCtrl * tree,PerStateInfo * pInfo,int * widthPtr,int * heightPtr)1936 PerStateBitmap_MaxSize(
1937 TreeCtrl *tree,
1938 PerStateInfo *pInfo,
1939 int *widthPtr,
1940 int *heightPtr)
1941 {
1942 PerStateDataBitmap *pData = (PerStateDataBitmap *) pInfo->data;
1943 int i, width, height, w, h;
1944
1945 width = height = 0;
1946
1947 for (i = 0; i < pInfo->count; i++, ++pData) {
1948 if (pData->bitmap == None)
1949 continue;
1950 Tk_SizeOfBitmap(tree->display, pData->bitmap, &w, &h);
1951 width = MAX(width, w);
1952 height = MAX(height, h);
1953 }
1954
1955 (*widthPtr) = width;
1956 (*heightPtr) = height;
1957 }
1958
1959 /*****/
1960
1961 typedef struct PerStateDataBoolean PerStateDataBoolean;
1962 struct PerStateDataBoolean
1963 {
1964 PerStateData header;
1965 int value;
1966 };
1967
1968 static int
PSDBooleanFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataBoolean * pBoolean)1969 PSDBooleanFromObj(
1970 TreeCtrl *tree,
1971 Tcl_Obj *obj,
1972 PerStateDataBoolean *pBoolean)
1973 {
1974 if (ObjectIsEmpty(obj)) {
1975 pBoolean->value = -1;
1976 } else {
1977 if (Tcl_GetBooleanFromObj(tree->interp, obj, &pBoolean->value) != TCL_OK)
1978 return TCL_ERROR;
1979 }
1980 return TCL_OK;
1981 }
1982
1983 static void
PSDBooleanFree(TreeCtrl * tree,PerStateDataBoolean * pBoolean)1984 PSDBooleanFree(
1985 TreeCtrl *tree,
1986 PerStateDataBoolean *pBoolean)
1987 {
1988 }
1989
1990 PerStateType pstBoolean =
1991 {
1992 "pstBoolean",
1993 sizeof(PerStateDataBoolean),
1994 (PerStateType_FromObjProc) PSDBooleanFromObj,
1995 (PerStateType_FreeProc) PSDBooleanFree
1996 };
1997
1998 int
PerStateBoolean_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)1999 PerStateBoolean_ForState(
2000 TreeCtrl *tree,
2001 PerStateInfo *pInfo,
2002 int state,
2003 int *match)
2004 {
2005 PerStateDataBoolean *pData;
2006
2007 pData = (PerStateDataBoolean *) PerStateInfo_ForState(tree, &pstBoolean, pInfo, state, match);
2008 if (pData != NULL)
2009 return pData->value;
2010 return -1;
2011 }
2012
2013 /*****/
2014
2015 typedef struct PerStateDataBorder PerStateDataBorder;
2016 struct PerStateDataBorder
2017 {
2018 PerStateData header;
2019 Tk_3DBorder border;
2020 };
2021
2022 static int
PSDBorderFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataBorder * pBorder)2023 PSDBorderFromObj(
2024 TreeCtrl *tree,
2025 Tcl_Obj *obj,
2026 PerStateDataBorder *pBorder)
2027 {
2028 if (ObjectIsEmpty(obj)) {
2029 /* Specify empty string to override masterX */
2030 pBorder->border = NULL;
2031 } else {
2032 pBorder->border = Tk_Alloc3DBorderFromObj(tree->interp, tree->tkwin, obj);
2033 if (pBorder->border == NULL)
2034 return TCL_ERROR;
2035 }
2036 return TCL_OK;
2037 }
2038
2039 static void
PSDBorderFree(TreeCtrl * tree,PerStateDataBorder * pBorder)2040 PSDBorderFree(
2041 TreeCtrl *tree,
2042 PerStateDataBorder *pBorder)
2043 {
2044 if (pBorder->border != NULL)
2045 Tk_Free3DBorder(pBorder->border);
2046 }
2047
2048 PerStateType pstBorder =
2049 {
2050 "pstBorder",
2051 sizeof(PerStateDataBorder),
2052 (PerStateType_FromObjProc) PSDBorderFromObj,
2053 (PerStateType_FreeProc) PSDBorderFree
2054 };
2055
2056 Tk_3DBorder
PerStateBorder_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2057 PerStateBorder_ForState(
2058 TreeCtrl *tree,
2059 PerStateInfo *pInfo,
2060 int state,
2061 int *match)
2062 {
2063 PerStateDataBorder *pData;
2064
2065 pData = (PerStateDataBorder *) PerStateInfo_ForState(tree, &pstBorder, pInfo, state, match);
2066 if (pData != NULL)
2067 return pData->border;
2068 return NULL;
2069 }
2070
2071 /*****/
2072
2073 typedef struct PerStateDataColor PerStateDataColor;
2074 struct PerStateDataColor
2075 {
2076 PerStateData header;
2077 TreeColor *color;
2078 };
2079
2080 static int
PSDColorFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataColor * pColor)2081 PSDColorFromObj(
2082 TreeCtrl *tree,
2083 Tcl_Obj *obj,
2084 PerStateDataColor *pColor)
2085 {
2086 if (ObjectIsEmpty(obj)) {
2087 /* Specify empty string to override masterX */
2088 pColor->color = NULL;
2089 } else {
2090 pColor->color = Tree_AllocColorFromObj(tree, obj);
2091 if (pColor->color == NULL)
2092 return TCL_ERROR;
2093 }
2094 return TCL_OK;
2095 }
2096
2097 static void
PSDColorFree(TreeCtrl * tree,PerStateDataColor * pColor)2098 PSDColorFree(
2099 TreeCtrl *tree,
2100 PerStateDataColor *pColor)
2101 {
2102 if (pColor->color != NULL)
2103 Tree_FreeColor(tree, pColor->color);
2104 }
2105
2106 PerStateType pstColor =
2107 {
2108 "pstColor",
2109 sizeof(PerStateDataColor),
2110 (PerStateType_FromObjProc) PSDColorFromObj,
2111 (PerStateType_FreeProc) PSDColorFree
2112 };
2113
2114 TreeColor *
PerStateColor_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2115 PerStateColor_ForState(
2116 TreeCtrl *tree,
2117 PerStateInfo *pInfo,
2118 int state,
2119 int *match)
2120 {
2121 PerStateDataColor *pData;
2122
2123 pData = (PerStateDataColor *) PerStateInfo_ForState(tree, &pstColor, pInfo, state, match);
2124 if (pData != NULL)
2125 return pData->color;
2126 return NULL;
2127 }
2128
2129 /*****/
2130
2131 typedef struct PerStateDataFont PerStateDataFont;
2132 struct PerStateDataFont
2133 {
2134 PerStateData header;
2135 Tk_Font tkfont;
2136 };
2137
2138 static int
PSDFontFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataFont * pFont)2139 PSDFontFromObj(
2140 TreeCtrl *tree,
2141 Tcl_Obj *obj,
2142 PerStateDataFont *pFont)
2143 {
2144 if (ObjectIsEmpty(obj)) {
2145 /* Specify empty string to override masterX */
2146 pFont->tkfont = NULL;
2147 } else {
2148 pFont->tkfont = Tk_AllocFontFromObj(tree->interp, tree->tkwin, obj);
2149 if (pFont->tkfont == NULL)
2150 return TCL_ERROR;
2151 }
2152 return TCL_OK;
2153 }
2154
2155 static void
PSDFontFree(TreeCtrl * tree,PerStateDataFont * pFont)2156 PSDFontFree(
2157 TreeCtrl *tree,
2158 PerStateDataFont *pFont)
2159 {
2160 if (pFont->tkfont != NULL)
2161 Tk_FreeFont(pFont->tkfont);
2162 }
2163
2164 PerStateType pstFont =
2165 {
2166 "pstFont",
2167 sizeof(PerStateDataFont),
2168 (PerStateType_FromObjProc) PSDFontFromObj,
2169 (PerStateType_FreeProc) PSDFontFree
2170 };
2171
2172 Tk_Font
PerStateFont_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2173 PerStateFont_ForState(
2174 TreeCtrl *tree,
2175 PerStateInfo *pInfo,
2176 int state,
2177 int *match)
2178 {
2179 PerStateDataFont *pData;
2180
2181 pData = (PerStateDataFont *) PerStateInfo_ForState(tree, &pstFont, pInfo, state, match);
2182 if (pData != NULL)
2183 return pData->tkfont;
2184 return NULL;
2185 }
2186
2187 /*****/
2188
2189 typedef struct PerStateDataImage PerStateDataImage;
2190 struct PerStateDataImage
2191 {
2192 PerStateData header;
2193 Tk_Image image;
2194 char *string;
2195 };
2196
2197 static int
PSDImageFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataImage * pImage)2198 PSDImageFromObj(
2199 TreeCtrl *tree,
2200 Tcl_Obj *obj,
2201 PerStateDataImage *pImage)
2202 {
2203 int length;
2204 char *string;
2205
2206 if (ObjectIsEmpty(obj)) {
2207 /* Specify empty string to override masterX */
2208 pImage->image = NULL;
2209 pImage->string = NULL;
2210 } else {
2211 string = Tcl_GetStringFromObj(obj, &length);
2212 pImage->image = Tree_GetImage(tree, string);
2213 if (pImage->image == NULL)
2214 return TCL_ERROR;
2215 pImage->string = ckalloc(length + 1);
2216 strcpy(pImage->string, string);
2217 }
2218 return TCL_OK;
2219 }
2220
2221 static void
PSDImageFree(TreeCtrl * tree,PerStateDataImage * pImage)2222 PSDImageFree(
2223 TreeCtrl *tree,
2224 PerStateDataImage *pImage)
2225 {
2226 if (pImage->string != NULL)
2227 ckfree(pImage->string);
2228 if (pImage->image != NULL)
2229 Tree_FreeImage(tree, pImage->image);
2230 }
2231
2232 PerStateType pstImage =
2233 {
2234 "pstImage",
2235 sizeof(PerStateDataImage),
2236 (PerStateType_FromObjProc) PSDImageFromObj,
2237 (PerStateType_FreeProc) PSDImageFree
2238 };
2239
2240 Tk_Image
PerStateImage_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2241 PerStateImage_ForState(
2242 TreeCtrl *tree,
2243 PerStateInfo *pInfo,
2244 int state,
2245 int *match)
2246 {
2247 PerStateDataImage *pData;
2248
2249 pData = (PerStateDataImage *) PerStateInfo_ForState(tree, &pstImage, pInfo, state, match);
2250 if (pData != NULL)
2251 return pData->image;
2252 return NULL;
2253 }
2254
2255 void
PerStateImage_MaxSize(TreeCtrl * tree,PerStateInfo * pInfo,int * widthPtr,int * heightPtr)2256 PerStateImage_MaxSize(
2257 TreeCtrl *tree,
2258 PerStateInfo *pInfo,
2259 int *widthPtr,
2260 int *heightPtr)
2261 {
2262 PerStateDataImage *pData = (PerStateDataImage *) pInfo->data;
2263 int i, width, height, w, h;
2264
2265 width = height = 0;
2266
2267 for (i = 0; i < pInfo->count; i++, ++pData) {
2268 if (pData->image == None)
2269 continue;
2270 Tk_SizeOfImage(pData->image, &w, &h);
2271 width = MAX(width, w);
2272 height = MAX(height, h);
2273 }
2274
2275 (*widthPtr) = width;
2276 (*heightPtr) = height;
2277 }
2278
2279 /*****/
2280
2281 typedef struct PerStateDataRelief PerStateDataRelief;
2282 struct PerStateDataRelief
2283 {
2284 PerStateData header;
2285 int relief;
2286 };
2287
2288 static int
PSDReliefFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataRelief * pRelief)2289 PSDReliefFromObj(
2290 TreeCtrl *tree,
2291 Tcl_Obj *obj,
2292 PerStateDataRelief *pRelief)
2293 {
2294 if (ObjectIsEmpty(obj)) {
2295 /* Specify empty string to override masterX */
2296 pRelief->relief = TK_RELIEF_NULL;
2297 } else {
2298 if (Tk_GetReliefFromObj(tree->interp, obj, &pRelief->relief) != TCL_OK)
2299 return TCL_ERROR;
2300 }
2301 return TCL_OK;
2302 }
2303
2304 static void
PSDReliefFree(TreeCtrl * tree,PerStateDataRelief * pRelief)2305 PSDReliefFree(
2306 TreeCtrl *tree,
2307 PerStateDataRelief *pRelief)
2308 {
2309 }
2310
2311 PerStateType pstRelief =
2312 {
2313 "pstRelief",
2314 sizeof(PerStateDataRelief),
2315 (PerStateType_FromObjProc) PSDReliefFromObj,
2316 (PerStateType_FreeProc) PSDReliefFree
2317 };
2318
2319 int
PerStateRelief_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2320 PerStateRelief_ForState(
2321 TreeCtrl *tree,
2322 PerStateInfo *pInfo,
2323 int state,
2324 int *match)
2325 {
2326 PerStateDataRelief *pData;
2327
2328 pData = (PerStateDataRelief *) PerStateInfo_ForState(tree, &pstRelief, pInfo, state, match);
2329 if (pData != NULL)
2330 return pData->relief;
2331 return TK_RELIEF_NULL;
2332 }
2333
2334 /*****/
2335
2336 int
Tree_GetFlagsFromString(TreeCtrl * tree,const char * string,int length,const char * typeStr,const CharFlag flags[],int * flagsPtr)2337 Tree_GetFlagsFromString(
2338 TreeCtrl *tree,
2339 const char *string,
2340 int length,
2341 const char *typeStr,
2342 const CharFlag flags[],
2343 int *flagsPtr
2344 )
2345 {
2346 int i, j, bits = 0, allBits = 0, numFlags = 0;
2347
2348 for (j = 0; flags[j].flagChar != '\0'; j++) {
2349 allBits |= flags[j].flagBit;
2350 numFlags++;
2351 }
2352
2353 for (i = 0; i < length; i++) {
2354 for (j = 0; flags[j].flagChar != '\0'; j++) {
2355 if (string[i] == flags[j].flagChar
2356 || string[i] == toupper(flags[j].flagChar)) {
2357 bits |= flags[j].flagBit;
2358 break;
2359 }
2360 }
2361 if (flags[j].flagChar == '\0') {
2362 Tcl_ResetResult(tree->interp);
2363 Tcl_AppendResult(tree->interp, "bad ", typeStr, " \"",
2364 string, "\": must be a string ",
2365 "containing zero or more of ",
2366 (char *) NULL);
2367 for (j = 0; flags[j].flagChar != '\0'; j++) {
2368 char buf[8];
2369 if (flags[j+1].flagChar != '\0')
2370 (void) sprintf(buf, "%c%s ", flags[j].flagChar,
2371 (numFlags > 2) ? "," : "");
2372 else
2373 (void) sprintf(buf, "and %c", flags[j].flagChar);
2374 Tcl_AppendResult(tree->interp, buf, (char *) NULL);
2375 }
2376 return TCL_ERROR;
2377 }
2378 }
2379
2380 (*flagsPtr) &= ~allBits;
2381 (*flagsPtr) |= bits;
2382
2383 return TCL_OK;
2384 }
2385
2386 int
Tree_GetFlagsFromObj(TreeCtrl * tree,Tcl_Obj * obj,const char * typeStr,const CharFlag flags[],int * flagsPtr)2387 Tree_GetFlagsFromObj(
2388 TreeCtrl *tree,
2389 Tcl_Obj *obj,
2390 const char *typeStr,
2391 const CharFlag flags[],
2392 int *flagsPtr
2393 )
2394 {
2395 int length;
2396 char *string;
2397
2398 string = Tcl_GetStringFromObj(obj, &length);
2399 return Tree_GetFlagsFromString(tree, string, length, typeStr, flags,
2400 flagsPtr);
2401 }
2402
2403 /*****/
2404
2405 /* The rect element's -open option */
2406 typedef struct PerStateDataFlags PerStateDataFlags;
2407 struct PerStateDataFlags
2408 {
2409 PerStateData header;
2410 int flags;
2411 };
2412
2413 static int
PSDFlagsFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataFlags * pFlags)2414 PSDFlagsFromObj(
2415 TreeCtrl *tree,
2416 Tcl_Obj *obj,
2417 PerStateDataFlags *pFlags)
2418 {
2419 if (ObjectIsEmpty(obj)) {
2420 pFlags->flags = 0xFFFFFFFF;
2421 } else {
2422 static const CharFlag openFlags[] = {
2423 { 'n', RECT_OPEN_N },
2424 { 'e', RECT_OPEN_E },
2425 { 's', RECT_OPEN_S },
2426 { 'w', RECT_OPEN_W },
2427 { 0, 0 }
2428 };
2429 pFlags->flags = 0;
2430 if (Tree_GetFlagsFromObj(tree, obj, "open value", openFlags,
2431 &pFlags->flags) != TCL_OK) {
2432 return TCL_ERROR;
2433 }
2434 }
2435 return TCL_OK;
2436 }
2437
2438 static void
PSDFlagsFree(TreeCtrl * tree,PerStateDataFlags * pFlags)2439 PSDFlagsFree(
2440 TreeCtrl *tree,
2441 PerStateDataFlags *pFlags)
2442 {
2443 }
2444
2445 PerStateType pstFlags =
2446 {
2447 "pstFlags",
2448 sizeof(PerStateDataFlags),
2449 (PerStateType_FromObjProc) PSDFlagsFromObj,
2450 (PerStateType_FreeProc) PSDFlagsFree
2451 };
2452
2453 int
PerStateFlags_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)2454 PerStateFlags_ForState(
2455 TreeCtrl *tree,
2456 PerStateInfo *pInfo,
2457 int state,
2458 int *match)
2459 {
2460 PerStateDataFlags *pData;
2461
2462 pData = (PerStateDataFlags *) PerStateInfo_ForState(tree, &pstFlags, pInfo, state, match);
2463 if (pData != NULL)
2464 return pData->flags;
2465 return 0xFFFFFFFF;
2466 }
2467
2468 /*****/
2469
2470 void
PSTSave(PerStateInfo * pInfo,PerStateInfo * pSave)2471 PSTSave(
2472 PerStateInfo *pInfo,
2473 PerStateInfo *pSave)
2474 {
2475 #ifdef TREECTRL_DEBUG
2476 pSave->type = pInfo->type; /* could be NULL */
2477 #endif
2478 pSave->data = pInfo->data;
2479 pSave->count = pInfo->count;
2480 pInfo->data = NULL;
2481 pInfo->count = 0;
2482 }
2483
2484 void
PSTRestore(TreeCtrl * tree,PerStateType * typePtr,PerStateInfo * pInfo,PerStateInfo * pSave)2485 PSTRestore(
2486 TreeCtrl *tree,
2487 PerStateType *typePtr,
2488 PerStateInfo *pInfo,
2489 PerStateInfo *pSave)
2490 {
2491 PerStateInfo_Free(tree, typePtr, pInfo);
2492 pInfo->data = pSave->data;
2493 pInfo->count = pSave->count;
2494 }
2495
2496 #ifdef ALLOC_HAX
2497
2498 /*
2499 * The following TreeAlloc_xxx calls implement a mini memory allocator that
2500 * allocates blocks of same-sized chunks, and holds on to those chunks when
2501 * they are freed so they can be reused quickly. If you don't want to use it
2502 * just comment out #define ALLOC_HAX in tkTreeCtrl.h.
2503 */
2504
2505 typedef struct AllocElem AllocElem;
2506 typedef struct AllocBlock AllocBlock;
2507 typedef struct AllocList AllocList;
2508 typedef struct AllocData AllocData;
2509
2510 #ifdef TREECTRL_DEBUG
2511 #define ALLOC_STATS
2512 #endif
2513
2514 #ifdef ALLOC_STATS
2515 typedef struct AllocStats AllocStats;
2516 #endif
2517
2518 /*
2519 * One of the following structures exists for each client piece of memory.
2520 * These structures are allocated in arrays (blocks).
2521 */
2522 struct AllocElem
2523 {
2524 AllocElem *next;
2525 #ifdef TREECTRL_DEBUG
2526 char dbug[4]; /* "DBUG" */
2527 int free; /* 1 if elem is available for reuse. */
2528 int size; /* Number of bytes in body[]. */
2529 #endif
2530 char body[1]; /* First byte of client's space. Actual
2531 * size of this field will be larger than
2532 * one. */
2533 };
2534
2535 struct AllocBlock
2536 {
2537 int count; /* Size of .elem[] */
2538 AllocBlock *next; /* Next block with same-sized elems. */
2539 AllocElem elem[1]; /* Actual size will be larger than one. */
2540 };
2541
2542 /*
2543 * One of the following structures maintains an array of blocks of AllocElems
2544 * of the same size.
2545 */
2546 struct AllocList
2547 {
2548 int size; /* Size of every AllocElem.body[] */
2549 AllocElem *head; /* Top of stack of unused pieces of memory. */
2550 AllocBlock *blocks; /* Linked list of allocated blocks. The blocks
2551 * may contain a different number of elements. */
2552 int blockSize; /* The number of AllocElems per block to allocate.
2553 * Starts at 16 and gets doubled up to 1024. */
2554 AllocList *next; /* Points to an AllocList with a different .size */
2555 };
2556
2557 /*
2558 * A pointer to one of the following structures is stored in each TreeCtrl.
2559 */
2560 struct AllocData
2561 {
2562 AllocList *freeLists; /* Linked list. */
2563 #ifdef ALLOC_STATS
2564 AllocStats *stats; /* For memory-usage reporting. */
2565 #endif
2566 };
2567
2568 #ifdef ALLOC_STATS
2569 struct AllocStats {
2570 Tk_Uid id; /* Name for reporting results. */
2571 unsigned count; /* Number of allocations. */
2572 unsigned size; /* Total allocated bytes. */
2573 AllocStats *next; /* Linked list. */
2574 };
2575 #endif
2576
2577 /*
2578 * The following macro computes the offset of the "body" field within
2579 * AllocElem. It is used to get back to the header pointer from the
2580 * body pointer that's used by clients.
2581 */
2582
2583 #ifdef offsetofXXX
2584 #define BODY_OFFSET ((size_t) offsetof(AllocElem, body))
2585 #else
2586 #define BODY_OFFSET ((size_t) (&((AllocElem *) 0)->body))
2587 #endif
2588
2589 #ifdef ALLOC_STATS
2590
2591 static AllocStats *
AllocStats_Get(ClientData _data,Tk_Uid id)2592 AllocStats_Get(
2593 ClientData _data,
2594 Tk_Uid id
2595 )
2596 {
2597 AllocData *data = (AllocData *) _data;
2598 AllocStats *stats = data->stats;
2599
2600 while (stats != NULL) {
2601 if (stats->id == id)
2602 break;
2603 stats = stats->next;
2604 }
2605 if (stats == NULL) {
2606 stats = (AllocStats *) ckalloc(sizeof(AllocStats));
2607 stats->id = id;
2608 stats->count = 0;
2609 stats->size = 0;
2610 stats->next = data->stats;
2611 data->stats = stats;
2612 }
2613 return stats;
2614 }
2615
2616 void
TreeAlloc_Stats(Tcl_Interp * interp,ClientData _data)2617 TreeAlloc_Stats(
2618 Tcl_Interp *interp,
2619 ClientData _data
2620 )
2621 {
2622 AllocData *data = (AllocData *) _data;
2623 AllocStats *stats = data->stats;
2624 int numElems = 0;
2625 Tcl_DString dString;
2626
2627 Tcl_DStringInit(&dString);
2628 while (stats != NULL) {
2629 DStringAppendf(&dString, "%-20s: %8d : %8d B %5d KB\n",
2630 stats->id, stats->count,
2631 stats->size, (stats->size + 1023) / 1024);
2632 numElems += stats->count;
2633 stats = stats->next;
2634 }
2635 DStringAppendf(&dString, "%-31s: %8d B %5d KB\n", "AllocElem overhead",
2636 numElems * BODY_OFFSET, (numElems * BODY_OFFSET) / 1024);
2637 Tcl_DStringResult(interp, &dString);
2638 }
2639
2640 #endif /* ALLOC_STATS */
2641
2642 /*
2643 *----------------------------------------------------------------------
2644 *
2645 * TreeAlloc_Alloc --
2646 *
2647 * Return storage for a piece of data of the given size.
2648 *
2649 * Results:
2650 * The return value is a pointer to memory for the caller's
2651 * use.
2652 *
2653 * Side effects:
2654 * Memory may be allocated.
2655 *
2656 *----------------------------------------------------------------------
2657 */
2658
2659 char *
TreeAlloc_Alloc(ClientData _data,Tk_Uid id,int size)2660 TreeAlloc_Alloc(
2661 ClientData _data, /* Token returned by TreeAlloc_Init(). */
2662 Tk_Uid id, /* ID for memory-usage reporting. */
2663 int size /* Number of bytes needed. */
2664 )
2665 {
2666 AllocData *data = (AllocData *) _data;
2667 AllocList *freeLists = data->freeLists;
2668 AllocList *freeList = freeLists;
2669 AllocBlock *block;
2670 AllocElem *elem, *result;
2671 #ifdef ALLOC_STATS
2672 AllocStats *stats = AllocStats_Get(_data, id);
2673 #endif
2674 int i;
2675
2676 #ifdef ALLOC_STATS
2677 stats->count++;
2678 stats->size += size;
2679 #endif
2680
2681 while ((freeList != NULL) && (freeList->size != size))
2682 freeList = freeList->next;
2683
2684 if (freeList == NULL) {
2685 freeList = (AllocList *) ckalloc(sizeof(AllocList));
2686 freeList->size = size;
2687 freeList->head = NULL;
2688 freeList->next = freeLists;
2689 freeList->blocks = NULL;
2690 freeList->blockSize = 16;
2691 freeLists = freeList;
2692 ((AllocData *) data)->freeLists = freeLists;
2693 }
2694
2695 if (freeList->head == NULL) {
2696 unsigned elemSize = TCL_ALIGN(BODY_OFFSET + size);
2697
2698 block = (AllocBlock *) ckalloc(Tk_Offset(AllocBlock, elem) +
2699 elemSize * freeList->blockSize);
2700 block->count = freeList->blockSize;
2701 block->next = freeList->blocks;
2702
2703 /* dbwin("TreeAlloc_Alloc alloc %d of size %d\n", freeList->blockSize, size); */
2704 freeList->blocks = block;
2705 if (freeList->blockSize < 1024)
2706 freeList->blockSize *= 2;
2707
2708 freeList->head = block->elem;
2709 elem = freeList->head;
2710 for (i = 1; i < block->count - 1; i++) {
2711 #ifdef TREECTRL_DEBUG
2712 strncpy(elem->dbug, "DBUG", 4);
2713 elem->free = 1;
2714 elem->size = size;
2715 #endif
2716 elem->next = (AllocElem *) (((char *) freeList->head) +
2717 elemSize * i);
2718 elem = elem->next;
2719 }
2720 elem->next = NULL;
2721 #ifdef TREECTRL_DEBUG
2722 strncpy(elem->dbug, "DBUG", 4);
2723 elem->free = 1;
2724 elem->size = size;
2725 #endif
2726 }
2727
2728 result = freeList->head;
2729 freeList->head = result->next;
2730 #ifdef TREECTRL_DEBUG
2731 if (!result->free)
2732 panic("TreeAlloc_Alloc: element not marked free");
2733 result->free = 0;
2734 #endif
2735 return result->body;
2736 }
2737
2738 /*
2739 *----------------------------------------------------------------------
2740 *
2741 * TreeAlloc_Realloc --
2742 *
2743 * Realloc.
2744 *
2745 * Results:
2746 * The return value is a pointer to memory for the caller's
2747 * use.
2748 *
2749 * Side effects:
2750 * Memory may be allocated.
2751 *
2752 *----------------------------------------------------------------------
2753 */
2754
2755 char *
TreeAlloc_Realloc(ClientData data,Tk_Uid id,char * ptr,int size1,int size2)2756 TreeAlloc_Realloc(
2757 ClientData data, /* Token returned by TreeAlloc_Init(). */
2758 Tk_Uid id, /* ID for memory-usage reporting. */
2759 char *ptr,
2760 int size1, /* Number of bytes in ptr. */
2761 int size2 /* Number of bytes needed. */
2762 )
2763 {
2764 char *ptr2;
2765
2766 ptr2 = TreeAlloc_Alloc(data, id, size2);
2767 memcpy(ptr2, ptr, MIN(size1, size2));
2768 TreeAlloc_Free(data, id, ptr, size1);
2769 return ptr2;
2770 }
2771
2772 /*
2773 *----------------------------------------------------------------------
2774 *
2775 * TreeAlloc_Free --
2776 *
2777 * Mark a piece of memory as free for reuse.
2778 *
2779 * Results:
2780 * The piece of memory is added to a list of free pieces of the
2781 * same size.
2782 *
2783 * Side effects:
2784 * None.
2785 *
2786 *----------------------------------------------------------------------
2787 */
2788
2789 void
TreeAlloc_Free(ClientData _data,Tk_Uid id,char * ptr,int size)2790 TreeAlloc_Free(
2791 ClientData _data, /* Token returned by TreeAlloc_Init(). */
2792 Tk_Uid id, /* ID for memory-usage reporting. */
2793 char *ptr, /* Memory to mark for reuse. Must have
2794 * been allocated by TreeAlloc_Alloc(). */
2795 int size /* Number of bytes. Must match the size
2796 * passed to TreeAlloc_CAlloc(). */
2797 )
2798 {
2799 AllocData *data = (AllocData *) _data;
2800 AllocList *freeLists = data->freeLists;
2801 AllocList *freeList = freeLists;
2802 AllocElem *elem;
2803 #ifdef ALLOC_STATS
2804 AllocStats *stats = AllocStats_Get(_data, id);
2805 #endif
2806
2807 #ifdef ALLOC_STATS
2808 stats->count--;
2809 stats->size -= size;
2810 #endif
2811
2812 /* Comment from Tcl_DbCkfree: */
2813 /*
2814 * The following cast is *very* tricky. Must convert the pointer
2815 * to an integer before doing arithmetic on it, because otherwise
2816 * the arithmetic will be done differently (and incorrectly) on
2817 * word-addressed machines such as Crays (will subtract only bytes,
2818 * even though BODY_OFFSET is in words on these machines).
2819 */
2820 /* Note: The Tcl source used to do "(unsigned long) ptr" but that
2821 * results in pointer truncation on 64-bit Windows builds. */
2822 elem = (AllocElem *) (((size_t) ptr) - BODY_OFFSET);
2823
2824 #ifdef TREECTRL_DEBUG
2825 if (strncmp(elem->dbug, "DBUG", 4) != 0)
2826 panic("TreeAlloc_Free: element header != DBUG");
2827 if (elem->free)
2828 panic("TreeAlloc_Free: element already marked free");
2829 if (elem->size != size)
2830 panic("TreeAlloc_Free: element size %d != size %d", elem->size, size);
2831 #endif
2832 while (freeList != NULL && freeList->size != size)
2833 freeList = freeList->next;
2834 if (freeList == NULL)
2835 panic("TreeAlloc_Free: can't find free list for size %d", size);
2836
2837 WIPE(elem->body, size);
2838 elem->next = freeList->head;
2839 #ifdef TREECTRL_DEBUG
2840 elem->free = 1;
2841 #endif
2842 freeList->head = elem;
2843 }
2844
2845 /*
2846 *----------------------------------------------------------------------
2847 *
2848 * TreeAlloc_CAlloc --
2849 *
2850 * Return storage for an array of pieces of memory.
2851 *
2852 * Results:
2853 * Pointer to the available memory.
2854 *
2855 * Side effects:
2856 * Memory may be allocated.
2857 *
2858 *----------------------------------------------------------------------
2859 */
2860
2861 char *
TreeAlloc_CAlloc(ClientData data,Tk_Uid id,int size,int count,int roundUp)2862 TreeAlloc_CAlloc(
2863 ClientData data, /* Token returned by TreeAlloc_Init(). */
2864 Tk_Uid id, /* ID for memory-usage reporting. */
2865 int size, /* Number of bytes needed for each piece
2866 * of memory. */
2867 int count, /* Number of pieces of memory needed. */
2868 int roundUp /* Positive number used to reduce the number
2869 * of lists of memory pieces of different
2870 * size. */
2871 )
2872 {
2873 int n = (count / roundUp) * roundUp + ((count % roundUp) ? roundUp : 0);
2874 #ifdef ALLOC_STATS
2875 AllocStats *stats = AllocStats_Get(data, id);
2876 #endif
2877
2878 #ifdef ALLOC_STATS
2879 stats->count += count - 1;
2880 #endif
2881 return TreeAlloc_Alloc(data, id, size * n);
2882 }
2883
2884 /*
2885 *----------------------------------------------------------------------
2886 *
2887 * TreeAlloc_CFree --
2888 *
2889 * Mark a piece of memory as free for reuse.
2890 *
2891 * Results:
2892 * The piece of memory is added to a list of free pieces of the
2893 * same size.
2894 *
2895 * Side effects:
2896 * None.
2897 *
2898 *----------------------------------------------------------------------
2899 */
2900
2901 void
TreeAlloc_CFree(ClientData data,Tk_Uid id,char * ptr,int size,int count,int roundUp)2902 TreeAlloc_CFree(
2903 ClientData data, /* Token returned by TreeAlloc_Init(). */
2904 Tk_Uid id, /* ID for memory-usage reporting. */
2905 char *ptr, /* Memory to mark for reuse. Must have
2906 * been allocated by TreeAlloc_CAlloc(). */
2907 int size, /* Same arg to TreeAlloc_CAlloc(). */
2908 int count, /* Same arg to TreeAlloc_CAlloc(). */
2909 int roundUp /* Same arg to TreeAlloc_CAlloc(). */
2910 )
2911 {
2912 int n = (count / roundUp) * roundUp + ((count % roundUp) ? roundUp : 0);
2913 #ifdef ALLOC_STATS
2914 AllocStats *stats = AllocStats_Get(data, id);
2915 #endif
2916
2917 TreeAlloc_Free(data, id, ptr, size * n);
2918 #ifdef ALLOC_STATS
2919 stats->count -= count - 1;
2920 #endif
2921 }
2922
2923 /*
2924 *----------------------------------------------------------------------
2925 *
2926 * TreeAlloc_Init --
2927 *
2928 * Allocate and initialize a new memory-manager record.
2929 *
2930 * Results:
2931 * Pointer to memory-manager record.
2932 *
2933 * Side effects:
2934 * Memory is allocated.
2935 *
2936 *----------------------------------------------------------------------
2937 */
2938
2939 ClientData
TreeAlloc_Init(void)2940 TreeAlloc_Init(void)
2941 {
2942 AllocData *data = (AllocData *) ckalloc(sizeof(AllocData));
2943 data->freeLists = NULL;
2944 #ifdef ALLOC_STATS
2945 data->stats = NULL;
2946 #endif
2947 return data;
2948 }
2949
2950 /*
2951 *----------------------------------------------------------------------
2952 *
2953 * TreeAlloc_Finalize --
2954 *
2955 * Free all the memory associated with a memory-manager record.
2956 *
2957 * Results:
2958 * Pointer to memory-manager record.
2959 *
2960 * Side effects:
2961 * Memory is deallocated.
2962 *
2963 *----------------------------------------------------------------------
2964 */
2965
2966 void
TreeAlloc_Finalize(ClientData _data)2967 TreeAlloc_Finalize(
2968 ClientData _data /* Pointer to AllocData created by
2969 * TreeAlloc_Init(). */
2970 )
2971 {
2972 AllocData *data = (AllocData *) _data;
2973 AllocList *freeList = data->freeLists;
2974 #ifdef ALLOC_STATS
2975 AllocStats *stats = data->stats;
2976 #endif
2977
2978 while (freeList != NULL) {
2979 AllocList *nextList = freeList->next;
2980 AllocBlock *block = freeList->blocks;
2981 while (block != NULL) {
2982 AllocBlock *nextBlock = block->next;
2983 ckfree((char *) block);
2984 block = nextBlock;
2985 }
2986 ckfree((char *) freeList);
2987 freeList = nextList;
2988 }
2989
2990 #ifdef ALLOC_STATS
2991 while (stats != NULL) {
2992 AllocStats *next = stats->next;
2993 ckfree((char *) stats);
2994 stats = next;
2995 }
2996 #endif
2997
2998 ckfree((char *) data);
2999 }
3000
3001 #endif /* ALLOC_HAX */
3002
3003 /*
3004 *----------------------------------------------------------------------
3005 *
3006 * TreePtrList_Init --
3007 *
3008 * Initializes an pointer list, discarding any previous contents
3009 * of the pointer list (TreePtrList_Free should have been called already
3010 * if the pointer list was previously in use).
3011 *
3012 * Results:
3013 * None.
3014 *
3015 * Side effects:
3016 * The pointer list is initialized to be empty.
3017 *
3018 *----------------------------------------------------------------------
3019 */
3020
3021 void
TreePtrList_Init(TreeCtrl * tree,TreePtrList * tplPtr,int count)3022 TreePtrList_Init(
3023 TreeCtrl *tree, /* Widget info. */
3024 TreePtrList *tplPtr, /* Structure describing pointer list. */
3025 int count /* Number of pointers the list should hold.
3026 * 0 for default. */
3027 )
3028 {
3029 #ifdef TREECTRL_DEBUG
3030 strncpy(tplPtr->magic, "MAGC", 4);
3031 #endif
3032 tplPtr->tree = tree;
3033 tplPtr->pointers = tplPtr->pointerSpace;
3034 tplPtr->count = 0;
3035 tplPtr->space = TIL_STATIC_SPACE;
3036
3037 if (count + 1 > TIL_STATIC_SPACE) {
3038 tplPtr->space = count + 1;
3039 tplPtr->pointers = (ClientData *) ckalloc(tplPtr->space * sizeof(ClientData));
3040 }
3041
3042 tplPtr->pointers[0] = NULL;
3043 }
3044
3045 /*
3046 *----------------------------------------------------------------------
3047 *
3048 * TreePtrList_Grow --
3049 *
3050 * Increase the available space in an pointer list.
3051 *
3052 * Results:
3053 * The pointers[] array is resized if needed.
3054 *
3055 * Side effects:
3056 * Memory gets reallocated if needed.
3057 *
3058 *----------------------------------------------------------------------
3059 */
3060
3061 void
TreePtrList_Grow(TreePtrList * tplPtr,int count)3062 TreePtrList_Grow(
3063 TreePtrList *tplPtr, /* Structure describing pointer list. */
3064 int count /* Number of pointers the list should hold. */
3065 )
3066 {
3067 #ifdef TREECTRL_DEBUG
3068 if (strncmp(tplPtr->magic, "MAGC", 4) != 0)
3069 panic("TreePtrList_Grow: using uninitialized list");
3070 #endif
3071 if (tplPtr->space >= count + 1)
3072 return;
3073 while (tplPtr->space < count + 1)
3074 tplPtr->space *= 2;
3075 if (tplPtr->pointers == tplPtr->pointerSpace) {
3076 ClientData *pointers;
3077 pointers = (ClientData *) ckalloc(tplPtr->space * sizeof(ClientData));
3078 memcpy(pointers, tplPtr->pointers, (tplPtr->count + 1) * sizeof(ClientData));
3079 tplPtr->pointers = pointers;
3080 } else {
3081 tplPtr->pointers = (ClientData *) ckrealloc((char *) tplPtr->pointers,
3082 tplPtr->space * sizeof(ClientData));
3083 }
3084 }
3085
3086 /*
3087 *----------------------------------------------------------------------
3088 *
3089 * TreePtrList_Append --
3090 *
3091 * Append an pointer to an pointer list.
3092 *
3093 * Results:
3094 * The return value is a pointer to the list of pointers.
3095 *
3096 * Side effects:
3097 * Memory gets reallocated if needed.
3098 *
3099 *----------------------------------------------------------------------
3100 */
3101
3102 ClientData *
TreePtrList_Append(TreePtrList * tplPtr,ClientData pointer)3103 TreePtrList_Append(
3104 TreePtrList *tplPtr, /* Structure describing pointer list. */
3105 ClientData pointer /* Item to append. */
3106 )
3107 {
3108 #ifdef TREECTRL_DEBUG
3109 if (strncmp(tplPtr->magic, "MAGC", 4) != 0)
3110 panic("TreePtrList_Append: using uninitialized list");
3111 #endif
3112 TreePtrList_Grow(tplPtr, tplPtr->count + 1);
3113 tplPtr->pointers[tplPtr->count] = pointer;
3114 tplPtr->count++;
3115 tplPtr->pointers[tplPtr->count] = NULL;
3116 return tplPtr->pointers;
3117 }
3118
3119 /*
3120 *----------------------------------------------------------------------
3121 *
3122 * TreePtrList_Concat --
3123 *
3124 * Join two pointer lists.
3125 *
3126 * Results:
3127 * The return value is a pointer to the list of pointers.
3128 *
3129 * Side effects:
3130 * Memory gets reallocated if needed.
3131 *
3132 *----------------------------------------------------------------------
3133 */
3134
3135 ClientData *
TreePtrList_Concat(TreePtrList * tplPtr,TreePtrList * tpl2Ptr)3136 TreePtrList_Concat(
3137 TreePtrList *tplPtr, /* Structure describing pointer list. */
3138 TreePtrList *tpl2Ptr /* Item list to append. */
3139 )
3140 {
3141 #ifdef TREECTRL_DEBUG
3142 if (strncmp(tplPtr->magic, "MAGC", 4) != 0)
3143 panic("TreePtrList_Concat: using uninitialized list");
3144 #endif
3145 TreePtrList_Grow(tplPtr, tplPtr->count + tpl2Ptr->count);
3146 memcpy(tplPtr->pointers + tplPtr->count, tpl2Ptr->pointers,
3147 tpl2Ptr->count * sizeof(ClientData));
3148 tplPtr->count += tpl2Ptr->count;
3149 tplPtr->pointers[tplPtr->count] = NULL;
3150 return tplPtr->pointers;
3151 }
3152
3153 /*
3154 *----------------------------------------------------------------------
3155 *
3156 * TreePtrList_Free --
3157 *
3158 * Frees up any memory allocated for the pointer list and
3159 * reinitializes the pointer list to an empty state.
3160 *
3161 * Results:
3162 * None.
3163 *
3164 * Side effects:
3165 * The previous contents of the pointer list are lost.
3166 *
3167 *---------------------------------------------------------------------- */
3168
3169 void
TreePtrList_Free(TreePtrList * tplPtr)3170 TreePtrList_Free(
3171 TreePtrList *tplPtr /* Structure describing pointer list. */
3172 )
3173 {
3174 #ifdef TREECTRL_DEBUG
3175 if (strncmp(tplPtr->magic, "MAGC", 4) != 0)
3176 panic("TreePtrList_Free: using uninitialized list");
3177 #endif
3178 if (tplPtr->pointers != tplPtr->pointerSpace) {
3179 ckfree((char *) tplPtr->pointers);
3180 }
3181 tplPtr->pointers = tplPtr->pointerSpace;
3182 tplPtr->count = 0;
3183 tplPtr->space = TIL_STATIC_SPACE;
3184 tplPtr->pointers[0] = NULL;
3185 }
3186
3187 #define TAG_INFO_SIZE(tagSpace) \
3188 (Tk_Offset(TagInfo, tagPtr) + ((tagSpace) * sizeof(Tk_Uid)))
3189
3190 static CONST char *TagInfoUid = "TagInfo";
3191
3192 /*
3193 *----------------------------------------------------------------------
3194 *
3195 * TagInfo_Add --
3196 *
3197 * Adds tags to a list of tags.
3198 *
3199 * Results:
3200 * Non-duplicate tags are added.
3201 *
3202 * Side effects:
3203 * Memory may be (re)allocated.
3204 *
3205 *----------------------------------------------------------------------
3206 */
3207
3208 TagInfo *
TagInfo_Add(TreeCtrl * tree,TagInfo * tagInfo,Tk_Uid tags[],int numTags)3209 TagInfo_Add(
3210 TreeCtrl *tree, /* Widget info. */
3211 TagInfo *tagInfo, /* Tag list. May be NULL. */
3212 Tk_Uid tags[],
3213 int numTags
3214 )
3215 {
3216 int i, j;
3217
3218 if (tagInfo == NULL) {
3219 if (numTags <= TREE_TAG_SPACE) {
3220 #ifdef ALLOC_HAX
3221 tagInfo = (TagInfo *) TreeAlloc_Alloc(tree->allocData, TagInfoUid,
3222 sizeof(TagInfo));
3223 #else
3224 tagInfo = (TagInfo *) ckalloc(sizeof(TagInfo));
3225 #endif
3226 tagInfo->tagSpace = TREE_TAG_SPACE;
3227 } else {
3228 int tagSpace = (numTags / TREE_TAG_SPACE) * TREE_TAG_SPACE +
3229 ((numTags % TREE_TAG_SPACE) ? TREE_TAG_SPACE : 0);
3230 if (tagSpace % TREE_TAG_SPACE) panic("TagInfo_Add miscalc");
3231 #ifdef ALLOC_HAX
3232 tagInfo = (TagInfo *) TreeAlloc_Alloc(tree->allocData, TagInfoUid,
3233 TAG_INFO_SIZE(tagSpace));
3234 #else
3235 tagInfo = (TagInfo *) ckalloc(TAG_INFO_SIZE(tagSpace));
3236 #endif
3237 tagInfo->tagSpace = tagSpace;
3238 }
3239 tagInfo->numTags = 0;
3240 }
3241 for (i = 0; i < numTags; i++) {
3242 for (j = 0; j < tagInfo->numTags; j++) {
3243 if (tagInfo->tagPtr[j] == tags[i])
3244 break;
3245 }
3246 if (j >= tagInfo->numTags) {
3247 /* Resize existing storage if needed. */
3248 if (tagInfo->tagSpace == tagInfo->numTags) {
3249 tagInfo->tagSpace += TREE_TAG_SPACE;
3250 #ifdef ALLOC_HAX
3251 tagInfo = (TagInfo *) TreeAlloc_Realloc(tree->allocData,
3252 TagInfoUid, (char *) tagInfo,
3253 TAG_INFO_SIZE(tagInfo->tagSpace - TREE_TAG_SPACE),
3254 TAG_INFO_SIZE(tagInfo->tagSpace));
3255 #else
3256 tagInfo = (TagInfo *) ckrealloc((char *) tagInfo,
3257 TAG_INFO_SIZE(tagInfo->tagSpace));
3258 #endif
3259 }
3260 tagInfo->tagPtr[tagInfo->numTags++] = tags[i];
3261 }
3262 }
3263 return tagInfo;
3264 }
3265
3266 /*
3267 *----------------------------------------------------------------------
3268 *
3269 * TagInfo_Remove --
3270 *
3271 * Removes tags from a list of tags.
3272 *
3273 * Results:
3274 * Existing tags are removed.
3275 *
3276 * Side effects:
3277 * Memory may be reallocated.
3278 *
3279 *----------------------------------------------------------------------
3280 */
3281
3282 TagInfo *
TagInfo_Remove(TreeCtrl * tree,TagInfo * tagInfo,Tk_Uid tags[],int numTags)3283 TagInfo_Remove(
3284 TreeCtrl *tree, /* Widget info. */
3285 TagInfo *tagInfo, /* Tag list. May be NULL. */
3286 Tk_Uid tags[],
3287 int numTags
3288 )
3289 {
3290 int i, j;
3291
3292 if (tagInfo == NULL)
3293 return tagInfo;
3294 for (i = 0; i < numTags; i++) {
3295 for (j = 0; j < tagInfo->numTags; j++) {
3296 if (tagInfo->tagPtr[j] == tags[i]) {
3297 tagInfo->tagPtr[j] =
3298 tagInfo->tagPtr[tagInfo->numTags - 1];
3299 tagInfo->numTags--;
3300 break;
3301 }
3302 }
3303 }
3304 if (tagInfo->numTags == 0) {
3305 TagInfo_Free(tree, tagInfo);
3306 tagInfo = NULL;
3307 }
3308 return tagInfo;
3309 }
3310
3311 /*
3312 *----------------------------------------------------------------------
3313 *
3314 * TagInfo_Names --
3315 *
3316 * Build a list of unique tag names.
3317 *
3318 * Results:
3319 * Unique tags are added to a dynamically-allocated list.
3320 *
3321 * Side effects:
3322 * Memory may be (re)allocated.
3323 *
3324 *----------------------------------------------------------------------
3325 */
3326
3327 Tk_Uid *
TagInfo_Names(TreeCtrl * tree,TagInfo * tagInfo,Tk_Uid * tags,int * numTagsPtr,int * tagSpacePtr)3328 TagInfo_Names(
3329 TreeCtrl *tree, /* Widget info. */
3330 TagInfo *tagInfo, /* Tag list. May be NULL. */
3331 Tk_Uid *tags, /* Current list, may be NULL. */
3332 int *numTagsPtr, /* Number of tags in tags[]. */
3333 int *tagSpacePtr /* Size of tags[]. */
3334 )
3335 {
3336 int numTags = *numTagsPtr, tagSpace = *tagSpacePtr;
3337 int i, j;
3338
3339 if (tagInfo == NULL)
3340 return tags;
3341 for (i = 0; i < tagInfo->numTags; i++) {
3342 Tk_Uid tag = tagInfo->tagPtr[i];
3343 for (j = 0; j < numTags; j++) {
3344 if (tag == tags[j])
3345 break;
3346 }
3347 if (j < numTags)
3348 continue;
3349 if ((tags == NULL) || (numTags == tagSpace)) {
3350 if (tags == NULL) {
3351 tagSpace = 32;
3352 tags = (Tk_Uid *) ckalloc(sizeof(Tk_Uid) * tagSpace);
3353 }
3354 else {
3355 tagSpace *= 2;
3356 tags = (Tk_Uid *) ckrealloc((char *) tags,
3357 sizeof(Tk_Uid) * tagSpace);
3358 }
3359 }
3360 tags[numTags++] = tag;
3361 }
3362 *numTagsPtr = numTags;
3363 *tagSpacePtr = tagSpace;
3364 return tags;
3365 }
3366
3367 /*
3368 *----------------------------------------------------------------------
3369 *
3370 * TagInfo_Copy --
3371 *
3372 * Copy a list of tags.
3373 *
3374 * Results:
3375 * Allocates a new TagInfo if given one is not NULL.
3376 *
3377 * Side effects:
3378 * Memory may be allocated.
3379 *
3380 *----------------------------------------------------------------------
3381 */
3382
3383 TagInfo *
TagInfo_Copy(TreeCtrl * tree,TagInfo * tagInfo)3384 TagInfo_Copy(
3385 TreeCtrl *tree, /* Widget info. */
3386 TagInfo *tagInfo /* Tag list. May be NULL. */
3387 )
3388 {
3389 TagInfo *copy = NULL;
3390
3391 if (tagInfo != NULL) {
3392 int tagSpace = tagInfo->tagSpace;
3393 #ifdef ALLOC_HAX
3394 copy = (TagInfo *) TreeAlloc_Alloc(tree->allocData, TagInfoUid,
3395 TAG_INFO_SIZE(tagSpace));
3396 #else
3397 copy = (TagInfo *) ckalloc(TAG_INFO_SIZE(tagSpace));
3398 #endif
3399 memcpy((void *) copy->tagPtr, tagInfo->tagPtr, tagInfo->numTags * sizeof(Tk_Uid));
3400 copy->numTags = tagInfo->numTags;
3401 copy->tagSpace = tagSpace;
3402 }
3403 return copy;
3404 }
3405
3406 /*
3407 *----------------------------------------------------------------------
3408 *
3409 * TagInfo_Free --
3410 *
3411 * Free a list of tags.
3412 *
3413 * Results:
3414 * TagInfo struct is freed if non-NULL.
3415 *
3416 * Side effects:
3417 * Memory may be freed.
3418 *
3419 *----------------------------------------------------------------------
3420 */
3421
3422 void
TagInfo_Free(TreeCtrl * tree,TagInfo * tagInfo)3423 TagInfo_Free(
3424 TreeCtrl *tree, /* Widget info. */
3425 TagInfo *tagInfo /* Tag list. May be NULL. */
3426 )
3427 {
3428 if (tagInfo != NULL)
3429 #ifdef ALLOC_HAX
3430 TreeAlloc_Free(tree->allocData, TagInfoUid, (char *) tagInfo,
3431 TAG_INFO_SIZE(tagInfo->tagSpace));
3432 #else
3433 ckfree((char *) tagInfo);
3434 #endif
3435 }
3436
3437 int
TagInfo_FromObj(TreeCtrl * tree,Tcl_Obj * objPtr,TagInfo ** tagInfoPtr)3438 TagInfo_FromObj(
3439 TreeCtrl *tree, /* Widget info. */
3440 Tcl_Obj *objPtr,
3441 TagInfo **tagInfoPtr
3442 )
3443 {
3444 int i, numTags;
3445 Tcl_Obj **listObjv;
3446 TagInfo *tagInfo = NULL;
3447
3448 if (Tcl_ListObjGetElements(tree->interp, objPtr, &numTags, &listObjv) != TCL_OK) {
3449 return TCL_ERROR;
3450 }
3451 if (numTags == 0) {
3452 *tagInfoPtr = NULL;
3453 return TCL_OK;
3454 }
3455 for (i = 0; i < numTags; i++) {
3456 Tk_Uid tag = Tk_GetUid(Tcl_GetString(listObjv[i]));
3457 tagInfo = TagInfo_Add(tree, tagInfo, &tag, 1);
3458 }
3459 *tagInfoPtr = tagInfo;
3460 return TCL_OK;
3461 }
3462
3463 static Tcl_Obj *
TagInfo_ToObj(TreeCtrl * tree,TagInfo * tagInfo)3464 TagInfo_ToObj(
3465 TreeCtrl *tree, /* Widget info. */
3466 TagInfo *tagInfo
3467 )
3468 {
3469 Tcl_Obj *listObj;
3470 int i;
3471
3472 if (tagInfo == NULL)
3473 return NULL;
3474
3475 listObj = Tcl_NewListObj(0, NULL);
3476 for (i = 0; i < tagInfo->numTags; i++) {
3477 Tcl_ListObjAppendElement(NULL, listObj,
3478 Tcl_NewStringObj((char *) tagInfo->tagPtr[i], -1));
3479 }
3480 return listObj;
3481 }
3482
3483 /*
3484 *----------------------------------------------------------------------
3485 *
3486 * TagInfoCO_Set --
3487 * TagInfoCO_Get --
3488 * TagInfoCO_Restore --
3489 * TagInfoCO_Free --
3490 *
3491 * These procedures implement a TK_OPTION_CUSTOM where the custom
3492 * option is a TagInfo record.
3493 *
3494 * Results:
3495 * None.
3496 *
3497 * Side effects:
3498 * None.
3499 *
3500 *----------------------------------------------------------------------
3501 */
3502
3503 static int
TagInfoCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)3504 TagInfoCO_Set(
3505 ClientData clientData,
3506 Tcl_Interp *interp,
3507 Tk_Window tkwin,
3508 Tcl_Obj **value,
3509 char *recordPtr,
3510 int internalOffset,
3511 char *saveInternalPtr,
3512 int flags
3513 )
3514 {
3515 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
3516 int objEmpty;
3517 TagInfo *new, **internalPtr;
3518
3519 if (internalOffset >= 0)
3520 internalPtr = (TagInfo **) (recordPtr + internalOffset);
3521 else
3522 internalPtr = NULL;
3523
3524 objEmpty = ObjectIsEmpty((*value));
3525
3526 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
3527 (*value) = NULL;
3528 else {
3529 if (TagInfo_FromObj(tree, (*value), &new) != TCL_OK)
3530 return TCL_ERROR;
3531 }
3532 if (internalPtr != NULL) {
3533 if ((*value) == NULL)
3534 new = NULL;
3535 *((TagInfo **) saveInternalPtr) = *internalPtr;
3536 *internalPtr = new;
3537 }
3538
3539 return TCL_OK;
3540 }
3541
3542 static Tcl_Obj *
TagInfoCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)3543 TagInfoCO_Get(
3544 ClientData clientData,
3545 Tk_Window tkwin,
3546 char *recordPtr,
3547 int internalOffset
3548 )
3549 {
3550 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
3551 TagInfo *value = *(TagInfo **) (recordPtr + internalOffset);
3552 return TagInfo_ToObj(tree, value);
3553 }
3554
3555 static void
TagInfoCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)3556 TagInfoCO_Restore(
3557 ClientData clientData,
3558 Tk_Window tkwin,
3559 char *internalPtr,
3560 char *saveInternalPtr
3561 )
3562 {
3563 *(TagInfo **) internalPtr = *(TagInfo **) saveInternalPtr;
3564 }
3565
3566 static void
TagInfoCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)3567 TagInfoCO_Free(
3568 ClientData clientData, /* unused. */
3569 Tk_Window tkwin, /* A window; unused */
3570 char *internalPtr /* Pointer to the place, where the internal
3571 * form resides. */
3572 )
3573 {
3574 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
3575
3576 TagInfo_Free(tree, *(TagInfo **)internalPtr);
3577 }
3578
3579 Tk_ObjCustomOption TreeCtrlCO_tagInfo =
3580 {
3581 "tag list",
3582 TagInfoCO_Set,
3583 TagInfoCO_Get,
3584 TagInfoCO_Restore,
3585 TagInfoCO_Free,
3586 (ClientData) NULL
3587 };
3588
3589 /*
3590 *----------------------------------------------------------------------
3591 *
3592 * TagExpr_Init --
3593 *
3594 * This procedure initializes a TagExpr struct by parsing a Tcl_Obj
3595 * string representation of a tag expression.
3596 *
3597 * Results:
3598 * A standard Tcl result.
3599 *
3600 * Side effects:
3601 * Memory may be allocated.
3602 *
3603 *----------------------------------------------------------------------
3604 */
3605
3606 int
TagExpr_Init(TreeCtrl * tree,Tcl_Obj * exprObj,TagExpr * expr)3607 TagExpr_Init(
3608 TreeCtrl *tree, /* Widget info. */
3609 Tcl_Obj *exprObj, /* Tag expression string. */
3610 TagExpr *expr /* Struct to initialize. */
3611 )
3612 {
3613 int i;
3614 char *tag;
3615
3616 expr->tree = tree;
3617 expr->index = 0;
3618 expr->length = 0;
3619 expr->uid = NULL;
3620 expr->allocated = sizeof(expr->staticUids) / sizeof(Tk_Uid);
3621 expr->uids = expr->staticUids;
3622 expr->simple = TRUE;
3623 expr->rewritebuffer = expr->staticRWB;
3624
3625 tag = Tcl_GetStringFromObj(exprObj, &expr->stringLength);
3626
3627 /* short circuit impossible searches for null tags */
3628 if (expr->stringLength == 0) {
3629 return TCL_OK;
3630 }
3631
3632 /*
3633 * Pre-scan tag for at least one unquoted "&&" "||" "^" "!"
3634 * if not found then use string as simple tag
3635 */
3636 for (i = 0; i < expr->stringLength ; i++) {
3637 if (tag[i] == '"') {
3638 i++;
3639 for ( ; i < expr->stringLength; i++) {
3640 if (tag[i] == '\\') {
3641 i++;
3642 continue;
3643 }
3644 if (tag[i] == '"') {
3645 break;
3646 }
3647 }
3648 } else {
3649 if ((tag[i] == '&' && tag[i+1] == '&')
3650 || (tag[i] == '|' && tag[i+1] == '|')
3651 || (tag[i] == '^')
3652 || (tag[i] == '!')) {
3653 expr->simple = FALSE;
3654 break;
3655 }
3656 }
3657 }
3658
3659 if (expr->simple) {
3660 expr->uid = Tk_GetUid(tag);
3661 return TCL_OK;
3662 }
3663
3664 expr->string = tag;
3665 expr->stringIndex = 0;
3666
3667 /* Allocate buffer for rewritten tags (after de-escaping) */
3668 if (expr->stringLength >= sizeof(expr->staticRWB))
3669 expr->rewritebuffer = ckalloc(expr->stringLength + 1);
3670
3671 if (TagExpr_Scan(expr) != TCL_OK) {
3672 TagExpr_Free(expr);
3673 return TCL_ERROR;
3674 }
3675 expr->length = expr->index;
3676
3677 return TCL_OK;
3678 }
3679
3680 /*
3681 * Uids for operands in compiled tag expressions.
3682 * Initialization is done by GetStaticUids().
3683 */
3684 typedef struct {
3685 Tk_Uid andUid;
3686 Tk_Uid orUid;
3687 Tk_Uid xorUid;
3688 Tk_Uid parenUid;
3689 Tk_Uid negparenUid;
3690 Tk_Uid endparenUid;
3691 Tk_Uid tagvalUid;
3692 Tk_Uid negtagvalUid;
3693 } SearchUids;
3694
3695 static Tcl_ThreadDataKey searchUidTDK;
3696
3697 /*
3698 *----------------------------------------------------------------------
3699 *
3700 * GetStaticUids --
3701 *
3702 * This procedure is invoked to return a structure filled with
3703 * the Uids used when doing tag searching. If it was never before
3704 * called in the current thread, it initializes the structure for
3705 * that thread (uids are only ever local to one thread [Bug
3706 * 1114977]).
3707 *
3708 * Results:
3709 * None.
3710 *
3711 * Side effects:
3712 * None.
3713 *
3714 *----------------------------------------------------------------------
3715 */
3716
3717 static SearchUids *
GetStaticUids()3718 GetStaticUids()
3719 {
3720 SearchUids *searchUids = (SearchUids *)
3721 Tcl_GetThreadData(&searchUidTDK, sizeof(SearchUids));
3722
3723 if (searchUids->andUid == NULL) {
3724 searchUids->andUid = Tk_GetUid("&&");
3725 searchUids->orUid = Tk_GetUid("||");
3726 searchUids->xorUid = Tk_GetUid("^");
3727 searchUids->parenUid = Tk_GetUid("(");
3728 searchUids->endparenUid = Tk_GetUid(")");
3729 searchUids->negparenUid = Tk_GetUid("!(");
3730 searchUids->tagvalUid = Tk_GetUid("!!");
3731 searchUids->negtagvalUid = Tk_GetUid("!");
3732 }
3733 return searchUids;
3734 }
3735
3736 /*
3737 *----------------------------------------------------------------------
3738 *
3739 * TagExpr_Scan --
3740 *
3741 * This procedure recursively parses a string representation of a
3742 * tag expression into an array of Tk_Uids.
3743 *
3744 * Results:
3745 * The return value indicates if the tag expression
3746 * was successfully scanned (syntax).
3747 *
3748 * Side effects:
3749 * Memory may be allocated.
3750 *
3751 *----------------------------------------------------------------------
3752 */
3753
3754 int
TagExpr_Scan(TagExpr * expr)3755 TagExpr_Scan(
3756 TagExpr *expr /* Info about a tag expression. */
3757 )
3758 {
3759 Tcl_Interp *interp = expr->tree->interp;
3760 int looking_for_tag; /* When true, scanner expects
3761 * next char(s) to be a tag,
3762 * else operand expected */
3763 int found_tag; /* One or more tags found */
3764 int found_endquote; /* For quoted tag string parsing */
3765 int negate_result; /* Pending negation of next tag value */
3766 char *tag; /* tag from tag expression string */
3767 SearchUids *searchUids; /* Collection of uids for basic search
3768 * expression terms. */
3769 char c;
3770
3771 searchUids = GetStaticUids();
3772 negate_result = 0;
3773 found_tag = 0;
3774 looking_for_tag = 1;
3775 while (expr->stringIndex < expr->stringLength) {
3776 c = expr->string[expr->stringIndex++];
3777
3778 if (expr->allocated == expr->index) {
3779 expr->allocated += 15;
3780 if (expr->uids != expr->staticUids) {
3781 expr->uids =
3782 (Tk_Uid *) ckrealloc((char *)(expr->uids),
3783 (expr->allocated)*sizeof(Tk_Uid));
3784 } else {
3785 expr->uids =
3786 (Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid));
3787 memcpy((void *) expr->uids, expr->staticUids, sizeof(expr->staticUids));
3788 }
3789 }
3790
3791 if (looking_for_tag) {
3792
3793 switch (c) {
3794 case ' ' : /* ignore unquoted whitespace */
3795 case '\t' :
3796 case '\n' :
3797 case '\r' :
3798 break;
3799
3800 case '!' : /* negate next tag or subexpr */
3801 if (looking_for_tag > 1) {
3802 Tcl_AppendResult(interp,
3803 "Too many '!' in tag search expression",
3804 (char *) NULL);
3805 return TCL_ERROR;
3806 }
3807 looking_for_tag++;
3808 negate_result = 1;
3809 break;
3810
3811 case '(' : /* scan (negated) subexpr recursively */
3812 if (negate_result) {
3813 expr->uids[expr->index++] = searchUids->negparenUid;
3814 negate_result = 0;
3815 } else {
3816 expr->uids[expr->index++] = searchUids->parenUid;
3817 }
3818 if (TagExpr_Scan(expr) != TCL_OK) {
3819 /* Result string should be already set
3820 * by nested call to tag_expr_scan() */
3821 return TCL_ERROR;
3822 }
3823 looking_for_tag = 0;
3824 found_tag = 1;
3825 break;
3826
3827 case '"' : /* quoted tag string */
3828 if (negate_result) {
3829 expr->uids[expr->index++] = searchUids->negtagvalUid;
3830 negate_result = 0;
3831 } else {
3832 expr->uids[expr->index++] = searchUids->tagvalUid;
3833 }
3834 tag = expr->rewritebuffer;
3835 found_endquote = 0;
3836 while (expr->stringIndex < expr->stringLength) {
3837 c = expr->string[expr->stringIndex++];
3838 if (c == '\\') {
3839 c = expr->string[expr->stringIndex++];
3840 }
3841 if (c == '"') {
3842 found_endquote = 1;
3843 break;
3844 }
3845 *tag++ = c;
3846 }
3847 if (! found_endquote) {
3848 Tcl_AppendResult(interp,
3849 "Missing endquote in tag search expression",
3850 (char *) NULL);
3851 return TCL_ERROR;
3852 }
3853 if (! (tag - expr->rewritebuffer)) {
3854 Tcl_AppendResult(interp,
3855 "Null quoted tag string in tag search expression",
3856 (char *) NULL);
3857 return TCL_ERROR;
3858 }
3859 *tag++ = '\0';
3860 expr->uids[expr->index++] =
3861 Tk_GetUid(expr->rewritebuffer);
3862 looking_for_tag = 0;
3863 found_tag = 1;
3864 break;
3865
3866 case '&' : /* illegal chars when looking for tag */
3867 case '|' :
3868 case '^' :
3869 case ')' :
3870 Tcl_AppendResult(interp,
3871 "Unexpected operator in tag search expression",
3872 (char *) NULL);
3873 return TCL_ERROR;
3874
3875 default : /* unquoted tag string */
3876 if (negate_result) {
3877 expr->uids[expr->index++] = searchUids->negtagvalUid;
3878 negate_result = 0;
3879 } else {
3880 expr->uids[expr->index++] = searchUids->tagvalUid;
3881 }
3882 tag = expr->rewritebuffer;
3883 *tag++ = c;
3884 /* copy rest of tag, including any embedded whitespace */
3885 while (expr->stringIndex < expr->stringLength) {
3886 c = expr->string[expr->stringIndex];
3887 if (c == '!' || c == '&' || c == '|' || c == '^'
3888 || c == '(' || c == ')' || c == '"') {
3889 break;
3890 }
3891 *tag++ = c;
3892 expr->stringIndex++;
3893 }
3894 /* remove trailing whitespace */
3895 while (1) {
3896 c = *--tag;
3897 /* there must have been one non-whitespace char,
3898 * so this will terminate */
3899 if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
3900 break;
3901 }
3902 }
3903 *++tag = '\0';
3904 expr->uids[expr->index++] =
3905 Tk_GetUid(expr->rewritebuffer);
3906 looking_for_tag = 0;
3907 found_tag = 1;
3908 }
3909
3910 } else { /* ! looking_for_tag */
3911
3912 switch (c) {
3913 case ' ' : /* ignore whitespace */
3914 case '\t' :
3915 case '\n' :
3916 case '\r' :
3917 break;
3918
3919 case '&' : /* AND operator */
3920 c = expr->string[expr->stringIndex++];
3921 if (c != '&') {
3922 Tcl_AppendResult(interp,
3923 "Singleton '&' in tag search expression",
3924 (char *) NULL);
3925 return TCL_ERROR;
3926 }
3927 expr->uids[expr->index++] = searchUids->andUid;
3928 looking_for_tag = 1;
3929 break;
3930
3931 case '|' : /* OR operator */
3932 c = expr->string[expr->stringIndex++];
3933 if (c != '|') {
3934 Tcl_AppendResult(interp,
3935 "Singleton '|' in tag search expression",
3936 (char *) NULL);
3937 return TCL_ERROR;
3938 }
3939 expr->uids[expr->index++] = searchUids->orUid;
3940 looking_for_tag = 1;
3941 break;
3942
3943 case '^' : /* XOR operator */
3944 expr->uids[expr->index++] = searchUids->xorUid;
3945 looking_for_tag = 1;
3946 break;
3947
3948 case ')' : /* end subexpression */
3949 expr->uids[expr->index++] = searchUids->endparenUid;
3950 goto breakwhile;
3951
3952 default : /* syntax error */
3953 Tcl_AppendResult(interp,
3954 "Invalid boolean operator in tag search expression",
3955 (char *) NULL);
3956 return TCL_ERROR;
3957 }
3958 }
3959 }
3960 breakwhile:
3961 if (found_tag && ! looking_for_tag) {
3962 return TCL_OK;
3963 }
3964 Tcl_AppendResult(interp, "Missing tag in tag search expression",
3965 (char *) NULL);
3966 return TCL_ERROR;
3967 }
3968
3969 /*
3970 *----------------------------------------------------------------------
3971 *
3972 * TagExpr_Eval --
3973 *
3974 * This procedure recursively evaluates a compiled tag expression.
3975 *
3976 * Results:
3977 * The return value indicates if the tag expression
3978 * successfully matched the tags of the given item.
3979 *
3980 * Side effects:
3981 * None.
3982 *
3983 *----------------------------------------------------------------------
3984 */
3985
3986 static int
_TagExpr_Eval(TagExpr * expr,TagInfo * tagInfo)3987 _TagExpr_Eval(
3988 TagExpr *expr, /* Info about a tag expression. */
3989 TagInfo *tagInfo /* Tags to test. */
3990 )
3991 {
3992 int looking_for_tag; /* When true, scanner expects
3993 * next char(s) to be a tag,
3994 * else operand expected */
3995 int negate_result; /* Pending negation of next tag value */
3996 Tk_Uid uid;
3997 Tk_Uid *tagPtr;
3998 int count;
3999 int result; /* Value of expr so far */
4000 int parendepth;
4001 SearchUids *searchUids; /* Collection of uids for basic search
4002 * expression terms. */
4003 TagInfo dummy;
4004
4005 if (expr->stringLength == 0) /* empty expression (an error?) */
4006 return 0;
4007
4008 /* No tags given. */
4009 if (tagInfo == NULL) {
4010 dummy.numTags = 0;
4011 tagInfo = &dummy;
4012 }
4013
4014 /* A single tag. */
4015 if (expr->simple) {
4016 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
4017 count > 0; tagPtr++, count--) {
4018 if (*tagPtr == expr->uid) {
4019 return 1;
4020 }
4021 }
4022 return 0;
4023 }
4024
4025 searchUids = GetStaticUids();
4026 result = 0; /* just to keep the compiler quiet */
4027
4028 negate_result = 0;
4029 looking_for_tag = 1;
4030 while (expr->index < expr->length) {
4031 uid = expr->uids[expr->index++];
4032 if (looking_for_tag) {
4033 if (uid == searchUids->tagvalUid) {
4034 /*
4035 * assert(expr->index < expr->length);
4036 */
4037 uid = expr->uids[expr->index++];
4038 result = 0;
4039 /*
4040 * set result 1 if tag is found in item's tags
4041 */
4042 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
4043 count > 0; tagPtr++, count--) {
4044 if (*tagPtr == uid) {
4045 result = 1;
4046 break;
4047 }
4048 }
4049
4050 } else if (uid == searchUids->negtagvalUid) {
4051 negate_result = ! negate_result;
4052 /*
4053 * assert(expr->index < expr->length);
4054 */
4055 uid = expr->uids[expr->index++];
4056 result = 0;
4057 /*
4058 * set result 1 if tag is found in item's tags
4059 */
4060 for (tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
4061 count > 0; tagPtr++, count--) {
4062 if (*tagPtr == uid) {
4063 result = 1;
4064 break;
4065 }
4066 }
4067
4068 } else if (uid == searchUids->parenUid) {
4069 /*
4070 * evaluate subexpressions with recursion
4071 */
4072 result = _TagExpr_Eval(expr, tagInfo);
4073
4074 } else if (uid == searchUids->negparenUid) {
4075 negate_result = ! negate_result;
4076 /*
4077 * evaluate subexpressions with recursion
4078 */
4079 result = _TagExpr_Eval(expr, tagInfo);
4080 /*
4081 * } else {
4082 * assert(0);
4083 */
4084 }
4085 if (negate_result) {
4086 result = ! result;
4087 negate_result = 0;
4088 }
4089 looking_for_tag = 0;
4090 } else { /* ! looking_for_tag */
4091 if (((uid == searchUids->andUid) && (!result)) ||
4092 ((uid == searchUids->orUid) && result)) {
4093 /*
4094 * short circuit expression evaluation
4095 *
4096 * if result before && is 0, or result before || is 1,
4097 * then the expression is decided and no further
4098 * evaluation is needed.
4099 */
4100
4101 parendepth = 0;
4102 while (expr->index < expr->length) {
4103 uid = expr->uids[expr->index++];
4104 if (uid == searchUids->tagvalUid ||
4105 uid == searchUids->negtagvalUid) {
4106 expr->index++;
4107 continue;
4108 }
4109 if (uid == searchUids->parenUid ||
4110 uid == searchUids->negparenUid) {
4111 parendepth++;
4112 continue;
4113 }
4114 if (uid == searchUids->endparenUid) {
4115 parendepth--;
4116 if (parendepth < 0) {
4117 break;
4118 }
4119 }
4120 }
4121 return result;
4122
4123 } else if (uid == searchUids->xorUid) {
4124 /*
4125 * if the previous result was 1
4126 * then negate the next result
4127 */
4128 negate_result = result;
4129
4130 } else if (uid == searchUids->endparenUid) {
4131 return result;
4132 /*
4133 * } else {
4134 * assert(0);
4135 */
4136 }
4137 looking_for_tag = 1;
4138 }
4139 }
4140 /*
4141 * assert(! looking_for_tag);
4142 */
4143 return result;
4144 }
4145
4146 int
TagExpr_Eval(TagExpr * expr,TagInfo * tagInfo)4147 TagExpr_Eval(
4148 TagExpr *expr, /* Info about a tag expression. */
4149 TagInfo *tagInfo /* Tags to test. */
4150 )
4151 {
4152 expr->index = 0;
4153 return _TagExpr_Eval(expr, tagInfo);
4154 }
4155
4156 /*
4157 *----------------------------------------------------------------------
4158 *
4159 * TagExpr_Free --
4160 *
4161 * This procedure frees the given struct.
4162 *
4163 * Results:
4164 * None.
4165 *
4166 * Side effects:
4167 * None.
4168 *
4169 *----------------------------------------------------------------------
4170 */
4171
4172 void
TagExpr_Free(TagExpr * expr)4173 TagExpr_Free(
4174 TagExpr *expr
4175 )
4176 {
4177 if (expr->rewritebuffer != expr->staticRWB)
4178 ckfree(expr->rewritebuffer);
4179 if (expr->uids != expr->staticUids)
4180 ckfree((char *) expr->uids);
4181 }
4182
4183 /*
4184 *----------------------------------------------------------------------
4185 *
4186 * OptionHax_Remember --
4187 * OptionHax_Forget --
4188 *
4189 * These procedures are used to work around a limitation in
4190 * the Tk_SavedOption structure: the internal form of a configuration
4191 * option cannot be larger than a double.
4192 *
4193 * Results:
4194 * None.
4195 *
4196 * Side effects:
4197 * None.
4198 *
4199 *----------------------------------------------------------------------
4200 */
4201
4202 static void
OptionHax_Remember(TreeCtrl * tree,char * ptr)4203 OptionHax_Remember(
4204 TreeCtrl *tree,
4205 char *ptr
4206 )
4207 {
4208 #ifdef TREECTRL_DEBUG
4209 int i;
4210 for (i = 0; i < tree->optionHaxCnt; i++) {
4211 if (ptr == tree->optionHax[i]) {
4212 panic("OptionHax_Remember: ptr is not new");
4213 }
4214 }
4215 if (tree->optionHaxCnt == sizeof(tree->optionHax) / sizeof(tree->optionHax[0]))
4216 panic("OptionHax_Remember: too many options");
4217 #endif
4218 tree->optionHax[tree->optionHaxCnt++] = ptr;
4219 /*dbwin("OptionHax_Remember %p\n", ptr);*/
4220 }
4221
4222 static int
OptionHax_Forget(TreeCtrl * tree,char * ptr)4223 OptionHax_Forget(
4224 TreeCtrl *tree,
4225 char *ptr
4226 )
4227 {
4228 int i;
4229
4230 for (i = 0; i < tree->optionHaxCnt; i++) {
4231 if (ptr == tree->optionHax[i]) {
4232 tree->optionHax[i] = tree->optionHax[--tree->optionHaxCnt];
4233 /*dbwin("OptionHax_Forget %p\n", ptr);*/
4234 return 1;
4235 }
4236 }
4237 return 0;
4238 }
4239
4240 /*
4241 *----------------------------------------------------------------------
4242 *
4243 * Tree_FindOptionSpec --
4244 *
4245 * Return a pointer to a name Tk_OptionSpec in a table.
4246 *
4247 * Results:
4248 * Returns a pointer or panics.
4249 *
4250 * Side effects:
4251 * None.
4252 *
4253 *----------------------------------------------------------------------
4254 */
4255
4256 Tk_OptionSpec *
Tree_FindOptionSpec(Tk_OptionSpec * optionTable,CONST char * optionName)4257 Tree_FindOptionSpec(
4258 Tk_OptionSpec *optionTable,
4259 CONST char *optionName
4260 )
4261 {
4262 while (optionTable->type != TK_OPTION_END) {
4263 if (strcmp(optionTable->optionName, optionName) == 0)
4264 return optionTable;
4265 optionTable++;
4266 }
4267 panic("Tree_FindOptionSpec: can't find %s", optionName);
4268 return NULL;
4269 }
4270
4271 /*
4272 *----------------------------------------------------------------------
4273 *
4274 * PerStateCO_Set --
4275 * PerStateCO_Get --
4276 * PerStateCO_Restore --
4277 * PerStateCO_Free --
4278 *
4279 * These procedures implement a TK_OPTION_CUSTOM where the custom
4280 * option is a PerStateInfo record.
4281 *
4282 * Results:
4283 * None.
4284 *
4285 * Side effects:
4286 * None.
4287 *
4288 *----------------------------------------------------------------------
4289 */
4290
4291 typedef struct PerStateCOClientData
4292 {
4293 PerStateType *typePtr;
4294 StateFromObjProc proc;
4295 } PerStateCOClientData;
4296
4297 static int
PerStateCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)4298 PerStateCO_Set(
4299 ClientData clientData,
4300 Tcl_Interp *interp,
4301 Tk_Window tkwin,
4302 Tcl_Obj **value,
4303 char *recordPtr,
4304 int internalOffset,
4305 char *saveInternalPtr,
4306 int flags
4307 )
4308 {
4309 PerStateCOClientData *cd = (PerStateCOClientData *) clientData;
4310 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4311 int objEmpty;
4312 PerStateInfo new, *internalPtr, *hax;
4313
4314 if (internalOffset >= 0)
4315 internalPtr = (PerStateInfo *) (recordPtr + internalOffset);
4316 else
4317 internalPtr = NULL;
4318
4319 objEmpty = ObjectIsEmpty((*value));
4320
4321 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
4322 (*value) = NULL;
4323 else {
4324 new.obj = (*value);
4325 new.data = NULL;
4326 new.count = 0;
4327 /* Tcl_IncrRefCount((*value));*/
4328 if (tree->configStateDomain == -1)
4329 panic("PerStateCO_Set configStateDomain == -1");
4330 if (PerStateInfo_FromObj(tree, tree->configStateDomain, cd->proc, cd->typePtr, &new) != TCL_OK) {
4331 /* Tcl_DecrRefCount((*value));*/
4332 return TCL_ERROR;
4333 }
4334 }
4335 if (internalPtr != NULL) {
4336 if ((*value) == NULL) {
4337 new.obj = NULL;
4338 new.data = NULL;
4339 new.count = 0;
4340 }
4341 OptionHax_Remember(tree, saveInternalPtr);
4342 if (internalPtr->obj != NULL) {
4343 hax = (PerStateInfo *) ckalloc(sizeof(PerStateInfo));
4344 *hax = *internalPtr;
4345 *((PerStateInfo **) saveInternalPtr) = hax;
4346 } else {
4347 *((PerStateInfo **) saveInternalPtr) = NULL;
4348 }
4349 *internalPtr = new;
4350 /*dbwin("PerStateCO_Set %p %s\n", internalPtr, cd->typePtr->name);*/
4351 }
4352
4353 return TCL_OK;
4354 }
4355
4356 static Tcl_Obj *
PerStateCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)4357 PerStateCO_Get(
4358 ClientData clientData,
4359 Tk_Window tkwin,
4360 char *recordPtr,
4361 int internalOffset
4362 )
4363 {
4364 PerStateInfo *value = (PerStateInfo *) (recordPtr + internalOffset);
4365 return value->obj; /* May be NULL. */
4366 }
4367
4368 static void
PerStateCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)4369 PerStateCO_Restore(
4370 ClientData clientData,
4371 Tk_Window tkwin,
4372 char *internalPtr,
4373 char *saveInternalPtr
4374 )
4375 {
4376 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4377 PerStateInfo *psi = (PerStateInfo *) internalPtr;
4378 PerStateInfo *hax = *(PerStateInfo **) saveInternalPtr;
4379 /*dbwin("PerStateCO_Restore\n");*/
4380 if (hax != NULL) {
4381 #ifdef TREECTRL_DEBUG
4382 psi->type = hax->type;
4383 #endif
4384 psi->data = hax->data;
4385 psi->count = hax->count;
4386 ckfree((char *) hax);
4387 } else {
4388 #ifdef TREECTRL_DEBUG
4389 psi->type = NULL;
4390 #endif
4391 /* psi->obj = NULL;*/
4392 psi->data = NULL;
4393 psi->count = 0;
4394 }
4395 OptionHax_Forget(tree, saveInternalPtr);
4396 }
4397
4398 static void
PerStateCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)4399 PerStateCO_Free(
4400 ClientData clientData, /* unused. */
4401 Tk_Window tkwin, /* A window; unused */
4402 char *internalPtr /* Pointer to the place, where the internal
4403 * form resides. */
4404 )
4405 {
4406 PerStateCOClientData *cd = (PerStateCOClientData *) clientData;
4407 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4408 PerStateInfo *hax;
4409 Tcl_Obj *objPtr = NULL;
4410
4411 if (OptionHax_Forget(tree, internalPtr)) {
4412 hax = *(PerStateInfo **) internalPtr;
4413 if (hax != NULL) {
4414 objPtr = hax->obj;
4415 PerStateInfo_Free(tree, cd->typePtr, hax);
4416 ckfree((char *) hax);
4417 }
4418 } else {
4419 /*dbwin("PerStateCO_Free %p %s\n", internalPtr, cd->typePtr->name);*/
4420 objPtr = ((PerStateInfo *) internalPtr)->obj;
4421 PerStateInfo_Free(tree, cd->typePtr, (PerStateInfo *) internalPtr);
4422 }
4423 /* if (objPtr != NULL)
4424 Tcl_DecrRefCount(objPtr);*/
4425 }
4426
4427 /*
4428 *----------------------------------------------------------------------
4429 *
4430 * PerStateCO_Alloc --
4431 *
4432 * Allocates a Tk_ObjCustomOption record and clientData.
4433 *
4434 * Results:
4435 * None.
4436 *
4437 * Side effects:
4438 * None.
4439 *
4440 *----------------------------------------------------------------------
4441 */
4442
4443 Tk_ObjCustomOption *
PerStateCO_Alloc(CONST char * optionName,PerStateType * typePtr,StateFromObjProc proc)4444 PerStateCO_Alloc(
4445 CONST char *optionName,
4446 PerStateType *typePtr,
4447 StateFromObjProc proc
4448 )
4449 {
4450 PerStateCOClientData *cd;
4451 Tk_ObjCustomOption *co;
4452
4453 /* ClientData for the Tk custom option record */
4454 cd = (PerStateCOClientData *) ckalloc(sizeof(PerStateCOClientData));
4455 cd->typePtr = typePtr;
4456 cd->proc = proc;
4457
4458 /* The Tk custom option record */
4459 co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
4460 co->name = (char *) optionName + 1;
4461 co->setProc = PerStateCO_Set;
4462 co->getProc = PerStateCO_Get;
4463 co->restoreProc = PerStateCO_Restore;
4464 co->freeProc = PerStateCO_Free;
4465 co->clientData = (ClientData) cd;
4466
4467 return co;
4468 }
4469
4470 /*
4471 *----------------------------------------------------------------------
4472 *
4473 * PerStateCO_Init --
4474 *
4475 * Initializes a Tk_OptionSpec.clientData for a custom option.
4476 *
4477 * Results:
4478 * None.
4479 *
4480 * Side effects:
4481 * None.
4482 *
4483 *----------------------------------------------------------------------
4484 */
4485
4486 int
PerStateCO_Init(Tk_OptionSpec * optionTable,CONST char * optionName,PerStateType * typePtr,StateFromObjProc proc)4487 PerStateCO_Init(
4488 Tk_OptionSpec *optionTable,
4489 CONST char *optionName,
4490 PerStateType *typePtr,
4491 StateFromObjProc proc
4492 )
4493 {
4494 Tk_OptionSpec *specPtr;
4495
4496 specPtr = Tree_FindOptionSpec(optionTable, optionName);
4497 if (specPtr->type != TK_OPTION_CUSTOM)
4498 panic("PerStateCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
4499 if (specPtr->clientData != NULL)
4500 return TCL_OK;
4501
4502 specPtr->clientData = PerStateCO_Alloc(optionName, typePtr, proc);
4503
4504 return TCL_OK;
4505 }
4506
4507 #define DEBUG_DYNAMICxxx
4508
4509 static CONST char *DynamicOptionUid = "DynamicOption";
4510
4511 /*
4512 *----------------------------------------------------------------------
4513 *
4514 * DynamicOption_Find --
4515 *
4516 * Returns a pointer to a dynamic-option record or NULL.
4517 *
4518 * Results:
4519 * None.
4520 *
4521 * Side effects:
4522 * None.
4523 *
4524 *----------------------------------------------------------------------
4525 */
4526
4527 static DynamicOption *
DynamicOption_Find(DynamicOption * first,int id)4528 DynamicOption_Find(
4529 DynamicOption *first, /* Head of linked list. */
4530 int id /* Unique id. */
4531 )
4532 {
4533 DynamicOption *opt = first;
4534
4535 while (opt != NULL) {
4536 if (opt->id == id)
4537 return opt;
4538 opt = opt->next;
4539 }
4540 return NULL;
4541 }
4542
4543 /*
4544 *----------------------------------------------------------------------
4545 *
4546 * DynamicOption_FindData --
4547 *
4548 * Returns a pointer to the option-specific data for a
4549 * dynamic-option record or NULL.
4550 *
4551 * Results:
4552 * None.
4553 *
4554 * Side effects:
4555 * None.
4556 *
4557 *----------------------------------------------------------------------
4558 */
4559
4560 void *
DynamicOption_FindData(DynamicOption * first,int id)4561 DynamicOption_FindData(
4562 DynamicOption *first, /* Head of linked list. */
4563 int id /* Unique id. */
4564 )
4565 {
4566 DynamicOption *opt = DynamicOption_Find(first, id);
4567 if (opt != NULL)
4568 return opt->data;
4569 return NULL;
4570 }
4571
4572 /*
4573 *----------------------------------------------------------------------
4574 *
4575 * DynamicOption_AllocIfNeeded --
4576 *
4577 * Returns a pointer to a dynamic-option record.
4578 *
4579 * Results:
4580 * If the dynamic-option record exists, it is returned. Otherwise
4581 * a new one is allocated and initialized.
4582 *
4583 * Side effects:
4584 * Memory may be allocated.
4585 *
4586 *----------------------------------------------------------------------
4587 */
4588
4589 DynamicOption *
DynamicOption_AllocIfNeeded(TreeCtrl * tree,DynamicOption ** firstPtr,int id,int size,DynamicOptionInitProc * init)4590 DynamicOption_AllocIfNeeded(
4591 TreeCtrl *tree,
4592 DynamicOption **firstPtr, /* Pointer to the head of linked list.
4593 * Will be updated if a new record is
4594 * created. */
4595 int id, /* Unique id. */
4596 int size, /* Size of option-specific data. */
4597 DynamicOptionInitProc *init /* Proc to intialize the option-specific
4598 * data. May be NULL. */
4599 )
4600 {
4601 DynamicOption *opt = *firstPtr;
4602
4603 while (opt != NULL) {
4604 if (opt->id == id)
4605 return opt;
4606 opt = opt->next;
4607 }
4608 #ifdef DEBUG_DYNAMIC
4609 dbwin("DynamicOption_AllocIfNeeded allocated id=%d\n", id);
4610 #endif
4611 #ifdef ALLOC_HAX
4612 opt = (DynamicOption *) TreeAlloc_Alloc(tree->allocData, DynamicOptionUid,
4613 Tk_Offset(DynamicOption, data) + size);
4614 #else
4615 opt = (DynamicOption *) ckalloc(Tk_Offset(DynamicOption, data) + size);
4616 #endif
4617 opt->id = id;
4618 memset(opt->data, '\0', size);
4619 if (init != NULL)
4620 (*init)(opt->data);
4621 opt->next = *firstPtr;
4622 *firstPtr = opt;
4623 return opt;
4624 }
4625
4626 /*
4627 *----------------------------------------------------------------------
4628 *
4629 * DynamicCO_Set --
4630 * DynamicCO_Get --
4631 * DynamicCO_Restore --
4632 * DynamicCO_Free --
4633 *
4634 * These procedures implement a TK_OPTION_CUSTOM where the custom
4635 * option is a DynamicOption record.
4636 *
4637 * A dynamic option is one for which storage is not allocated until
4638 * the option is configured for the first time. Dynamic options are
4639 * saved in a linked list.
4640 *
4641 * Results:
4642 * None.
4643 *
4644 * Side effects:
4645 * None.
4646 *
4647 *----------------------------------------------------------------------
4648 */
4649
4650 /* This is the Tk_OptionSpec.clientData field for a dynamic option. */
4651 typedef struct DynamicCOClientData
4652 {
4653 int id; /* Unique id. */
4654 int size; /* Size of client data. */
4655 int objOffset; /* Offset in the client data to store the
4656 * object representation of the option.
4657 * May be < 0. */
4658 int internalOffset; /* Offset in the client data to store the
4659 * internal representation of the option.
4660 * May be < 0. */
4661 Tk_ObjCustomOption *custom; /* Table of procedures and clientData for
4662 * the actual option. */
4663 DynamicOptionInitProc *init;/* This gets called to initialize the client
4664 * data when it is first allocated. May be
4665 * NULL. */
4666 } DynamicCOClientData;
4667
4668 /* This is used to save the current value of an option when a call to
4669 * Tk_SetOptions is in progress. */
4670 typedef struct DynamicCOSave
4671 {
4672 Tcl_Obj *objPtr; /* The object representation of the option. */
4673 double internalForm; /* The internal form of the option. */
4674 } DynamicCOSave;
4675
4676 static int
DynamicCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)4677 DynamicCO_Set(
4678 ClientData clientData,
4679 Tcl_Interp *interp,
4680 Tk_Window tkwin,
4681 Tcl_Obj **value,
4682 char *recordPtr,
4683 int internalOffset,
4684 char *saveInternalPtr,
4685 int flags
4686 )
4687 {
4688 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4689 DynamicCOClientData *cd = clientData;
4690 DynamicOption **firstPtr, *opt;
4691 DynamicCOSave *save;
4692 Tcl_Obj **objPtrPtr = NULL;
4693
4694 /* Get pointer to the head of the list of dynamic options. */
4695 firstPtr = (DynamicOption **) (recordPtr + internalOffset);
4696
4697 /* Get the dynamic option record. Create it if needed, and update the
4698 * linked list of dynamic options. */
4699 opt = DynamicOption_AllocIfNeeded(tree, firstPtr, cd->id, cd->size, cd->init);
4700
4701 if (cd->objOffset >= 0)
4702 objPtrPtr = (Tcl_Obj **) (opt->data + cd->objOffset);
4703
4704 save = (DynamicCOSave *) ckalloc(sizeof(DynamicCOSave));
4705 #ifdef DEBUG_DYNAMIC
4706 dbwin("DynamicCO_Set id=%d saveInternalPtr=%p save=%p\n", cd->id, saveInternalPtr, save);
4707 #endif
4708 if (objPtrPtr != NULL) {
4709 save->objPtr = *objPtrPtr;
4710 #ifdef DEBUG_DYNAMIC
4711 if (save->objPtr)
4712 dbwin(" old object '%s' refCount=%d\n", Tcl_GetString(save->objPtr), save->objPtr->refCount);
4713 else
4714 dbwin(" old object NULL\n");
4715 #endif
4716 }
4717 if (cd->custom->setProc(cd->custom->clientData, interp, tkwin, value,
4718 opt->data, cd->internalOffset, (char *) &save->internalForm,
4719 flags) != TCL_OK) {
4720 ckfree((char *) save);
4721 return TCL_ERROR;
4722 }
4723
4724 if (objPtrPtr != NULL) {
4725 #ifdef DEBUG_DYNAMIC
4726 if (*value)
4727 dbwin(" new object '%s' refCount=%d\n", Tcl_GetString(*value), (*value)->refCount);
4728 else
4729 dbwin(" new object NULL\n");
4730 #endif
4731 *objPtrPtr = *value;
4732 if (*value != NULL)
4733 Tcl_IncrRefCount(*value);
4734 }
4735
4736 *(DynamicCOSave **) saveInternalPtr = save;
4737 OptionHax_Remember(tree, saveInternalPtr);
4738
4739 return TCL_OK;
4740 }
4741
4742 static Tcl_Obj *
DynamicCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)4743 DynamicCO_Get(
4744 ClientData clientData,
4745 Tk_Window tkwin,
4746 char *recordPtr,
4747 int internalOffset
4748 )
4749 {
4750 DynamicCOClientData *cd = clientData;
4751 DynamicOption *first = *(DynamicOption **) (recordPtr + internalOffset);
4752 DynamicOption *opt = DynamicOption_Find(first, cd->id);
4753
4754 #ifdef DEBUG_DYNAMIC
4755 dbwin("DynamicCO_Get id=%d opt=%p objOffset=%d\n", cd->id, opt, cd->objOffset);
4756 #endif
4757 if (opt == NULL)
4758 return NULL;
4759
4760 if (cd->objOffset >= 0) {
4761 #ifdef TREECTRL_DEBUG
4762 Tcl_Obj *objPtr = *(Tcl_Obj **) (opt->data + cd->objOffset);
4763 if (objPtr && objPtr->refCount == 0) panic("DynamicCO_Get refCount=0");
4764 #endif
4765 return *(Tcl_Obj **) (opt->data + cd->objOffset);
4766 }
4767
4768 if (cd->custom->getProc != NULL)
4769 return cd->custom->getProc(cd->custom->clientData, tkwin, opt->data, cd->internalOffset);
4770 return NULL;
4771 }
4772
4773 static void
DynamicCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)4774 DynamicCO_Restore(
4775 ClientData clientData,
4776 Tk_Window tkwin,
4777 char *internalPtr,
4778 char *saveInternalPtr
4779 )
4780 {
4781 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4782 DynamicCOClientData *cd = clientData;
4783 DynamicOption *first = *(DynamicOption **) internalPtr;
4784 DynamicOption *opt = DynamicOption_Find(first, cd->id);
4785 DynamicCOSave *save = *(DynamicCOSave **)saveInternalPtr;
4786 Tcl_Obj **objPtrPtr;
4787
4788 if (opt == NULL)
4789 panic("DynamicCO_Restore: opt=NULL");
4790 #ifdef DEBUG_DYNAMIC
4791 dbwin("DynamicCO_Restore id=%d internalOffset=%d save=%p\n", cd->id, cd->internalOffset, save);
4792 #endif
4793 if (cd->internalOffset >= 0 && cd->custom->restoreProc != NULL)
4794 cd->custom->restoreProc(cd->custom->clientData, tkwin,
4795 opt->data + cd->internalOffset, (char *) &save->internalForm);
4796
4797 if (cd->objOffset >= 0) {
4798 objPtrPtr = (Tcl_Obj **) (opt->data + cd->objOffset);
4799 #ifdef DEBUG_DYNAMIC
4800 if (*objPtrPtr)
4801 dbwin("DynamicCO_Restore replace object '%s' refCount=%d\n", Tcl_GetString(*objPtrPtr), (*objPtrPtr)->refCount);
4802 else
4803 dbwin("DynamicCO_Restore replace object NULL\n");
4804 if (save->objPtr)
4805 dbwin("DynamicCO_Restore restore object '%s' refCount=%d\n", Tcl_GetString(save->objPtr), save->objPtr->refCount);
4806 else
4807 dbwin("DynamicCO_Restore restore object NULL\n");
4808 #endif
4809 /* if (*objPtrPtr != NULL)
4810 Tcl_DecrRefCount(*objPtrPtr);*/
4811 *objPtrPtr = save->objPtr;
4812 }
4813
4814 ckfree((char *) save);
4815 OptionHax_Forget(tree, saveInternalPtr);
4816 }
4817
4818 static void
DynamicCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)4819 DynamicCO_Free(
4820 ClientData clientData,
4821 Tk_Window tkwin,
4822 char *internalPtr
4823 )
4824 {
4825 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
4826 DynamicCOClientData *cd = clientData;
4827 Tcl_Obj **objPtrPtr;
4828
4829 if (OptionHax_Forget(tree, internalPtr)) {
4830 DynamicCOSave *save = *(DynamicCOSave **) internalPtr;
4831 #ifdef DEBUG_DYNAMIC
4832 dbwin("DynamicCO_Free id=%d internalPtr=%p save=%p\n", cd->id, internalPtr, save);
4833 #endif
4834 if (cd->internalOffset >= 0 && cd->custom->freeProc != NULL)
4835 cd->custom->freeProc(cd->custom->clientData, tkwin,
4836 (char *) &save->internalForm);
4837 if (cd->objOffset >= 0) {
4838 #ifdef DEBUG_DYNAMIC
4839 if (save->objPtr) {
4840 dbwin("DynamicCO_Free free object '%s' refCount=%d-1\n", Tcl_GetString(save->objPtr), (save->objPtr)->refCount);
4841 if (save->objPtr->refCount == 0) panic("DynamicCO_Free refCount=0");
4842 }
4843 else
4844 dbwin("DynamicCO_Free free object NULL\n");
4845 #endif
4846 if (save->objPtr) {
4847 Tcl_DecrRefCount(save->objPtr);
4848 }
4849 }
4850 ckfree((char *) save);
4851 } else {
4852 DynamicOption *first = *(DynamicOption **) internalPtr;
4853 DynamicOption *opt = DynamicOption_Find(first, cd->id);
4854 #ifdef DEBUG_DYNAMIC
4855 dbwin("DynamicCO_Free id=%d internalPtr=%p save=NULL\n", cd->id, internalPtr);
4856 #endif
4857 if (opt != NULL && cd->internalOffset >= 0 && cd->custom->freeProc != NULL)
4858 cd->custom->freeProc(cd->custom->clientData, tkwin,
4859 opt->data + cd->internalOffset);
4860 if (opt != NULL && cd->objOffset >= 0) {
4861 objPtrPtr = (Tcl_Obj **) (opt->data + cd->objOffset);
4862 #ifdef DEBUG_DYNAMIC
4863 if (*objPtrPtr) {
4864 dbwin("DynamicCO_Free free object '%s' refCount=%d-1\n", Tcl_GetString(*objPtrPtr), (*objPtrPtr)->refCount);
4865 if ((*objPtrPtr)->refCount == 0) panic("DynamicCO_Free refCount=0");
4866 }
4867 else
4868 dbwin("DynamicCO_Free free object NULL\n");
4869 #endif
4870 if (*objPtrPtr != NULL) {
4871 Tcl_DecrRefCount(*objPtrPtr);
4872 }
4873 }
4874 }
4875 }
4876
4877 /*
4878 *----------------------------------------------------------------------
4879 *
4880 * DynamicOption_Init --
4881 *
4882 * Initialize a Tk_OptionSpec.clientData field before calling
4883 * Tk_CreateOptionTable.
4884 *
4885 * Results:
4886 * None.
4887 *
4888 * Side effects:
4889 * Memory may be allocated.
4890 *
4891 *----------------------------------------------------------------------
4892 */
4893
4894 int
DynamicCO_Init(Tk_OptionSpec * optionTable,CONST char * optionName,int id,int size,int objOffset,int internalOffset,Tk_ObjCustomOption * custom,DynamicOptionInitProc * init)4895 DynamicCO_Init(
4896 Tk_OptionSpec *optionTable, /* Table to search. */
4897 CONST char *optionName, /* Name of the option. */
4898 int id, /* Unique id. */
4899 int size, /* Size of client data. */
4900 int objOffset, /* Offset in the client data to store the
4901 * object representation of the option.
4902 * May be < 0. */
4903 int internalOffset, /* Offset in the client data to store the
4904 * internal representation of the option.
4905 * May be < 0. */
4906 Tk_ObjCustomOption *custom, /* Table of procedures and clientData for
4907 * the actual option. */
4908 DynamicOptionInitProc *init /* This gets called to initialize the client
4909 * data when it is first allocated. May be
4910 * NULL. */
4911 )
4912 {
4913 Tk_OptionSpec *specPtr;
4914 DynamicCOClientData *cd;
4915 Tk_ObjCustomOption *co;
4916
4917 if (size <= 0)
4918 panic("DynamicCO_Init: option %s size=%d", optionName, size);
4919
4920 specPtr = Tree_FindOptionSpec(optionTable, optionName);
4921 if (specPtr->type != TK_OPTION_CUSTOM)
4922 panic("DynamicCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
4923 if (specPtr->clientData != NULL)
4924 return TCL_OK;
4925
4926 /* ClientData for the Tk custom option record */
4927 cd = (DynamicCOClientData *) ckalloc(sizeof(DynamicCOClientData));
4928 cd->id = id;
4929 cd->size = size;
4930 cd->objOffset = objOffset;
4931 cd->internalOffset = internalOffset;
4932 cd->custom = custom;
4933 cd->init = init;
4934
4935 /* The Tk custom option record */
4936 co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
4937 co->name = (char *) optionName + 1;
4938 co->setProc = DynamicCO_Set;
4939 co->getProc = DynamicCO_Get;
4940 co->restoreProc = DynamicCO_Restore;
4941 co->freeProc = DynamicCO_Free;
4942 co->clientData = (ClientData) cd;
4943
4944 /* Update the option table */
4945 specPtr->clientData = co;
4946 #ifdef DEBUG_DYNAMIC
4947 dbwin("DynamicCO_Init id=%d size=%d objOffset=%d internalOffset=%d custom->name=%s\n",
4948 id, size, objOffset, internalOffset, custom->name);
4949 #endif
4950 return TCL_OK;
4951 }
4952
4953 /*
4954 *----------------------------------------------------------------------
4955 *
4956 * DynamicOption_Free --
4957 *
4958 * Free a linked list of dynamic-option records. This gets called
4959 * after Tk_FreeConfigOptions.
4960 *
4961 * Results:
4962 * None.
4963 *
4964 * Side effects:
4965 * Memory may be freed.
4966 *
4967 *----------------------------------------------------------------------
4968 */
4969
4970 void
DynamicOption_Free(TreeCtrl * tree,DynamicOption * first,Tk_OptionSpec * optionTable)4971 DynamicOption_Free(
4972 TreeCtrl *tree,
4973 DynamicOption *first,
4974 Tk_OptionSpec *optionTable
4975 )
4976 {
4977 DynamicOption *opt = first;
4978 DynamicCOClientData *cd;
4979 Tk_ObjCustomOption *co;
4980 int i;
4981
4982 while (opt != NULL) {
4983 DynamicOption *next = opt->next;
4984 for (i = 0; optionTable[i].type != TK_OPTION_END; i++) {
4985
4986 if (optionTable[i].type != TK_OPTION_CUSTOM)
4987 continue;
4988
4989 co = (Tk_ObjCustomOption *) optionTable[i].clientData;
4990 if (co->setProc != DynamicCO_Set)
4991 continue;
4992
4993 cd = (DynamicCOClientData *) co->clientData;
4994 if (cd->id != opt->id)
4995 continue;
4996
4997 #ifdef ALLOC_HAX
4998 TreeAlloc_Free(tree->allocData, DynamicOptionUid, (char *) opt,
4999 Tk_Offset(DynamicOption, data) + cd->size);
5000 #else
5001 ckfree((char *) opt);
5002 #endif
5003 break;
5004 }
5005 opt = next;
5006 }
5007 }
5008
5009 /*
5010 *----------------------------------------------------------------------
5011 *
5012 * DynamicOption_Free1 --
5013 *
5014 * Free a single dynamic-option record. This is a big hack so that
5015 * dynamic-option records that aren't associated with a Tk_OptionSpec
5016 * array can be used.
5017 *
5018 * Results:
5019 * None.
5020 *
5021 * Side effects:
5022 * Memory may be freed.
5023 *
5024 *----------------------------------------------------------------------
5025 */
5026
5027 void
DynamicOption_Free1(TreeCtrl * tree,DynamicOption ** firstPtr,int id,int size)5028 DynamicOption_Free1(
5029 TreeCtrl *tree,
5030 DynamicOption **firstPtr,
5031 int id,
5032 int size
5033 )
5034 {
5035 DynamicOption *opt = *firstPtr, *prev = NULL;
5036
5037 while (opt != NULL) {
5038 if (opt->id == id) {
5039 if (prev == NULL)
5040 *firstPtr = opt->next;
5041 else
5042 prev->next = opt->next;
5043 #ifdef ALLOC_HAX
5044 TreeAlloc_Free(tree->allocData, DynamicOptionUid, (char *) opt,
5045 Tk_Offset(DynamicOption, data) + size);
5046 #else
5047 ckfree((char *) opt);
5048 #endif
5049 return;
5050 }
5051 prev = opt;
5052 opt = opt->next;
5053 }
5054 }
5055
5056 /*
5057 *----------------------------------------------------------------------
5058 *
5059 * Tree_InitOptions --
5060 * Tree_SetOptions --
5061 *
5062 * These procedures are just wrappers around Tk_InitOptions and
5063 * Tk_SetOptions. They set tree->configStateDomain so that any
5064 * per-state options know which state domain to use.
5065 *
5066 * Results:
5067 * A standard Tcl result.
5068 *
5069 * Side effects:
5070 * Whatever the wrapped function does.
5071 *
5072 *----------------------------------------------------------------------
5073 */
5074
5075 int
Tree_InitOptions(TreeCtrl * tree,int domain,void * recordPtr,Tk_OptionTable optionTable)5076 Tree_InitOptions(
5077 TreeCtrl *tree,
5078 int domain,
5079 void *recordPtr,
5080 Tk_OptionTable optionTable
5081 )
5082 {
5083 int result;
5084
5085 if (tree->configStateDomain != -1)
5086 panic("Tree_InitOptions configStateDomain != -1");
5087
5088 tree->configStateDomain = domain;
5089
5090 result = Tk_InitOptions(tree->interp, recordPtr, optionTable, tree->tkwin);
5091
5092 tree->configStateDomain = -1;
5093 return result;
5094 }
5095
5096 int
Tree_SetOptions(TreeCtrl * tree,int domain,void * recordPtr,Tk_OptionTable optionTable,int objc,Tcl_Obj * CONST objv[],Tk_SavedOptions * savePtr,int * maskPtr)5097 Tree_SetOptions(
5098 TreeCtrl *tree,
5099 int domain,
5100 void *recordPtr,
5101 Tk_OptionTable optionTable,
5102 int objc,
5103 Tcl_Obj *CONST objv[],
5104 Tk_SavedOptions *savePtr,
5105 int *maskPtr
5106 )
5107 {
5108 int result;
5109
5110 if (tree->configStateDomain != -1)
5111 panic("Tree_SetOptions configStateDomain != -1");
5112
5113 tree->configStateDomain = domain;
5114
5115 result = Tk_SetOptions(tree->interp, recordPtr, optionTable, objc, objv,
5116 tree->tkwin, savePtr, maskPtr);
5117
5118 tree->configStateDomain = -1;
5119 return result;
5120 }
5121
5122 /*
5123 *----------------------------------------------------------------------
5124 *
5125 * StringCO_Set --
5126 * StringCO_Get --
5127 * StringCO_Restore --
5128 * StringCO_Free --
5129 *
5130 * These procedures implement a TK_OPTION_CUSTOM where the custom
5131 * option is exactly the same as a TK_OPTION_STRING. This is used
5132 * when storage for the option is dynamically allocated.
5133 *
5134 * Results:
5135 * None.
5136 *
5137 * Side effects:
5138 * None.
5139 *
5140 *----------------------------------------------------------------------
5141 */
5142
5143 static int
StringCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)5144 StringCO_Set(
5145 ClientData clientData,
5146 Tcl_Interp *interp,
5147 Tk_Window tkwin,
5148 Tcl_Obj **valuePtr,
5149 char *recordPtr,
5150 int internalOffset,
5151 char *saveInternalPtr,
5152 int flags
5153 )
5154 {
5155 int objEmpty;
5156 char *internalPtr, *new, *value;
5157 int length;
5158
5159 if (internalOffset >= 0)
5160 internalPtr = (char *) (recordPtr + internalOffset);
5161 else
5162 internalPtr = NULL;
5163
5164 objEmpty = ObjectIsEmpty((*valuePtr));
5165
5166 if ((flags & TK_OPTION_NULL_OK) && objEmpty)
5167 (*valuePtr) = NULL;
5168
5169 if (internalPtr != NULL) {
5170 if (*valuePtr != NULL) {
5171 value = Tcl_GetStringFromObj(*valuePtr, &length);
5172 new = ckalloc((unsigned) (length + 1));
5173 strcpy(new, value);
5174 } else {
5175 new = NULL;
5176 }
5177 *((char **) saveInternalPtr) = *((char **) internalPtr);
5178 *((char **) internalPtr) = new;
5179 }
5180
5181 return TCL_OK;
5182 }
5183
5184 static Tcl_Obj *
StringCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5185 StringCO_Get(
5186 ClientData clientData,
5187 Tk_Window tkwin,
5188 char *recordPtr,
5189 int internalOffset
5190 )
5191 {
5192 char **internalPtr = (char **) (recordPtr + internalOffset);
5193
5194 return Tcl_NewStringObj(*internalPtr, -1);
5195 }
5196
5197 static void
StringCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)5198 StringCO_Restore(
5199 ClientData clientData,
5200 Tk_Window tkwin,
5201 char *internalPtr,
5202 char *saveInternalPtr
5203 )
5204 {
5205 *(char **) internalPtr = *(char **) saveInternalPtr;
5206 }
5207
5208 static void
StringCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)5209 StringCO_Free(
5210 ClientData clientData,
5211 Tk_Window tkwin,
5212 char *internalPtr
5213 )
5214 {
5215 if (*((char **) internalPtr) != NULL) {
5216 ckfree(*((char **) internalPtr));
5217 *((char **) internalPtr) = NULL;
5218 }
5219 }
5220
5221 Tk_ObjCustomOption TreeCtrlCO_string =
5222 {
5223 "string",
5224 StringCO_Set,
5225 StringCO_Get,
5226 StringCO_Restore,
5227 StringCO_Free,
5228 (ClientData) NULL
5229 };
5230
5231 /*
5232 *----------------------------------------------------------------------
5233 *
5234 * PixelsCO_Set --
5235 * PixelsCO_Get --
5236 * PixelsCO_Restore --
5237 *
5238 * These procedures implement a TK_OPTION_CUSTOM where the custom
5239 * option is exactly the same as a TK_OPTION_PIXELS. This is used
5240 * when storage for the option is dynamically allocated.
5241 *
5242 * Results:
5243 * None.
5244 *
5245 * Side effects:
5246 * None.
5247 *
5248 *----------------------------------------------------------------------
5249 */
5250
5251 static int
PixelsCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)5252 PixelsCO_Set(
5253 ClientData clientData,
5254 Tcl_Interp *interp,
5255 Tk_Window tkwin,
5256 Tcl_Obj **valuePtr,
5257 char *recordPtr,
5258 int internalOffset,
5259 char *saveInternalPtr,
5260 int flags
5261 )
5262 {
5263 int objEmpty;
5264 int *internalPtr, new;
5265
5266 if (internalOffset >= 0)
5267 internalPtr = (int *) (recordPtr + internalOffset);
5268 else
5269 internalPtr = NULL;
5270
5271 objEmpty = ObjectIsEmpty((*valuePtr));
5272
5273 if ((flags & TK_OPTION_NULL_OK) && objEmpty) {
5274 (*valuePtr) = NULL;
5275 new = 0;
5276 } else {
5277 if (Tk_GetPixelsFromObj(interp, tkwin, *valuePtr, &new) != TCL_OK)
5278 return TCL_ERROR;
5279 }
5280
5281 if (internalPtr != NULL) {
5282 *((int *) saveInternalPtr) = *internalPtr;
5283 *internalPtr = new;
5284 }
5285
5286 return TCL_OK;
5287 }
5288
5289 static Tcl_Obj *
PixelsCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5290 PixelsCO_Get(
5291 ClientData clientData,
5292 Tk_Window tkwin,
5293 char *recordPtr,
5294 int internalOffset
5295 )
5296 {
5297 int *internalPtr = (int *) (recordPtr + internalOffset);
5298
5299 return Tcl_NewIntObj(*internalPtr);
5300 }
5301
5302 static void
PixelsCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)5303 PixelsCO_Restore(
5304 ClientData clientData,
5305 Tk_Window tkwin,
5306 char *internalPtr,
5307 char *saveInternalPtr
5308 )
5309 {
5310 *(int *) internalPtr = *(int *) saveInternalPtr;
5311 }
5312
5313 Tk_ObjCustomOption TreeCtrlCO_pixels =
5314 {
5315 "string",
5316 PixelsCO_Set,
5317 PixelsCO_Get,
5318 PixelsCO_Restore,
5319 NULL,
5320 (ClientData) NULL
5321 };
5322
5323 /*
5324 *----------------------------------------------------------------------
5325 *
5326 * StyleCO_Set --
5327 * StyleCO_Get --
5328 * StyleCO_Restore --
5329 *
5330 * These procedures implement a TK_OPTION_CUSTOM where the custom
5331 * option is a TreeStyle.
5332 *
5333 * Results:
5334 * None.
5335 *
5336 * Side effects:
5337 * None.
5338 *
5339 *----------------------------------------------------------------------
5340 */
5341
5342 static int
StyleCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)5343 StyleCO_Set(
5344 ClientData clientData,
5345 Tcl_Interp *interp,
5346 Tk_Window tkwin,
5347 Tcl_Obj **valuePtr,
5348 char *recordPtr,
5349 int internalOffset,
5350 char *saveInternalPtr,
5351 int flags
5352 )
5353 {
5354 int domain = PTR2INT(clientData), domainS;
5355 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
5356 int objEmpty;
5357 TreeStyle *internalPtr, new;
5358
5359 if (internalOffset >= 0)
5360 internalPtr = (TreeStyle *) (recordPtr + internalOffset);
5361 else
5362 internalPtr = NULL;
5363
5364 objEmpty = ObjectIsEmpty((*valuePtr));
5365
5366 if ((flags & TK_OPTION_NULL_OK) && objEmpty) {
5367 (*valuePtr) = NULL;
5368 new = NULL;
5369 } else {
5370 if (TreeStyle_FromObj(tree, *valuePtr, &new) != TCL_OK)
5371 return TCL_ERROR;
5372 domainS = TreeStyle_GetStateDomain(tree, new);
5373 if (domainS != domain) {
5374 FormatResult(interp,
5375 "expected state domain \"%s\" but got \"%s\"",
5376 tree->stateDomain[domain].name,
5377 tree->stateDomain[domainS].name);
5378 return TCL_ERROR;
5379 }
5380 }
5381
5382 if (internalPtr != NULL) {
5383 *((TreeStyle *) saveInternalPtr) = *internalPtr;
5384 *internalPtr = new;
5385 }
5386
5387 return TCL_OK;
5388 }
5389
5390 static Tcl_Obj *
StyleCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5391 StyleCO_Get(
5392 ClientData clientData,
5393 Tk_Window tkwin,
5394 char *recordPtr,
5395 int internalOffset
5396 )
5397 {
5398 TreeStyle *internalPtr = (TreeStyle *) (recordPtr + internalOffset);
5399
5400 if (*internalPtr == NULL)
5401 return NULL;
5402 return TreeStyle_ToObj(*internalPtr);
5403 }
5404
5405 static void
StyleCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)5406 StyleCO_Restore(
5407 ClientData clientData,
5408 Tk_Window tkwin,
5409 char *internalPtr,
5410 char *saveInternalPtr
5411 )
5412 {
5413 *(TreeStyle *) internalPtr = *(TreeStyle *) saveInternalPtr;
5414 }
5415
5416 Tk_ObjCustomOption TreeCtrlCO_style =
5417 {
5418 "style",
5419 StyleCO_Set,
5420 StyleCO_Get,
5421 StyleCO_Restore,
5422 NULL,
5423 (ClientData) NULL
5424 };
5425
5426 /*
5427 *----------------------------------------------------------------------
5428 *
5429 * TreeStyleCO_Init --
5430 *
5431 * Initializes a Tk_OptionSpec.clientData for a custom option.
5432 *
5433 * Results:
5434 * None.
5435 *
5436 * Side effects:
5437 * None.
5438 *
5439 *----------------------------------------------------------------------
5440 */
5441
5442 void
TreeStyleCO_Init(Tk_OptionSpec * optionTable,CONST char * optionName,int domain)5443 TreeStyleCO_Init(
5444 Tk_OptionSpec *optionTable,
5445 CONST char *optionName,
5446 int domain
5447 )
5448 {
5449 Tk_OptionSpec *specPtr;
5450 Tk_ObjCustomOption *co;
5451
5452 specPtr = Tree_FindOptionSpec(optionTable, optionName);
5453 if (specPtr->type != TK_OPTION_CUSTOM)
5454 panic("TreeStyleCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
5455 if (specPtr->clientData != NULL)
5456 return;
5457
5458 co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
5459 *co = TreeCtrlCO_style;
5460 co->clientData = INT2PTR(domain);
5461
5462 specPtr->clientData = co;
5463 }
5464
5465 /*
5466 *----------------------------------------------------------------------
5467 *
5468 * BooleanFlagCO_Set --
5469 * BooleanFlagCO_Get --
5470 * BooleanFlagCO_Restore --
5471 *
5472 * These procedures implement a TK_OPTION_CUSTOM where the custom
5473 * option is a boolean value whose internal rep is a single bit of
5474 * an int rather than the entire int.
5475 *
5476 * Results:
5477 * None.
5478 *
5479 * Side effects:
5480 * None.
5481 *
5482 *----------------------------------------------------------------------
5483 */
5484
5485 static int
BooleanFlagCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)5486 BooleanFlagCO_Set(
5487 ClientData clientData,
5488 Tcl_Interp *interp,
5489 Tk_Window tkwin,
5490 Tcl_Obj **value,
5491 char *recordPtr,
5492 int internalOffset,
5493 char *saveInternalPtr,
5494 int flags
5495 )
5496 {
5497 int theFlag = PTR2INT(clientData);
5498 int new, *internalPtr;
5499
5500 if (internalOffset >= 0)
5501 internalPtr = (int *) (recordPtr + internalOffset);
5502 else
5503 internalPtr = NULL;
5504
5505 if (Tcl_GetBooleanFromObj(interp, (*value), &new) != TCL_OK)
5506 return TCL_ERROR;
5507
5508 if (internalPtr != NULL) {
5509 *((int *) saveInternalPtr) = *internalPtr;
5510 if (new)
5511 *internalPtr |= theFlag;
5512 else
5513 *internalPtr &= ~theFlag;
5514 }
5515
5516 return TCL_OK;
5517 }
5518
5519 static Tcl_Obj *
BooleanFlagCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5520 BooleanFlagCO_Get(
5521 ClientData clientData,
5522 Tk_Window tkwin,
5523 char *recordPtr,
5524 int internalOffset
5525 )
5526 {
5527 int theFlag = PTR2INT(clientData);
5528 int value = *(int *) (recordPtr + internalOffset);
5529
5530 return Tcl_NewBooleanObj(value & theFlag);
5531 }
5532
5533 static void
BooleanFlagCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)5534 BooleanFlagCO_Restore(
5535 ClientData clientData,
5536 Tk_Window tkwin,
5537 char *internalPtr,
5538 char *saveInternalPtr
5539 )
5540 {
5541 int theFlag = PTR2INT(clientData);
5542 int value = *(int *) saveInternalPtr;
5543
5544 if (value & theFlag)
5545 *((int *) internalPtr) |= theFlag;
5546 else
5547 *((int *) internalPtr) &= ~theFlag;
5548 }
5549
5550 int
BooleanFlagCO_Init(Tk_OptionSpec * optionTable,CONST char * optionName,int theFlag)5551 BooleanFlagCO_Init(
5552 Tk_OptionSpec *optionTable,
5553 CONST char *optionName,
5554 int theFlag
5555 )
5556 {
5557 Tk_OptionSpec *specPtr;
5558 Tk_ObjCustomOption *co;
5559
5560 specPtr = Tree_FindOptionSpec(optionTable, optionName);
5561 if (specPtr->type != TK_OPTION_CUSTOM)
5562 panic("BooleanFlagCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
5563 if (specPtr->clientData != NULL)
5564 return TCL_OK;
5565
5566 /* The Tk custom option record */
5567 co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
5568 co->name = "boolean";
5569 co->setProc = BooleanFlagCO_Set;
5570 co->getProc = BooleanFlagCO_Get;
5571 co->restoreProc = BooleanFlagCO_Restore;
5572 co->freeProc = NULL;
5573 co->clientData = (ClientData) INT2PTR(theFlag);
5574
5575 specPtr->clientData = co;
5576
5577 return TCL_OK;
5578 }
5579
5580 /*
5581 *----------------------------------------------------------------------
5582 *
5583 * ItemButtonCO_Set --
5584 * ItemButtonCO_Get --
5585 * ItemButtonCO_Restore --
5586 *
5587 * These procedures implement a TK_OPTION_CUSTOM where the custom
5588 * option is a boolean value or "auto"; the internal rep is two
5589 * bits of an int: one bit for the boolean, and one for "auto".
5590 * This is used for the item option -button.
5591 *
5592 * Results:
5593 * None.
5594 *
5595 * Side effects:
5596 * None.
5597 *
5598 *----------------------------------------------------------------------
5599 */
5600
5601 struct ItemButtonCOClientData {
5602 int flag1; /* Bit to set when object is "true". */
5603 int flag2; /* Bit to set when object is "auto". */
5604 };
5605
5606 static int
ItemButtonCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)5607 ItemButtonCO_Set(
5608 ClientData clientData,
5609 Tcl_Interp *interp,
5610 Tk_Window tkwin,
5611 Tcl_Obj **value,
5612 char *recordPtr,
5613 int internalOffset,
5614 char *saveInternalPtr,
5615 int flags
5616 )
5617 {
5618 struct ItemButtonCOClientData *cd = clientData;
5619 int new, *internalPtr, on, off;
5620 char *s;
5621 int length;
5622
5623 if (internalOffset >= 0)
5624 internalPtr = (int *) (recordPtr + internalOffset);
5625 else
5626 internalPtr = NULL;
5627
5628 s = Tcl_GetStringFromObj((*value), &length);
5629 if (s[0] == 'a' && strncmp(s, "auto", length) == 0) {
5630 on = cd->flag2;
5631 off = cd->flag1;
5632 } else {
5633 if (Tcl_GetBooleanFromObj(interp, (*value), &new) != TCL_OK) {
5634 FormatResult(interp, "expected boolean or auto but got \"%s\"", s);
5635 return TCL_ERROR;
5636 }
5637 if (new) {
5638 on = cd->flag1;
5639 off = cd->flag2;
5640 } else {
5641 on = 0;
5642 off = cd->flag1 | cd->flag2;
5643 }
5644 }
5645
5646 if (internalPtr != NULL) {
5647 *((int *) saveInternalPtr) = *internalPtr;
5648 *internalPtr |= on;
5649 *internalPtr &= ~off;
5650 }
5651
5652 return TCL_OK;
5653 }
5654
5655 static Tcl_Obj *
ItemButtonCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)5656 ItemButtonCO_Get(
5657 ClientData clientData,
5658 Tk_Window tkwin,
5659 char *recordPtr,
5660 int internalOffset
5661 )
5662 {
5663 struct ItemButtonCOClientData *cd = clientData;
5664 int value = *(int *) (recordPtr + internalOffset);
5665
5666 if (value & cd->flag2)
5667 return Tcl_NewStringObj("auto", -1);
5668 return Tcl_NewBooleanObj((value & cd->flag1) != 0);
5669 }
5670
5671 static void
ItemButtonCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)5672 ItemButtonCO_Restore(
5673 ClientData clientData,
5674 Tk_Window tkwin,
5675 char *internalPtr,
5676 char *saveInternalPtr
5677 )
5678 {
5679 struct ItemButtonCOClientData *cd = clientData;
5680 int value = *(int *) saveInternalPtr;
5681
5682 *((int *) internalPtr) &= ~(cd->flag1 | cd->flag2);
5683 *((int *) internalPtr) |= value & (cd->flag1 | cd->flag2);
5684 }
5685
5686 int
ItemButtonCO_Init(Tk_OptionSpec * optionTable,CONST char * optionName,int flag1,int flag2)5687 ItemButtonCO_Init(
5688 Tk_OptionSpec *optionTable,
5689 CONST char *optionName,
5690 int flag1,
5691 int flag2
5692 )
5693 {
5694 Tk_OptionSpec *specPtr;
5695 Tk_ObjCustomOption *co;
5696 struct ItemButtonCOClientData *cd;
5697
5698 specPtr = Tree_FindOptionSpec(optionTable, optionName);
5699 if (specPtr->type != TK_OPTION_CUSTOM)
5700 panic("BooleanFlagCO_Init: %s is not TK_OPTION_CUSTOM", optionName);
5701 if (specPtr->clientData != NULL)
5702 return TCL_OK;
5703
5704 /* ClientData for the Tk custom option record. */
5705 cd = (struct ItemButtonCOClientData *)ckalloc(
5706 sizeof(struct ItemButtonCOClientData));
5707 cd->flag1 = flag1;
5708 cd->flag2 = flag2;
5709
5710 /* The Tk custom option record */
5711 co = (Tk_ObjCustomOption *) ckalloc(sizeof(Tk_ObjCustomOption));
5712 co->name = "button option";
5713 co->setProc = ItemButtonCO_Set;
5714 co->getProc = ItemButtonCO_Get;
5715 co->restoreProc = ItemButtonCO_Restore;
5716 co->freeProc = NULL;
5717 co->clientData = cd;
5718
5719 specPtr->clientData = co;
5720
5721 return TCL_OK;
5722 }
5723
5724 /*
5725 *----------------------------------------------------------------------
5726 *
5727 * Tree_GetIntForIndex --
5728 *
5729 * This is basically a direct copy of TclGetIntForIndex with one
5730 * important difference: the caller gets to know whether the index
5731 * was of the form "end?-offset?".
5732 *
5733 * Results:
5734 * None.
5735 *
5736 * Side effects:
5737 * None.
5738 *
5739 *----------------------------------------------------------------------
5740 */
5741
5742 int
Tree_GetIntForIndex(TreeCtrl * tree,Tcl_Obj * objPtr,int * indexPtr,int * endRelativePtr)5743 Tree_GetIntForIndex(
5744 TreeCtrl *tree, /* Widget info. */
5745 Tcl_Obj *objPtr, /* Points to an object containing either "end"
5746 * or an integer. */
5747 int *indexPtr, /* Location filled in with an integer
5748 * representing an index. */
5749 int *endRelativePtr /* Set to 1 if the returned index is relative
5750 * to "end". */
5751 )
5752 {
5753 int endValue = 0;
5754 char *bytes;
5755
5756 if (TclGetIntForIndex(tree->interp, objPtr, endValue, indexPtr) != TCL_OK)
5757 return TCL_ERROR;
5758
5759 bytes = Tcl_GetString(objPtr);
5760 if (*bytes == 'e') {
5761 *endRelativePtr = 1;
5762 } else {
5763 *endRelativePtr = 0;
5764 }
5765 return TCL_OK;
5766 }
5767
5768 /*
5769 *----------------------------------------------------------------------
5770 *
5771 * Tree_DrawRoundRectX11 --
5772 *
5773 * Draw a rounded rectangle with a solid color.
5774 *
5775 * Results:
5776 * None.
5777 *
5778 * Side effects:
5779 * Drawing.
5780 *
5781 *----------------------------------------------------------------------
5782 */
5783
5784 void
Tree_DrawRoundRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,GC gc,TreeRectangle tr,int outlineWidth,int rx,int ry,int open)5785 Tree_DrawRoundRectX11(
5786 TreeCtrl *tree, /* Widget info. */
5787 TreeDrawable td, /* Where to draw. */
5788 TreeClip *clip, /* Clipping area or NULL. */
5789 GC gc, /* Graphics context. */
5790 TreeRectangle tr, /* Where to draw. */
5791 int outlineWidth,
5792 int rx, int ry, /* Corner radius */
5793 int open /* RECT_OPEN_x flags */
5794 )
5795 {
5796 int x = tr.x, y = tr.y, width = tr.width, height = tr.height;
5797 TreeRectangle rects[4], *pr = rects;
5798 int nrects = 0;
5799 int drawW = (open & RECT_OPEN_W) == 0;
5800 int drawN = (open & RECT_OPEN_N) == 0;
5801 int drawE = (open & RECT_OPEN_E) == 0;
5802 int drawS = (open & RECT_OPEN_S) == 0;
5803 int i;
5804
5805 /* Calculate the bounds of each edge that should be drawn */
5806 if (drawW) {
5807 pr->x = x, pr->y = y, pr->width = outlineWidth, pr->height = height;
5808 if (drawN)
5809 pr->y += ry, pr->height -= ry;
5810 if (drawS)
5811 pr->height -= ry;
5812 if (pr->width > 0 && pr->height > 0)
5813 pr++, nrects++;
5814 }
5815 if (drawN) {
5816 pr->x = x, pr->y = y, pr->width = width, pr->height = outlineWidth;
5817 if (drawW)
5818 pr->x += rx, pr->width -= rx;
5819 if (drawE)
5820 pr->width -= rx;
5821 if (pr->width > 0 && pr->height > 0)
5822 pr++, nrects++;
5823 }
5824 if (drawE) {
5825 pr->x = x + width - outlineWidth, pr->y = y, pr->width = outlineWidth, pr->height = height;
5826 if (drawN)
5827 pr->y += ry, pr->height -= ry;
5828 if (drawS)
5829 pr->height -= ry;
5830 if (pr->width > 0 && pr->height > 0)
5831 pr++, nrects++;
5832 }
5833 if (drawS) {
5834 pr->x = x, pr->y = y + height - outlineWidth, pr->width = width, pr->height = outlineWidth;
5835 if (drawW)
5836 pr->x += rx, pr->width -= rx;
5837 if (drawE)
5838 pr->width -= rx;
5839 if (pr->width > 0 && pr->height > 0)
5840 pr++, nrects++;
5841 }
5842 for (i = 0; i < nrects; i++)
5843 Tree_FillRectangle(tree, td, clip, gc, rects[i]);
5844
5845 /* On Win32 the code below works, leaving a 1-pixel hole at each
5846 * corner. But on X11 there is no hole. */
5847 if (rx == 1 && ry == 1)
5848 return;
5849
5850 width -= 1, height -= 1;
5851
5852 if (drawW && drawN)
5853 Tree_DrawArc(tree, td, clip, gc, x, y, rx*2, ry*2, 64*90, 64*90); /* top-left */
5854 if (drawW && drawS)
5855 Tree_DrawArc(tree, td, clip, gc, x, y + height - ry*2, rx*2, ry*2, 64*180, 64*90); /* bottom-left */
5856 if (drawE && drawN)
5857 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y, rx*2, ry*2, 64*0, 64*90); /* top-right */
5858 if (drawE && drawS)
5859 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y + height - ry*2, rx*2, ry*2, 64*270, 64*90); /* bottom-right */
5860
5861 for (i = 1; i < outlineWidth; i++) {
5862 x += 1, width -= 2;
5863 if (drawW && drawN)
5864 Tree_DrawArc(tree, td, clip, gc, x, y, rx*2, ry*2, 64*90, 64*90); /* top-left */
5865 if (drawW && drawS)
5866 Tree_DrawArc(tree, td, clip, gc, x, y + height - ry*2, rx*2, ry*2, 64*180, 64*90); /* bottom-left */
5867 if (drawE && drawN)
5868 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y, rx*2, ry*2, 64*0, 64*90); /* top-right */
5869 if (drawE && drawS)
5870 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y + height - ry*2, rx*2, ry*2, 64*270, 64*90); /* bottom-right */
5871
5872 y += 1, height -= 2;
5873 if (drawW && drawN)
5874 Tree_DrawArc(tree, td, clip, gc, x, y, rx*2, ry*2, 64*90, 64*90); /* top-left */
5875 if (drawW && drawS)
5876 Tree_DrawArc(tree, td, clip, gc, x, y + height - ry*2, rx*2, ry*2, 64*180, 64*90); /* bottom-left */
5877 if (drawE && drawN)
5878 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y, rx*2, ry*2, 64*0, 64*90); /* top-right */
5879 if (drawE && drawS)
5880 Tree_DrawArc(tree, td, clip, gc, x + width - rx*2, y + height - ry*2, rx*2, ry*2, 64*270, 64*90); /* bottom-right */
5881 }
5882 }
5883
5884 /*
5885 *----------------------------------------------------------------------
5886 *
5887 * Tree_FillRoundRectX11 --
5888 *
5889 * Fill a rounded rectangle with a solid color.
5890 *
5891 * Results:
5892 * None.
5893 *
5894 * Side effects:
5895 * Drawing.
5896 *
5897 *----------------------------------------------------------------------
5898 */
5899
5900 void
Tree_FillRoundRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,GC gc,TreeRectangle tr,int rx,int ry,int open)5901 Tree_FillRoundRectX11(
5902 TreeCtrl *tree, /* Widget info. */
5903 TreeDrawable td, /* Where to draw. */
5904 TreeClip *clip, /* Clipping area or NULL. */
5905 GC gc, /* Graphics context. */
5906 TreeRectangle tr, /* Rectangle to paint. */
5907 int rx, int ry, /* Corner radius */
5908 int open /* RECT_OPEN_x flags */
5909 )
5910 {
5911 TreeRectangle rects[3], *rectp = rects;
5912 int nrects = 0;
5913 int drawW = (open & RECT_OPEN_W) == 0;
5914 int drawN = (open & RECT_OPEN_N) == 0;
5915 int drawE = (open & RECT_OPEN_E) == 0;
5916 int drawS = (open & RECT_OPEN_S) == 0;
5917 int i;
5918
5919 tr.height -= 1, tr.width -= 1;
5920 if (drawW && drawN)
5921 Tree_FillArc(tree, td, clip, gc, tr.x, tr.y, rx*2, ry*2, 64*90, 64*90); /* top-left */
5922 if (drawW && drawS)
5923 Tree_FillArc(tree, td, clip, gc, tr.x, tr.y + tr.height - ry*2, rx*2, ry*2, 64*180, 64*90); /* bottom-left */
5924 if (drawE && drawN)
5925 Tree_FillArc(tree, td, clip, gc, tr.x + tr.width - rx*2, tr.y, rx*2, ry*2, 64*0, 64*90); /* top-right */
5926 if (drawE && drawS)
5927 Tree_FillArc(tree, td, clip, gc, tr.x + tr.width - rx*2, tr.y + tr.height - ry*2, rx*2, ry*2, 64*270, 64*90); /* bottom-right */
5928 tr.height += 1, tr.width += 1;
5929
5930 /* Everything but left/right edges */
5931 rectp->x = tr.x + rx;
5932 rectp->y = tr.y;
5933 rectp->width = tr.width - rx * 2;
5934 rectp->height = tr.height;
5935 if (rectp->width > 0 && rectp->height > 0) {
5936 nrects++;
5937 rectp++;
5938 }
5939
5940 /* Left edge */
5941 rectp->x = tr.x;
5942 rectp->y = tr.y;
5943 rectp->width = rx;
5944 rectp->height = tr.height;
5945 if (drawW && drawN)
5946 rectp->y += ry, rectp->height -= ry;
5947 if (drawW && drawS)
5948 rectp->height -= ry;
5949 if (rectp->width > 0 && rectp->height > 0) {
5950 nrects++;
5951 rectp++;
5952 }
5953
5954 /* Right edge */
5955 rectp->x = tr.x + tr.width - rx;
5956 rectp->y = tr.y;
5957 rectp->width = rx;
5958 rectp->height = tr.height;
5959 if (drawE && drawN)
5960 rectp->y += ry, rectp->height -= ry;
5961 if (drawE && drawS)
5962 rectp->height -= ry;
5963 if (rectp->width > 0 && rectp->height > 0) {
5964 nrects++;
5965 rectp++;
5966 }
5967
5968 for (i = 0; i < nrects; i++)
5969 Tree_FillRectangle(tree, td, clip, gc, rects[i]);
5970 }
5971
5972 /*****/
5973
5974 typedef struct PerStateDataGradient PerStateDataGradient;
5975 struct PerStateDataGradient
5976 {
5977 PerStateData header;
5978 TreeGradient gradient;
5979 };
5980
5981 static int
PSDGradientFromObj(TreeCtrl * tree,Tcl_Obj * obj,PerStateDataGradient * pGradient)5982 PSDGradientFromObj(
5983 TreeCtrl *tree,
5984 Tcl_Obj *obj,
5985 PerStateDataGradient *pGradient)
5986 {
5987 if (ObjectIsEmpty(obj)) {
5988 /* Specify empty string to override masterX */
5989 pGradient->gradient = NULL;
5990 } else {
5991 if (TreeGradient_FromObj(tree, obj, &pGradient->gradient) != TCL_OK)
5992 return TCL_ERROR;
5993 pGradient->gradient->refCount++;
5994 }
5995 return TCL_OK;
5996 }
5997
5998 static void
PSDGradientFree(TreeCtrl * tree,PerStateDataGradient * pGradient)5999 PSDGradientFree(
6000 TreeCtrl *tree,
6001 PerStateDataGradient *pGradient)
6002 {
6003 if (pGradient->gradient != NULL)
6004 TreeGradient_Release(tree, pGradient->gradient);
6005 }
6006
6007 PerStateType pstGradient =
6008 {
6009 "pstGradient",
6010 sizeof(PerStateDataGradient),
6011 (PerStateType_FromObjProc) PSDGradientFromObj,
6012 (PerStateType_FreeProc) PSDGradientFree
6013 };
6014
6015 TreeGradient
PerStateGradient_ForState(TreeCtrl * tree,PerStateInfo * pInfo,int state,int * match)6016 PerStateGradient_ForState(
6017 TreeCtrl *tree,
6018 PerStateInfo *pInfo,
6019 int state,
6020 int *match)
6021 {
6022 PerStateDataGradient *pData;
6023
6024 pData = (PerStateDataGradient *) PerStateInfo_ForState(tree, &pstGradient, pInfo, state, match);
6025 if (pData != NULL)
6026 return pData->gradient;
6027 return NULL;
6028 }
6029
6030 /*****/
6031
6032 /*
6033 *----------------------------------------------------------------------
6034 *
6035 * Tree_AllocColorFromObj --
6036 *
6037 * Allocate a TreeColor structure based on the given object.
6038 *
6039 * Results:
6040 * A new TreeColor struct or NULL if an error occurred.
6041 *
6042 * Side effects:
6043 * Memory may be allocated. If the color refers to a gradient
6044 * its refCount is incremented.
6045 *
6046 *----------------------------------------------------------------------
6047 */
6048
6049 TreeColor *
Tree_AllocColorFromObj(TreeCtrl * tree,Tcl_Obj * obj)6050 Tree_AllocColorFromObj(
6051 TreeCtrl *tree, /* Widget info. */
6052 Tcl_Obj *obj /* Gradient name or Tk color specification */
6053 )
6054 {
6055 TreeGradient gradient = NULL;
6056 XColor *color = NULL;
6057 TreeColor *tc;
6058
6059 if (TreeGradient_FromObj(tree, obj, &gradient) == TCL_OK) {
6060 gradient->refCount++;
6061 } else {
6062 Tcl_ResetResult(tree->interp);
6063 color = Tk_AllocColorFromObj(tree->interp, tree->tkwin, obj);
6064 if (color == NULL) {
6065 FormatResult(tree->interp, "unknown color or gradient name \"%s\"",
6066 Tcl_GetString(obj));
6067 return NULL;
6068 }
6069 }
6070
6071 tc = (TreeColor *) ckalloc(sizeof(TreeColor));
6072 tc->color = color;
6073 tc->gradient = gradient;
6074
6075 return tc;
6076 }
6077
6078 /*
6079 *----------------------------------------------------------------------
6080 *
6081 * Tree_FreeColor --
6082 *
6083 * Free a TreeColor.
6084 *
6085 * Results:
6086 * None.
6087 *
6088 * Side effects:
6089 * Memory may be freed. If the color refers to a gradient
6090 * its refCount is decremented.
6091 *
6092 *----------------------------------------------------------------------
6093 */
6094
6095 void
Tree_FreeColor(TreeCtrl * tree,TreeColor * tc)6096 Tree_FreeColor(
6097 TreeCtrl *tree, /* Widget info. */
6098 TreeColor *tc /* Color to free, may be NULL */
6099 )
6100 {
6101 if (tc != NULL) {
6102 if (tc->color)
6103 Tk_FreeColor(tc->color);
6104 if (tc->gradient)
6105 TreeGradient_Release(tree, tc->gradient);
6106 WFREE(tc, TreeColor);
6107 }
6108 }
6109
6110 /*
6111 *----------------------------------------------------------------------
6112 *
6113 * TreeColor_ToObj --
6114 *
6115 * Returns the Tcl_Obj representation of a TreeColor.
6116 *
6117 * Results:
6118 * The returned representation is bogus, ensure any TreeColor
6119 * options store the object representation.
6120 *
6121 * Side effects:
6122 * Creates a new Tcl_Obj.
6123 *
6124 *----------------------------------------------------------------------
6125 */
6126
6127 Tcl_Obj *
TreeColor_ToObj(TreeCtrl * tree,TreeColor * tc)6128 TreeColor_ToObj(
6129 TreeCtrl *tree, /* Widget info. */
6130 TreeColor *tc /* Color to get Tcl_Obj rep of */
6131 )
6132 {
6133 /* FIXME */
6134 return Tcl_NewStringObj("insert tree color rep here", -1);
6135 }
6136
6137 /*
6138 *----------------------------------------------------------------------
6139 *
6140 * TreeColor_IsOpaque --
6141 *
6142 * Test whether a tree color would be drawn fully opaque or not.
6143 *
6144 * Results:
6145 * 1 if opaque, 0 otherwise.
6146 *
6147 * Side effects:
6148 * None.
6149 *
6150 *----------------------------------------------------------------------
6151 */
6152
6153 int
TreeColor_IsOpaque(TreeCtrl * tree,TreeColor * tc)6154 TreeColor_IsOpaque(
6155 TreeCtrl *tree, /* Widget info. */
6156 TreeColor *tc /* Color info. */
6157 )
6158 {
6159 if (tc == NULL)
6160 return 0;
6161 if (tc->gradient != NULL)
6162 return TreeGradient_IsOpaque(tree, tc->gradient);
6163 return tc->color != NULL;
6164 }
6165
6166 /*
6167 *----------------------------------------------------------------------
6168 *
6169 * TreeColorCO_Set --
6170 * TreeColorCO_Get --
6171 * TreeColorCO_Restore --
6172 *
6173 * These procedures implement a TK_OPTION_CUSTOM where the custom
6174 * option is a TreeColor.
6175 *
6176 * Results:
6177 * None.
6178 *
6179 * Side effects:
6180 * None.
6181 *
6182 *----------------------------------------------------------------------
6183 */
6184
6185 static int
TreeColorCO_Set(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** valuePtr,char * recordPtr,int internalOffset,char * saveInternalPtr,int flags)6186 TreeColorCO_Set(
6187 ClientData clientData,
6188 Tcl_Interp *interp,
6189 Tk_Window tkwin,
6190 Tcl_Obj **valuePtr,
6191 char *recordPtr,
6192 int internalOffset,
6193 char *saveInternalPtr,
6194 int flags
6195 )
6196 {
6197 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
6198 int objEmpty;
6199 TreeColor **internalPtr, *new;
6200
6201 if (internalOffset >= 0)
6202 internalPtr = (TreeColor **) (recordPtr + internalOffset);
6203 else
6204 internalPtr = NULL;
6205
6206 objEmpty = ObjectIsEmpty((*valuePtr));
6207
6208 if ((flags & TK_OPTION_NULL_OK) && objEmpty) {
6209 (*valuePtr) = NULL;
6210 new = 0;
6211 } else {
6212 new = Tree_AllocColorFromObj(tree, *valuePtr);
6213 if (new == NULL)
6214 return TCL_ERROR;
6215 }
6216
6217 if (internalPtr != NULL) {
6218 *((TreeColor **) saveInternalPtr) = *internalPtr;
6219 *internalPtr = new;
6220 }
6221
6222 return TCL_OK;
6223 }
6224
6225 static Tcl_Obj *
TreeColorCO_Get(ClientData clientData,Tk_Window tkwin,char * recordPtr,int internalOffset)6226 TreeColorCO_Get(
6227 ClientData clientData,
6228 Tk_Window tkwin,
6229 char *recordPtr,
6230 int internalOffset
6231 )
6232 {
6233 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
6234 TreeColor **internalPtr = (TreeColor **) (recordPtr + internalOffset);
6235 return TreeColor_ToObj(tree, *internalPtr);
6236 }
6237
6238 static void
TreeColorCO_Restore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * saveInternalPtr)6239 TreeColorCO_Restore(
6240 ClientData clientData,
6241 Tk_Window tkwin,
6242 char *internalPtr,
6243 char *saveInternalPtr
6244 )
6245 {
6246 *(TreeColor **) internalPtr = *(TreeColor **) saveInternalPtr;
6247 }
6248
6249 static void
TreeColorCO_Free(ClientData clientData,Tk_Window tkwin,char * internalPtr)6250 TreeColorCO_Free(
6251 ClientData clientData,
6252 Tk_Window tkwin,
6253 char *internalPtr
6254 )
6255 {
6256 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
6257 TreeColor *value = *((TreeColor **) internalPtr);
6258 if (value != NULL) {
6259 Tree_FreeColor(tree, value);
6260 *((TreeColor **) internalPtr) = NULL;
6261 }
6262 }
6263
6264 Tk_ObjCustomOption TreeCtrlCO_treecolor =
6265 {
6266 "tree color",
6267 TreeColorCO_Set,
6268 TreeColorCO_Get,
6269 TreeColorCO_Restore,
6270 TreeColorCO_Free,
6271 (ClientData) NULL
6272 };
6273
6274 /*****/
6275
6276 /* *** Borrowed some gradient code from TkPath *** */
6277
6278 static GradientStop *
NewGradientStop(double offset,XColor * color,double opacity)6279 NewGradientStop(
6280 double offset,
6281 XColor *color,
6282 double opacity
6283 )
6284 {
6285 GradientStop *stopPtr;
6286
6287 stopPtr = (GradientStop *) ckalloc(sizeof(GradientStop));
6288 memset(stopPtr, '\0', sizeof(GradientStop));
6289 stopPtr->offset = offset;
6290 stopPtr->color = color;
6291 stopPtr->opacity = opacity;
6292 return stopPtr;
6293 }
6294
6295 static GradientStopArray *
NewGradientStopArray(int nstops)6296 NewGradientStopArray(
6297 int nstops
6298 )
6299 {
6300 GradientStopArray *stopArrPtr;
6301 GradientStop **stops;
6302
6303 stopArrPtr = (GradientStopArray *) ckalloc(sizeof(GradientStopArray));
6304 memset(stopArrPtr, '\0', sizeof(GradientStopArray));
6305
6306 /* Array of *pointers* to GradientStop. */
6307 stops = (GradientStop **) ckalloc(nstops*sizeof(GradientStop *));
6308 memset(stops, '\0', nstops*sizeof(GradientStop *));
6309 stopArrPtr->nstops = nstops;
6310 stopArrPtr->stops = stops;
6311 return stopArrPtr;
6312 }
6313
6314 static void
FreeAllStops(GradientStop ** stops,int nstops)6315 FreeAllStops(
6316 GradientStop **stops,
6317 int nstops
6318 )
6319 {
6320 int i;
6321 for (i = 0; i < nstops; i++) {
6322 if (stops[i] != NULL) {
6323 Tk_FreeColor(stops[i]->color);
6324 WFREE(stops[i], GradientStop);
6325 }
6326 }
6327 WCFREE(stops, GradientStop*, nstops);
6328 }
6329
6330 static void
FreeStopArray(GradientStopArray * stopArrPtr)6331 FreeStopArray(
6332 GradientStopArray *stopArrPtr
6333 )
6334 {
6335 if (stopArrPtr != NULL) {
6336 FreeAllStops(stopArrPtr->stops, stopArrPtr->nstops);
6337 WFREE(stopArrPtr, GradientStopArray);
6338 }
6339 }
6340
6341 /*
6342 * The stops are a list of stop lists where each stop list is:
6343 * {offset color ?opacity?}
6344 */
6345 static int
StopsSet(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * oldInternalPtr,int flags)6346 StopsSet(
6347 ClientData clientData,
6348 Tcl_Interp *interp, /* Current interp; may be used for errors. */
6349 Tk_Window tkwin, /* Window for which option is being set. */
6350 Tcl_Obj **value, /* Pointer to the pointer to the value object.
6351 * We use a pointer to the pointer because
6352 * we may need to return a value (NULL). */
6353 char *recordPtr, /* Pointer to storage for the widget record. */
6354 int internalOffset, /* Offset within *recordPtr at which the
6355 internal value is to be stored. */
6356 char *oldInternalPtr, /* Pointer to storage for the old value. */
6357 int flags /* Flags for the option, set Tk_SetOptions. */
6358 )
6359 {
6360 char *internalPtr;
6361 int i, nstops, stopLen;
6362 int objEmpty = 0;
6363 Tcl_Obj *valuePtr;
6364 double offset, lastOffset, opacity;
6365 Tcl_Obj **objv;
6366 Tcl_Obj *stopObj;
6367 Tcl_Obj *obj;
6368 XColor *color;
6369 GradientStopArray *new = NULL;
6370
6371 valuePtr = *value;
6372 if (internalOffset >= 0)
6373 internalPtr = recordPtr + internalOffset;
6374 else
6375 internalPtr = NULL;
6376 objEmpty = ObjectIsEmpty(valuePtr);
6377
6378 if ((flags & TK_OPTION_NULL_OK) && objEmpty) {
6379 valuePtr = NULL;
6380 } else {
6381
6382 /* Deal with each stop list in turn. */
6383 if (Tcl_ListObjGetElements(interp, valuePtr, &nstops, &objv) != TCL_OK) {
6384 return TCL_ERROR;
6385 }
6386 if (nstops < 2) {
6387 FormatResult(interp, "at least 2 stops required, %d given", nstops);
6388 return TCL_ERROR;
6389 }
6390 new = NewGradientStopArray(nstops);
6391 lastOffset = 0.0;
6392
6393 for (i = 0; i < nstops; i++) {
6394 stopObj = objv[i];
6395 if (Tcl_ListObjLength(interp, stopObj, &stopLen) != TCL_OK) {
6396 goto error;
6397 }
6398 if ((stopLen < 2) || (stopLen > 3)) {
6399 Tcl_SetObjResult(interp, Tcl_NewStringObj(
6400 "stop list not {offset color ?opacity?}", -1));
6401 goto error;
6402 }
6403 Tcl_ListObjIndex(interp, stopObj, 0, &obj);
6404 if (Tcl_GetDoubleFromObj(interp, obj, &offset) != TCL_OK) {
6405 goto error;
6406 }
6407 if ((offset < 0.0) || (offset > 1.0)) {
6408 Tcl_SetObjResult(interp, Tcl_NewStringObj(
6409 "stop offsets must be in the range 0.0 to 1.0", -1));
6410 goto error;
6411 }
6412 if ((i == 0) && (offset != 0.0)) {
6413 FormatResult(interp, "first stop offset must be 0.0, got %.4g", offset);
6414 goto error;
6415 }
6416 if ((i == nstops - 1) && (offset != 1.0)) {
6417 FormatResult(interp, "last stop offset must be 1.0, got %.4g", offset);
6418 goto error;
6419 }
6420 if (offset < lastOffset) {
6421 Tcl_SetObjResult(interp, Tcl_NewStringObj(
6422 "stop offsets must be ordered", -1));
6423 goto error;
6424 }
6425 Tcl_ListObjIndex(interp, stopObj, 1, &obj);
6426 color = Tk_AllocColorFromObj(interp, Tk_MainWindow(interp), obj);
6427 if (color == NULL)
6428 goto error;
6429 if (stopLen == 3) {
6430 Tcl_ListObjIndex(interp, stopObj, 2, &obj);
6431 if (Tcl_GetDoubleFromObj(interp, obj, &opacity) != TCL_OK) {
6432 goto error;
6433 }
6434 } else {
6435 opacity = 1.0;
6436 }
6437
6438 /* Make new stop. */
6439 new->stops[i] = NewGradientStop(offset, color, opacity);
6440 lastOffset = offset;
6441 }
6442 }
6443 if (internalPtr != NULL) {
6444 *((GradientStopArray **) oldInternalPtr) = *((GradientStopArray **) internalPtr);
6445 *((GradientStopArray **) internalPtr) = new;
6446 }
6447 return TCL_OK;
6448
6449 error:
6450 if (new != NULL) {
6451 FreeStopArray(new);
6452 }
6453 return TCL_ERROR;
6454 }
6455
6456 static void
StopsRestore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * oldInternalPtr)6457 StopsRestore(
6458 ClientData clientData,
6459 Tk_Window tkwin,
6460 char *internalPtr, /* Pointer to storage for value. */
6461 char *oldInternalPtr /* Pointer to old value. */
6462 )
6463 {
6464 *(GradientStopArray **)internalPtr = *(GradientStopArray **)oldInternalPtr;
6465 }
6466
6467 static void
StopsFree(ClientData clientData,Tk_Window tkwin,char * internalPtr)6468 StopsFree(
6469 ClientData clientData,
6470 Tk_Window tkwin,
6471 char *internalPtr
6472 )
6473 {
6474 if (*((char **) internalPtr) != NULL) {
6475 FreeStopArray(*(GradientStopArray **)internalPtr);
6476 }
6477 }
6478
6479 static Tk_ObjCustomOption stopsCO =
6480 {
6481 "stops",
6482 StopsSet,
6483 NULL,
6484 StopsRestore,
6485 StopsFree,
6486 (ClientData) NULL
6487 };
6488
6489 /*****/
6490
6491 typedef enum {
6492 GCT_AREA = 0,
6493 GCT_CANVAS,
6494 GCT_COLUMN,
6495 GCT_ITEM
6496 } GradientCoordType;
6497
6498 static const char *coordTypeNames[] = {
6499 "area", "canvas", "column", "item", NULL
6500 };
6501
6502 struct GradientCoord {
6503 GradientCoordType type;
6504 float value;
6505 TreeColumn column; /* optional arg to GCT_COLUMN */
6506 TreeItem item; /* optional arg to GCT_ITEM */
6507 int area; /* required arg to GCT_AREA */
6508 };
6509
6510 static int
GradientCoordSet(ClientData clientData,Tcl_Interp * interp,Tk_Window tkwin,Tcl_Obj ** value,char * recordPtr,int internalOffset,char * oldInternalPtr,int flags)6511 GradientCoordSet(
6512 ClientData clientData,
6513 Tcl_Interp *interp, /* Current interp; may be used for errors. */
6514 Tk_Window tkwin, /* Window for which option is being set. */
6515 Tcl_Obj **value, /* Pointer to the pointer to the value object.
6516 * We use a pointer to the pointer because
6517 * we may need to return a value (NULL). */
6518 char *recordPtr, /* Pointer to storage for the widget record. */
6519 int internalOffset, /* Offset within *recordPtr at which the
6520 internal value is to be stored. */
6521 char *oldInternalPtr, /* Pointer to storage for the old value. */
6522 int flags /* Flags for the option, set Tk_SetOptions. */
6523 )
6524 {
6525 TreeCtrl *tree = (TreeCtrl *) ((TkWindow *) tkwin)->instanceData;
6526 char *internalPtr;
6527 int objEmpty = 0;
6528 Tcl_Obj *valuePtr;
6529 Tcl_Obj **objv;
6530 int objc;
6531 double coordValue;
6532 GradientCoordType coordType;
6533 GradientCoord *new = NULL;
6534 TreeColumn column = NULL;
6535 TreeItem item = NULL;
6536 int area = TREE_AREA_NONE;
6537
6538 valuePtr = *value;
6539 if (internalOffset >= 0)
6540 internalPtr = recordPtr + internalOffset;
6541 else
6542 internalPtr = NULL;
6543 objEmpty = ObjectIsEmpty(valuePtr);
6544
6545 if ((flags & TK_OPTION_NULL_OK) && objEmpty) {
6546 valuePtr = NULL;
6547 } else {
6548 if (Tcl_ListObjGetElements(interp, valuePtr, &objc, &objv) != TCL_OK) {
6549 return TCL_ERROR;
6550 }
6551 if (objc < 2) {
6552 FormatResult(interp, "expected list {offset coordType ?arg ...?}");
6553 return TCL_ERROR;
6554 }
6555 if (Tcl_GetIndexFromObj(interp, objv[1], coordTypeNames,
6556 "coordinate type", 0, (int *) &coordType) != TCL_OK) {
6557 return TCL_ERROR;
6558 }
6559 if (Tcl_GetDoubleFromObj(interp, objv[0], &coordValue) != TCL_OK) {
6560 return TCL_ERROR;
6561 }
6562 if (coordType == GCT_AREA) {
6563 if (objc != 3) {
6564 FormatResult(interp, "wrong # args after \"area\": must be 1");
6565 return TCL_ERROR;
6566 }
6567 if (TreeArea_FromObj(tree, objv[2], &area) != TCL_OK) {
6568 return TCL_ERROR;
6569 }
6570 }
6571 if (coordType == GCT_COLUMN && objc > 2) {
6572 if (objc > 3) {
6573 FormatResult(interp, "wrong # args after \"column\": must be 0 or 1");
6574 return TCL_ERROR;
6575 }
6576 if (TreeColumn_FromObj(tree, objv[2], &column, CFO_NOT_NULL)
6577 != TCL_OK) {
6578 return TCL_ERROR;
6579 }
6580 }
6581 if (coordType == GCT_ITEM && objc > 2) {
6582 if (objc > 3) {
6583 FormatResult(interp, "wrong # args after \"item\": must be 0 or 1");
6584 return TCL_ERROR;
6585 }
6586 if (TreeItem_FromObj(tree, objv[2], &item, IFO_NOT_NULL)
6587 != TCL_OK) {
6588 return TCL_ERROR;
6589 }
6590 }
6591 new = (GradientCoord *) ckalloc(sizeof(GradientCoord));
6592 new->type = coordType;
6593 new->value = (float) coordValue;
6594 new->column = column;
6595 new->item = item;
6596 new->area = area;
6597 }
6598 if (internalPtr != NULL) {
6599 *((GradientCoord **) oldInternalPtr) = *((GradientCoord **) internalPtr);
6600 *((GradientCoord **) internalPtr) = new;
6601 }
6602 return TCL_OK;
6603 }
6604
6605 static void
GradientCoordRestore(ClientData clientData,Tk_Window tkwin,char * internalPtr,char * oldInternalPtr)6606 GradientCoordRestore(
6607 ClientData clientData,
6608 Tk_Window tkwin,
6609 char *internalPtr, /* Pointer to storage for value. */
6610 char *oldInternalPtr) /* Pointer to old value. */
6611 {
6612 *(GradientCoord **)internalPtr = *(GradientCoord **)oldInternalPtr;
6613 }
6614
6615 static void
GradientCoordFree(ClientData clientData,Tk_Window tkwin,char * internalPtr)6616 GradientCoordFree(
6617 ClientData clientData,
6618 Tk_Window tkwin,
6619 char *internalPtr)
6620 {
6621 if (*((char **) internalPtr) != NULL) {
6622 ckfree(*(char **)internalPtr);
6623 }
6624 }
6625
6626 static Tk_ObjCustomOption gradientCoordCO =
6627 {
6628 "coordinate",
6629 GradientCoordSet,
6630 NULL,
6631 GradientCoordRestore,
6632 GradientCoordFree,
6633 (ClientData) NULL
6634 };
6635
6636 /*
6637 *--------------------------------------------------------------
6638 *
6639 * TreeGradient_ColumnDeleted --
6640 *
6641 * Removes any reference to a column from the coordinates of
6642 * all gradients.
6643 *
6644 * Results:
6645 * None.
6646 *
6647 * Side effects:
6648 * None.
6649 *
6650 *--------------------------------------------------------------
6651 */
6652
6653 void
TreeGradient_ColumnDeleted(TreeCtrl * tree,TreeColumn column)6654 TreeGradient_ColumnDeleted(
6655 TreeCtrl *tree, /* Widget info. */
6656 TreeColumn column /* Column about to be deleted. */
6657 )
6658 {
6659 Tcl_HashEntry *hPtr;
6660 Tcl_HashSearch search;
6661 TreeGradient gradient;
6662
6663 hPtr = Tcl_FirstHashEntry(&tree->gradientHash, &search);
6664 while (hPtr != NULL) {
6665 gradient = (TreeGradient) Tcl_GetHashValue(hPtr);
6666
6667 #define CHECK_GCOORD(GCRD,OBJ) \
6668 if ((GCRD != NULL) && (GCRD->column == column)) { \
6669 ckfree((char *) GCRD); \
6670 Tcl_DecrRefCount(OBJ); \
6671 GCRD = NULL; \
6672 OBJ = NULL; \
6673 }
6674 CHECK_GCOORD(gradient->left, gradient->leftObj);
6675 CHECK_GCOORD(gradient->right, gradient->rightObj);
6676 CHECK_GCOORD(gradient->top, gradient->topObj);
6677 CHECK_GCOORD(gradient->bottom, gradient->bottomObj);
6678 #undef CHECK_GCOORD
6679
6680 hPtr = Tcl_NextHashEntry(&search);
6681 }
6682 }
6683
6684 /*
6685 *--------------------------------------------------------------
6686 *
6687 * TreeGradient_ItemDeleted --
6688 *
6689 * Removes any reference to an item from the coordinates of
6690 * all gradients.
6691 *
6692 * Results:
6693 * None.
6694 *
6695 * Side effects:
6696 * None.
6697 *
6698 *--------------------------------------------------------------
6699 */
6700
6701 void
TreeGradient_ItemDeleted(TreeCtrl * tree,TreeItem item)6702 TreeGradient_ItemDeleted(
6703 TreeCtrl *tree, /* Widget info. */
6704 TreeItem item /* Item about to be deleted. */
6705 )
6706 {
6707 Tcl_HashEntry *hPtr;
6708 Tcl_HashSearch search;
6709 TreeGradient gradient;
6710
6711 hPtr = Tcl_FirstHashEntry(&tree->gradientHash, &search);
6712 while (hPtr != NULL) {
6713 gradient = (TreeGradient) Tcl_GetHashValue(hPtr);
6714
6715 #define CHECK_GCOORD(GCRD,OBJ) \
6716 if ((GCRD != NULL) && (GCRD->item == item)) { \
6717 ckfree((char *) GCRD); \
6718 Tcl_DecrRefCount(OBJ); \
6719 GCRD = NULL; \
6720 OBJ = NULL; \
6721 }
6722 CHECK_GCOORD(gradient->left, gradient->leftObj);
6723 CHECK_GCOORD(gradient->right, gradient->rightObj);
6724 CHECK_GCOORD(gradient->top, gradient->topObj);
6725 CHECK_GCOORD(gradient->bottom, gradient->bottomObj);
6726 #undef CHECK_GCOORD
6727
6728 hPtr = Tcl_NextHashEntry(&search);
6729 }
6730 }
6731
6732 static TreeColumn
FindNthVisibleColumn(TreeCtrl * tree,TreeColumn column,int * n)6733 FindNthVisibleColumn(
6734 TreeCtrl *tree,
6735 TreeColumn column,
6736 int *n
6737 )
6738 {
6739 int index = TreeColumn_Index(column);
6740 TreeColumn column2 = column, column3 = column;
6741
6742 if ((*n) > 0) {
6743 while ((*n) > 0 && ++index < tree->columnCount) {
6744 column3 = TreeColumn_Next(column3);
6745 if (TreeColumn_Visible(column3)) {
6746 column2 = column3;
6747 (*n) -= 1;
6748 }
6749 }
6750 } else {
6751 while ((*n) < 0 && --index >= 0) {
6752 column3 = TreeColumn_Prev(column3);
6753 if (TreeColumn_Visible(column3)) {
6754 column2 = column3;
6755 (*n) += 1;
6756 }
6757 }
6758 }
6759 return column2;
6760 }
6761
6762 static int
GetGradientBrushCoordX(TreeCtrl * tree,GradientCoord * gcrd,TreeColumn column,TreeItem item,int * xPtr)6763 GetGradientBrushCoordX(
6764 TreeCtrl *tree, /* Widget Info. */
6765 GradientCoord *gcrd, /* Left or right coordinate. */
6766 TreeColumn column, /* Column or NULL. */
6767 TreeItem item, /* Item or NULL. */
6768 int *xPtr /* Returned canvas coordinate.
6769 * Will remain unchanged if the
6770 * gradient coordinate cannot be
6771 * resolved. */
6772 )
6773 {
6774 if (gcrd == NULL)
6775 return 0;
6776
6777 switch (gcrd->type) {
6778 case GCT_AREA: {
6779 TreeRectangle tr;
6780 if (Tree_AreaBbox(tree, gcrd->area, &tr) == TRUE) {
6781 (*xPtr) = (int) (TreeRect_Left(tr) + TreeRect_Width(tr) * gcrd->value);
6782 (*xPtr) += tree->xOrigin; /* Window -> Canvas */
6783 return 1;
6784 }
6785 break;
6786 }
6787
6788 case GCT_CANVAS: {
6789 int canvasWidth = Tree_FakeCanvasWidth(tree);
6790 (*xPtr) = (int) (canvasWidth * gcrd->value);
6791 return 1;
6792 }
6793
6794 /* FIXME: if item != NULL then make column offset relative to item */
6795 case GCT_COLUMN:
6796 if (gcrd->column != NULL)
6797 column = gcrd->column;
6798 if (column != NULL) {
6799 int x, w;
6800 if (gcrd->value < 0.0) {
6801 int n = (int) ceil(-gcrd->value);
6802 n = -n; /* backwards */
6803 column = FindNthVisibleColumn(tree, column, &n);
6804 if (TreeColumn_Visible(column)) {
6805 double tmp, frac;
6806 if ((n < 0) || (frac = modf(-gcrd->value, &tmp)) == 0.0)
6807 frac = 1.0;
6808 x = TreeColumn_Offset(column);
6809 w = TreeColumn_UseWidth(column);
6810 (*xPtr) = (int) (x + w * (1.0 - frac));
6811 return 1;
6812 }
6813 } else if (gcrd->value > 1.0) {
6814 int n = (int) ceil(gcrd->value - 1.0);
6815 column = FindNthVisibleColumn(tree, column, &n);
6816 if (TreeColumn_Visible(column)) {
6817 double tmp, frac;
6818 if ((n > 0) || (frac = modf(gcrd->value, &tmp)) == 0.0)
6819 frac = 1.0;
6820 x = TreeColumn_Offset(column);
6821 w = TreeColumn_UseWidth(column);
6822 (*xPtr) = (int) (x + w * frac);
6823 return 1;
6824 }
6825 } else {
6826 if (TreeColumn_Visible(column)) {
6827 x = TreeColumn_Offset(column);
6828 w = TreeColumn_UseWidth(column);
6829 (*xPtr) = (int) (x + w * gcrd->value);
6830 return 1;
6831 }
6832 }
6833 }
6834 break;
6835
6836 case GCT_ITEM:
6837 if (gcrd->item != NULL)
6838 item = gcrd->item;
6839 if (item != NULL) {
6840 TreeRectangle tr;
6841 int lock;
6842 if (tree->columnCountVis > 0)
6843 lock = COLUMN_LOCK_NONE;
6844 else if (tree->columnCountVisLeft > 0)
6845 lock = COLUMN_LOCK_LEFT;
6846 else if (tree->columnCountVisRight > 0)
6847 lock = COLUMN_LOCK_RIGHT;
6848 else
6849 break; /* not possible else wouldn't be drawing */
6850 if (gcrd->value < 0.0) {
6851 int clip = 0, row, col, row2, col2;
6852 if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK) {
6853 int n = (int) ceil(-gcrd->value);
6854 TreeItem item2 = Tree_RNCToItem(tree, row, col - n);
6855 (void) Tree_ItemToRNC(tree, item2, &row2, &col2);
6856 if (row2 == row)
6857 item = item2; /* there is an item to the left */
6858 if (row2 != row || col2 != col - n)
6859 clip = 1; /* no item 'n' columns to the left */
6860 }
6861 if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6862 double tmp, frac;
6863 if (clip || (frac = modf(-gcrd->value, &tmp)) == 0.0)
6864 frac = 1.0;
6865 (*xPtr) = (int) (tr.x + tr.width * (1.0 - frac));
6866 return 1;
6867 }
6868 } else if (gcrd->value > 1.0) {
6869 int clip = 0, row, col, row2, col2;
6870 if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK) {
6871 int n = (int) ceil(gcrd->value - 1.0);
6872 TreeItem item2 = Tree_RNCToItem(tree, row, col + n);
6873 (void) Tree_ItemToRNC(tree, item2, &row2, &col2);
6874 if (row2 == row)
6875 item = item2; /* there is an item to the right */
6876 if (row2 != row || col2 != col + n)
6877 clip = 1; /* no item 'n' columns to the right */
6878 }
6879 if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6880 double tmp, frac;
6881 if (clip || (frac = modf(gcrd->value, &tmp)) == 0.0)
6882 frac = 1.0;
6883 (*xPtr) = (int) (tr.x + tr.width * frac);
6884 return 1;
6885 }
6886 } else if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6887 (*xPtr) = (int) (tr.x + tr.width * gcrd->value);
6888 return 1;
6889 }
6890 }
6891 break;
6892 }
6893 return 0;
6894 }
6895
6896 static int
GetGradientBrushCoordY(TreeCtrl * tree,GradientCoord * gcrd,TreeColumn column,TreeItem item,int * yPtr)6897 GetGradientBrushCoordY(
6898 TreeCtrl *tree, /* Widget Info. */
6899 GradientCoord *gcrd, /* Top or bottom coordinate. */
6900 TreeColumn column, /* Column or NULL. */
6901 TreeItem item, /* Item or NULL. */
6902 int *yPtr /* Returned canvas coordinate.
6903 * Will remain unchanged if the
6904 * gradient coordinate cannot be
6905 * resolved. */
6906 )
6907 {
6908 if (gcrd == NULL)
6909 return 0;
6910
6911 switch (gcrd->type) {
6912 case GCT_AREA: {
6913 TreeRectangle tr;
6914 if (Tree_AreaBbox(tree, gcrd->area, &tr) == TRUE) {
6915 (*yPtr) = (int) (TreeRect_Top(tr) + TreeRect_Height(tr) * gcrd->value);
6916 (*yPtr) += tree->yOrigin; /* Window -> Canvas */
6917 return 1;
6918 }
6919 break;
6920 }
6921
6922 case GCT_CANVAS: {
6923 int canvasHeight = Tree_FakeCanvasHeight(tree);
6924 (*yPtr) = (int) (canvasHeight * gcrd->value);
6925 return 1;
6926 }
6927
6928 case GCT_COLUMN:
6929 /* Can't think of any use for this */
6930 break;
6931
6932 case GCT_ITEM:
6933 if (gcrd->item != NULL)
6934 item = gcrd->item;
6935 if (item != NULL) {
6936 TreeRectangle tr;
6937 int lock;
6938 if (tree->columnCountVis > 0)
6939 lock = COLUMN_LOCK_NONE;
6940 else if (tree->columnCountVisLeft > 0)
6941 lock = COLUMN_LOCK_LEFT;
6942 else if (tree->columnCountVisRight > 0)
6943 lock = COLUMN_LOCK_RIGHT;
6944 else
6945 break; /* not possible else wouldn't be drawing */
6946 if (gcrd->value < 0.0) {
6947 int clip = 0, row, col, row2, col2;
6948 if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK) {
6949 int n = (int) ceil(-gcrd->value);
6950 TreeItem item2 = Tree_RNCToItem(tree, row - n, col);
6951 (void) Tree_ItemToRNC(tree, item2, &row2, &col2);
6952 if (col2 == col)
6953 item = item2; /* there is an item above */
6954 if (row2 != row - n || col2 != col)
6955 clip = 1; /* no item 'n' rows above */
6956 }
6957 if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6958 double tmp, frac;
6959 if (clip || (frac = modf(-gcrd->value, &tmp)) == 0.0)
6960 frac = 1.0;
6961 (*yPtr) = (int) (tr.y + tr.height * (1.0 - frac));
6962 return 1;
6963 }
6964 } else if (gcrd->value > 1.0) {
6965 int clip = 0, row, col, row2, col2;
6966 if (Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK) {
6967 int n = (int) ceil(gcrd->value - 1.0);
6968 TreeItem item2 = Tree_RNCToItem(tree, row + n, col);
6969 (void) Tree_ItemToRNC(tree, item2, &row2, &col2);
6970 if (col2 == col)
6971 item = item2; /* there is an item below */
6972 if (row2 != row + n || col2 != col)
6973 clip = 1; /* no item 'n' rows below */
6974 }
6975 if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6976 double tmp, frac;
6977 if (clip || (frac = modf(gcrd->value, &tmp)) == 0.0)
6978 frac = 1.0;
6979 (*yPtr) = (int) (tr.y + tr.height * frac);
6980 return 1;
6981 }
6982 } else if (Tree_ItemBbox(tree, item, lock, &tr) != -1) {
6983 (*yPtr) = (int) (tr.y + tr.height * gcrd->value);
6984 return 1;
6985 }
6986 }
6987 break;
6988 }
6989 return 0;
6990 }
6991
6992 /*
6993 *--------------------------------------------------------------
6994 *
6995 * TreeGradient_GetBrushBounds --
6996 *
6997 * Returns the bounds of the brush that should be used to
6998 * draw a gradient.
6999 *
7000 * Results:
7001 * Return 1 if the brush size is non-empty, 0 otherwise.
7002 *
7003 * Side effects:
7004 * May recalculate item/column layout info if it is out-of-date.
7005 *
7006 *--------------------------------------------------------------
7007 */
7008
7009 int
TreeGradient_GetBrushBounds(TreeCtrl * tree,TreeGradient gradient,const TreeRectangle * trPaint,TreeRectangle * trBrush,TreeColumn column,TreeItem item)7010 TreeGradient_GetBrushBounds(
7011 TreeCtrl *tree, /* Widget Info. */
7012 TreeGradient gradient, /* Gradient token. */
7013 const TreeRectangle *trPaint, /* Area to paint, canvas coords. */
7014 TreeRectangle *trBrush, /* Returned brush bounds. */
7015 TreeColumn column, /* Column or NULL. */
7016 TreeItem item /* Item or NULL. */
7017 )
7018 {
7019 int x1, y1, x2, y2;
7020
7021 x1 = trPaint->x;
7022 y1 = trPaint->y;
7023 x2 = trPaint->x + trPaint->width;
7024 y2 = trPaint->y + trPaint->height;
7025
7026 /* FIXME: If an item's or column's visibility changes, may need to redraw */
7027
7028 (void) GetGradientBrushCoordX(tree, gradient->left, column, item, &x1);
7029 (void) GetGradientBrushCoordX(tree, gradient->right, column, item, &x2);
7030 (void) GetGradientBrushCoordY(tree, gradient->top, column, item, &y1);
7031 (void) GetGradientBrushCoordY(tree, gradient->bottom, column, item, &y2);
7032
7033 trBrush->x = x1;
7034 trBrush->y = y1;
7035 trBrush->width = x2 - x1;
7036 trBrush->height = y2 - y1;
7037
7038 return trBrush->width > 0 && trBrush->height > 0;
7039 }
7040
7041 /*
7042 *--------------------------------------------------------------
7043 *
7044 * TreeGradient_IsRelativeToCanvas --
7045 *
7046 * Returns true if the gradient brush is relative to the
7047 * canvas along the horizontal and vertical axes.
7048 *
7049 * Results:
7050 * None.
7051 *
7052 * Side effects:
7053 * None.
7054 *
7055 *--------------------------------------------------------------
7056 */
7057
7058 void
TreeGradient_IsRelativeToCanvas(TreeGradient gradient,int * relX,int * relY)7059 TreeGradient_IsRelativeToCanvas(
7060 TreeGradient gradient,
7061 int *relX,
7062 int *relY
7063 )
7064 {
7065 (*relX) = (*relY) = 1;
7066
7067 if (gradient->vertical == 0 &&
7068 ((gradient->left != NULL &&
7069 gradient->left->type == GCT_AREA) ||
7070 (gradient->right != NULL &&
7071 gradient->right->type == GCT_AREA)))
7072 (*relX) = 0;
7073
7074 if (gradient->vertical==1 &&
7075 ((gradient->top != NULL &&
7076 gradient->top->type == GCT_AREA) ||
7077 (gradient->bottom != NULL &&
7078 gradient->bottom->type == GCT_AREA)))
7079 (*relY) = 0;
7080 }
7081
7082 /*
7083 *--------------------------------------------------------------
7084 *
7085 * TreeColor_GetBrushBounds --
7086 *
7087 * Determines the bounds of the gradient brush.
7088 *
7089 * Results:
7090 * None.
7091 *
7092 * Side effects:
7093 * May mark an item as needing to be redrawn when scrolling
7094 * occurs if the gradient brush isn't relative to the canvas.
7095 *
7096 *--------------------------------------------------------------
7097 */
7098
7099 void
TreeColor_GetBrushBounds(TreeCtrl * tree,TreeColor * tc,TreeRectangle trPaint,int xOrigin,int yOrigin,TreeColumn column,TreeItem item,TreeRectangle * trBrush)7100 TreeColor_GetBrushBounds(
7101 TreeCtrl *tree, /* Widget info. */
7102 TreeColor *tc, /* Color or gradient. */
7103 TreeRectangle trPaint, /* Area to be painted. */
7104 int xOrigin, /* Offset of trPaint from canvas. */
7105 int yOrigin,
7106 TreeColumn column, /* Column trPaint is in, or NULL. */
7107 TreeItem item, /* Item trPaint is in, or NULL. */
7108 TreeRectangle *trBrush /* Returned brush bounds. */
7109 )
7110 {
7111 int relX, relY;
7112
7113 if (tc->gradient != NULL) {
7114
7115 trPaint.x += xOrigin;
7116 trPaint.y += yOrigin;
7117 (void) TreeGradient_GetBrushBounds(tree, tc->gradient, &trPaint,
7118 trBrush, column, item);
7119 trBrush->x -= xOrigin;
7120 trBrush->y -= yOrigin;
7121
7122 if (item != NULL) {
7123 TreeGradient_IsRelativeToCanvas(tc->gradient, &relX, &relY);
7124 if (relX == 0)
7125 Tree_InvalidateItemOnScrollX(tree, item);
7126 if (relY == 0)
7127 Tree_InvalidateItemOnScrollY(tree, item);
7128 }
7129 } else {
7130 *trBrush = trPaint;
7131 }
7132 }
7133
7134 /*****/
7135
7136 #define GRAD_CONF_STOPS 0x0001
7137 #define GRAD_CONF_STEPS 0x0002
7138
7139 static char *orientStringTable[] = { "horizontal", "vertical", (char *) NULL };
7140
7141 static Tk_OptionSpec gradientOptionSpecs[] = {
7142 {TK_OPTION_CUSTOM, "-bottom", NULL, NULL, NULL,
7143 Tk_Offset(TreeGradient_, bottomObj),
7144 Tk_Offset(TreeGradient_, bottom),
7145 TK_OPTION_NULL_OK, (ClientData) &gradientCoordCO, 0},
7146 {TK_OPTION_CUSTOM, "-left", NULL, NULL, NULL,
7147 Tk_Offset(TreeGradient_, leftObj),
7148 Tk_Offset(TreeGradient_, left),
7149 TK_OPTION_NULL_OK, (ClientData) &gradientCoordCO, 0},
7150 {TK_OPTION_CUSTOM, "-right", NULL, NULL, NULL,
7151 Tk_Offset(TreeGradient_, rightObj),
7152 Tk_Offset(TreeGradient_, right),
7153 TK_OPTION_NULL_OK, (ClientData) &gradientCoordCO, 0},
7154 {TK_OPTION_CUSTOM, "-top", NULL, NULL, NULL,
7155 Tk_Offset(TreeGradient_, topObj),
7156 Tk_Offset(TreeGradient_, top),
7157 TK_OPTION_NULL_OK, (ClientData) &gradientCoordCO, 0},
7158 {TK_OPTION_STRING_TABLE, "-orient", (char *) NULL, (char *) NULL,
7159 "horizontal", -1, Tk_Offset(TreeGradient_, vertical),
7160 0, (ClientData) orientStringTable, 0},
7161 {TK_OPTION_INT, "-steps", (char *) NULL, (char *) NULL,
7162 "1", -1, Tk_Offset(TreeGradient_, steps),
7163 0, (ClientData) NULL, GRAD_CONF_STEPS},
7164 {TK_OPTION_CUSTOM, "-stops", NULL, NULL, NULL,
7165 Tk_Offset(TreeGradient_, stopsObj),
7166 Tk_Offset(TreeGradient_, stopArrPtr),
7167 TK_OPTION_NULL_OK, (ClientData) &stopsCO, GRAD_CONF_STOPS},
7168 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
7169 (char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
7170 };
7171
7172 /*
7173 *----------------------------------------------------------------------
7174 *
7175 * TreeGradient_FromObj --
7176 *
7177 * Convert a Tcl_Obj to a TreeGradient.
7178 *
7179 * Results:
7180 * A standard Tcl result.
7181 *
7182 * Side effects:
7183 * None.
7184 *
7185 *----------------------------------------------------------------------
7186 */
7187
7188 int
TreeGradient_FromObj(TreeCtrl * tree,Tcl_Obj * obj,TreeGradient * gradientPtr)7189 TreeGradient_FromObj(
7190 TreeCtrl *tree, /* Widget info. */
7191 Tcl_Obj *obj, /* Object to convert from. */
7192 TreeGradient *gradientPtr) /* Returned gradient token. */
7193 {
7194 char *name;
7195 Tcl_HashEntry *hPtr;
7196
7197 name = Tcl_GetString(obj);
7198 hPtr = Tcl_FindHashEntry(&tree->gradientHash, name);
7199 if (hPtr != NULL) {
7200 (*gradientPtr) = (TreeGradient) Tcl_GetHashValue(hPtr);
7201 }
7202 if ((hPtr == NULL) || ((*gradientPtr)->deletePending != 0)) {
7203 Tcl_AppendResult(tree->interp, "gradient \"", name, "\" doesn't exist",
7204 NULL);
7205 return TCL_ERROR;
7206 }
7207 return TCL_OK;
7208 }
7209
7210 /*
7211 *----------------------------------------------------------------------
7212 *
7213 * Gradient_ToObj --
7214 *
7215 * Create a new Tcl_Obj representing a gradient.
7216 *
7217 * Results:
7218 * A Tcl_Obj.
7219 *
7220 * Side effects:
7221 * Memory is allocated.
7222 *
7223 *----------------------------------------------------------------------
7224 */
7225
7226 static Tcl_Obj *
Gradient_ToObj(TreeGradient gradient)7227 Gradient_ToObj(
7228 TreeGradient gradient /* Gradient to create Tcl_Obj from. */
7229 )
7230 {
7231 return Tcl_NewStringObj(gradient->name, -1);
7232 }
7233
7234 /*
7235 *----------------------------------------------------------------------
7236 *
7237 * CalcStepColors --
7238 *
7239 * Calculates the colors between (and including) 2 color stops.
7240 * These colors are only used when native gradients aren't
7241 * supported.
7242 *
7243 * Results:
7244 * 'stepColors' is filled in with exactly 'step's XColors.
7245 *
7246 * Side effects:
7247 * Colors are allocated.
7248 *
7249 *----------------------------------------------------------------------
7250 */
7251
7252 static void
CalcStepColors(TreeCtrl * tree,GradientStop * stop1,GradientStop * stop2,XColor * stepColors[],int steps)7253 CalcStepColors(
7254 TreeCtrl *tree, /* Widget info. */
7255 GradientStop *stop1, /* The first color stop. */
7256 GradientStop *stop2, /* The second color stop. */
7257 XColor *stepColors[], /* Returned colors. */
7258 int steps /* Number of colors to allocate. */
7259 )
7260 {
7261 int i;
7262 XColor *c1 = stop1->color, *c2 = stop2->color;
7263
7264 if (steps == 1) {
7265 stepColors[0] = Tk_GetColorByValue(tree->tkwin,
7266 (stop1->offset > 0) ? stop2->color : stop1->color);
7267 return;
7268 }
7269
7270 #undef CLAMP
7271 #define CLAMP(a,b,c) (((a)<(b))?(b):(((a)>(c))?(c):(a)))
7272
7273 for (i = 1; i <= steps; i++) {
7274 XColor pref;
7275 int range, increment;
7276 double factor = (i - 1) * 1.0f / (steps - 1);
7277
7278 range = (c2->red - c1->red);
7279 increment = (int)(range * factor);
7280 pref.red = CLAMP(c1->red + increment, 0, USHRT_MAX);
7281
7282 range = (c2->green - c1->green);
7283 increment = (int)(range * factor);
7284 pref.green = CLAMP(c1->green + increment, 0, USHRT_MAX);
7285
7286 range = (c2->blue - c1->blue);
7287 increment = (int)(range * factor);
7288 pref.blue = CLAMP(c1->blue + increment, 0, USHRT_MAX);
7289
7290 stepColors[i - 1] = Tk_GetColorByValue(tree->tkwin, &pref);
7291 }
7292 #undef CLAMP
7293 }
7294
7295 /*
7296 *----------------------------------------------------------------------
7297 *
7298 * Gradient_CalcStepColors --
7299 *
7300 * Calculates the entire list of step colors.
7301 * These colors are only used when native gradients aren't
7302 * supported.
7303 *
7304 * Results:
7305 * The TreeGradient.stepColors array is written with exactly
7306 * #steps X #stops XColors.
7307 *
7308 * Side effects:
7309 * Colors are allocated.
7310 *
7311 *----------------------------------------------------------------------
7312 */
7313
7314 #ifdef TREECTRL_DEBUG
sameXColor(XColor * c1,XColor * c2)7315 int sameXColor(XColor *c1, XColor *c2)
7316 {
7317 return c1->red == c2->red && c1->green==c2->green && c1->blue==c2->blue;
7318 }
7319 #endif
7320
7321 static void
Gradient_CalcStepColors(TreeCtrl * tree,TreeGradient gradient)7322 Gradient_CalcStepColors(
7323 TreeCtrl *tree, /* Widget info. */
7324 TreeGradient gradient /* Gradient token. */
7325 )
7326 {
7327 int i, step1, step2;
7328
7329 for (i = 0; i < gradient->stopArrPtr->nstops - 1; i++) {
7330 GradientStop *stop1 = gradient->stopArrPtr->stops[i];
7331 GradientStop *stop2 = gradient->stopArrPtr->stops[i+1];
7332 step1 = (int)floor(stop1->offset * (gradient->nStepColors));
7333 step2 = (int)floor(stop2->offset * (gradient->nStepColors))-1;
7334 /*dbwin("CalcStepColors %g -> %g steps=%d-%d\n", stop1->offset,stop2->offset,step1,step2);*/
7335 CalcStepColors(tree, stop1, stop2, gradient->stepColors + step1,
7336 step2 - step1 + 1);
7337 }
7338 #ifdef TREECTRL_DEBUG
7339 if (!sameXColor(gradient->stepColors[0], gradient->stopArrPtr->stops[0]->color))
7340 dbwin("stepColors[0] not same");
7341 if (!sameXColor(gradient->stepColors[gradient->nStepColors - 1], gradient->stopArrPtr->stops[gradient->stopArrPtr->nstops - 1]->color))
7342 dbwin("stepColors[end-1] not same");
7343 #endif
7344 }
7345
7346 /*
7347 *----------------------------------------------------------------------
7348 *
7349 * Gradient_Config --
7350 *
7351 * This procedure is called to process an objc/objv list to set
7352 * configuration options for a TreeGradient.
7353 *
7354 * Results:
7355 * The return value is a standard Tcl result. If TCL_ERROR is
7356 * returned, then an error message is left in interp's result.
7357 *
7358 * Side effects:
7359 * Configuration information, such as text string, colors, font,
7360 * etc. get set for gradient; old resources get freed, if there
7361 * were any. Display changes may occur.
7362 *
7363 *----------------------------------------------------------------------
7364 */
7365
7366 static int
Gradient_Config(TreeCtrl * tree,TreeGradient gradient,int objc,Tcl_Obj * CONST objv[],int createFlag)7367 Gradient_Config(
7368 TreeCtrl *tree, /* Widget info. */
7369 TreeGradient gradient, /* Gradient token. */
7370 int objc, /* Number of arguments. */
7371 Tcl_Obj *CONST objv[], /* Argument values. */
7372 int createFlag /* TRUE if the gradient is being created. */
7373 )
7374 {
7375 TreeGradient_ saved;
7376 Tk_SavedOptions savedOptions;
7377 int error;
7378 Tcl_Obj *errorResult = NULL;
7379 int mask, maskFree = 0;
7380
7381 saved.nStepColors = 0;
7382 saved.stepColors = NULL;
7383
7384 for (error = 0; error <= 1; error++) {
7385 if (error == 0) {
7386 if (Tk_SetOptions(tree->interp, (char *) gradient,
7387 tree->gradientOptionTable,
7388 objc, objv, tree->tkwin,
7389 &savedOptions, &mask) != TCL_OK) {
7390 mask = 0;
7391 continue;
7392 }
7393
7394 /* Wouldn't have to do this if Tk_InitOptions() would return
7395 * a mask of configured options like Tk_SetOptions() does. */
7396 if (createFlag) {
7397 mask |= GRAD_CONF_STOPS | GRAD_CONF_STEPS;
7398 }
7399
7400 /*
7401 * Step 1: Save old values
7402 */
7403
7404 if (mask & (GRAD_CONF_STOPS | GRAD_CONF_STEPS)) {
7405 saved.nStepColors = gradient->nStepColors;
7406 saved.stepColors = gradient->stepColors;
7407 }
7408
7409 /*
7410 * Step 2: Process new values
7411 */
7412
7413 if (mask & (GRAD_CONF_STOPS | GRAD_CONF_STEPS)) {
7414 if (gradient->steps < 1 || gradient->steps > 25) {
7415 FormatResult(tree->interp, "steps must be >= 1 and <= 25");
7416 continue;
7417 }
7418 if ((gradient->stopArrPtr != NULL) &&
7419 (gradient->stopArrPtr->nstops > 0)) {
7420 gradient->nStepColors = gradient->stopArrPtr->nstops * gradient->steps;
7421 gradient->stepColors = (XColor **)ckalloc(sizeof(XColor*)*gradient->nStepColors);
7422 Gradient_CalcStepColors(tree, gradient);
7423 maskFree |= GRAD_CONF_STEPS;
7424 } else {
7425 gradient->nStepColors = 0;
7426 gradient->stepColors = NULL;
7427 }
7428 }
7429
7430 /*
7431 * Step 3: Free saved values
7432 */
7433
7434 if (mask & (GRAD_CONF_STOPS | GRAD_CONF_STEPS)) {
7435 if (saved.stepColors != NULL) { /* will be NULL when createFlag==1 */
7436 int i;
7437 for (i = 0; i < saved.nStepColors; i++) {
7438 Tk_FreeColor(saved.stepColors[i]);
7439 }
7440 WCFREE(saved.stepColors, XColor*, saved.nStepColors);
7441 }
7442 }
7443
7444 Tk_FreeSavedOptions(&savedOptions);
7445 break;
7446 } else {
7447 errorResult = Tcl_GetObjResult(tree->interp);
7448 Tcl_IncrRefCount(errorResult);
7449 Tk_RestoreSavedOptions(&savedOptions);
7450
7451 /*
7452 * Free new values.
7453 */
7454
7455 if (maskFree & (GRAD_CONF_STOPS | GRAD_CONF_STEPS)) {
7456 if (gradient->stepColors != NULL) {
7457 int i;
7458 for (i = 0; i < gradient->nStepColors; i++) {
7459 Tk_FreeColor(gradient->stepColors[i]);
7460 }
7461 WCFREE(gradient->stepColors, XColor*, gradient->nStepColors);
7462 }
7463 }
7464
7465 /*
7466 * Restore old values.
7467 */
7468 if (mask & (GRAD_CONF_STOPS | GRAD_CONF_STEPS)) {
7469 gradient->nStepColors = saved.nStepColors;
7470 gradient->stepColors = saved.stepColors;
7471 }
7472
7473 Tcl_SetObjResult(tree->interp, errorResult);
7474 Tcl_DecrRefCount(errorResult);
7475 return TCL_ERROR;
7476 }
7477 }
7478 return TCL_OK;
7479 }
7480
7481 /*
7482 *----------------------------------------------------------------------
7483 *
7484 * Gradient_FreeResources --
7485 *
7486 * Free memory etc associated with a TreeGradient.
7487 *
7488 * Results:
7489 * None.
7490 *
7491 * Side effects:
7492 * Memory is deallocated.
7493 *
7494 *----------------------------------------------------------------------
7495 */
7496
7497 static void
Gradient_FreeResources(TreeCtrl * tree,TreeGradient gradient,int deleting)7498 Gradient_FreeResources(
7499 TreeCtrl *tree, /* Widget info. */
7500 TreeGradient gradient, /* Gradient token. */
7501 int deleting /* TRUE if deleting the gradient,
7502 FALSE if createing a new gradient to
7503 replace a deletePending gradient. */
7504 )
7505 {
7506 Tcl_HashEntry *hPtr;
7507 int i;
7508
7509 Tk_FreeConfigOptions((char *) gradient,
7510 tree->gradientOptionTable,
7511 tree->tkwin);
7512
7513 if (gradient->stepColors != NULL) {
7514 for (i = 0; i < gradient->nStepColors; i++)
7515 Tk_FreeColor(gradient->stepColors[i]);
7516 WCFREE(gradient->stepColors, XColor*, gradient->nStepColors);
7517 }
7518
7519 if (deleting == 0)
7520 return;
7521
7522 hPtr = Tcl_FindHashEntry(&tree->gradientHash, gradient->name);
7523 if (hPtr) /* NULL during creation */
7524 Tcl_DeleteHashEntry(hPtr);
7525
7526 WFREE(gradient, TreeGradient_);
7527 }
7528
7529 /*
7530 *----------------------------------------------------------------------
7531 *
7532 * TreeGradient_Release --
7533 *
7534 * Decrease refCount on a gradient and free it if deletion was
7535 * pending.
7536 *
7537 * Results:
7538 * None.
7539 *
7540 * Side effects:
7541 * Memory may be deallocated.
7542 *
7543 *----------------------------------------------------------------------
7544 */
7545
7546 void
TreeGradient_Release(TreeCtrl * tree,TreeGradient gradient)7547 TreeGradient_Release(
7548 TreeCtrl *tree, /* Widget info. */
7549 TreeGradient gradient /* Gradient token. */
7550 )
7551 {
7552 #ifdef TREECTRL_DEBUG
7553 if (gradient->refCount <= 0)
7554 panic("TreeGradient_Release: refCount <= 0");
7555 #endif
7556 gradient->refCount--;
7557 if ((gradient->refCount == 0) && (gradient->deletePending != 0)) {
7558 Gradient_FreeResources(tree, gradient, 1);
7559 }
7560 }
7561
7562 /*
7563 *----------------------------------------------------------------------
7564 *
7565 * Gradient_CreateAndConfig --
7566 *
7567 * Allocate and initialize a gradient.
7568 *
7569 * Results:
7570 * Pointer to the new Gradient, or NULL if an error occurs.
7571 *
7572 * Side effects:
7573 * Memory is allocated.
7574 *
7575 *----------------------------------------------------------------------
7576 */
7577
7578 static TreeGradient
Gradient_CreateAndConfig(TreeCtrl * tree,char * name,int objc,Tcl_Obj * CONST objv[])7579 Gradient_CreateAndConfig(
7580 TreeCtrl *tree, /* Widget info. */
7581 char *name, /* Name of new gradient. */
7582 int objc, /* Number of config-option arg-value pairs. */
7583 Tcl_Obj *CONST objv[] /* Config-option arg-value pairs. */
7584 )
7585 {
7586 TreeGradient gradient;
7587
7588 gradient = (TreeGradient) ckalloc(sizeof(TreeGradient_));
7589 memset(gradient, '\0', sizeof(TreeGradient_));
7590 gradient->name = Tk_GetUid(name);
7591
7592 if (Tk_InitOptions(tree->interp, (char *) gradient,
7593 tree->gradientOptionTable, tree->tkwin) != TCL_OK) {
7594 WFREE(gradient, TreeGradient_);
7595 return NULL;
7596 }
7597
7598 if (Gradient_Config(tree, gradient, objc, objv, TRUE) != TCL_OK) {
7599 Gradient_FreeResources(tree, gradient, 1);
7600 return NULL;
7601 }
7602
7603 return gradient;
7604 }
7605
7606 static void
Gradient_Changed(TreeCtrl * tree,TreeGradient gradient)7607 Gradient_Changed(
7608 TreeCtrl *tree, /* Widget info. */
7609 TreeGradient gradient /* Gradient token. */
7610 )
7611 {
7612 Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_OUT_OF_DATE | DINFO_DRAW_HEADER);
7613 }
7614
7615 static void
Gradient_Deleted(TreeCtrl * tree,TreeGradient gradient)7616 Gradient_Deleted(
7617 TreeCtrl *tree, /* Widget info. */
7618 TreeGradient gradient /* Gradient token. */
7619 )
7620 {
7621 }
7622
7623 /*
7624 *----------------------------------------------------------------------
7625 *
7626 * TreeGradientCmd --
7627 *
7628 * This procedure is invoked to process the [gradient]
7629 * widget command. See the user documentation for details on what
7630 * it does.
7631 *
7632 * Results:
7633 * A standard Tcl result.
7634 *
7635 * Side effects:
7636 * See the user documentation.
7637 *
7638 *----------------------------------------------------------------------
7639 */
7640
7641 int
TreeGradientCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * CONST objv[])7642 TreeGradientCmd(
7643 ClientData clientData, /* Widget info. */
7644 Tcl_Interp *interp, /* Current interpreter. */
7645 int objc, /* Number of arguments. */
7646 Tcl_Obj *CONST objv[] /* Argument values. */
7647 )
7648 {
7649 TreeCtrl *tree = clientData;
7650 static CONST char *commandNames[] = {
7651 "cget", "configure", "create", "delete", "names", "native",
7652 (char *) NULL
7653 };
7654 enum {
7655 COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE,
7656 COMMAND_DELETE, COMMAND_NAMES, COMMAND_NATIVE
7657 };
7658 int index;
7659
7660 if (objc < 3) {
7661 Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
7662 return TCL_ERROR;
7663 }
7664
7665 if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
7666 &index) != TCL_OK) {
7667 return TCL_ERROR;
7668 }
7669
7670 switch (index) {
7671 case COMMAND_CGET: {
7672 Tcl_Obj *resultObjPtr = NULL;
7673 TreeGradient gradient;
7674
7675 if (objc != 5) {
7676 Tcl_WrongNumArgs(interp, 3, objv, "name option");
7677 return TCL_ERROR;
7678 }
7679 if (TreeGradient_FromObj(tree, objv[3], &gradient) != TCL_OK)
7680 return TCL_ERROR;
7681 resultObjPtr = Tk_GetOptionValue(interp, (char *) gradient,
7682 tree->gradientOptionTable, objv[4], tree->tkwin);
7683 if (resultObjPtr == NULL)
7684 return TCL_ERROR;
7685 Tcl_SetObjResult(interp, resultObjPtr);
7686 break;
7687 }
7688 case COMMAND_CONFIGURE: {
7689 Tcl_Obj *resultObjPtr = NULL;
7690 TreeGradient gradient;
7691
7692 if (objc < 4) {
7693 Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value ...?");
7694 return TCL_ERROR;
7695 }
7696 if (TreeGradient_FromObj(tree, objv[3], &gradient) != TCL_OK)
7697 return TCL_ERROR;
7698 if (objc <= 5) {
7699 resultObjPtr = Tk_GetOptionInfo(interp, (char *) gradient,
7700 tree->gradientOptionTable,
7701 (objc == 4) ? (Tcl_Obj *) NULL : objv[4],
7702 tree->tkwin);
7703 if (resultObjPtr == NULL)
7704 return TCL_ERROR;
7705 Tcl_SetObjResult(interp, resultObjPtr);
7706 } else {
7707 if (Gradient_Config(tree, gradient,
7708 objc - 4, objv + 4, 0) != TCL_OK)
7709 return TCL_ERROR;
7710 Gradient_Changed(tree, gradient);
7711 }
7712 break;
7713 }
7714 /* T gradient create NAME ?option value ...? */
7715 case COMMAND_CREATE: {
7716 char *name;
7717 int len;
7718 Tcl_HashEntry *hPtr;
7719 int isNew;
7720 TreeGradient gradient;
7721
7722 if (objc < 4) {
7723 Tcl_WrongNumArgs(interp, 3, objv, "name ?option value ...?");
7724 return TCL_ERROR;
7725 }
7726 name = Tcl_GetStringFromObj(objv[3], &len);
7727 if (!len) {
7728 FormatResult(interp, "invalid gradient name \"\"");
7729 return TCL_ERROR;
7730 }
7731 /* Just like named fonts, if we create a new gradient that has the
7732 * same name as one that is awaiting deletion, we copy the new
7733 * config to the old gradient and forget it is being deleted. */
7734 hPtr = Tcl_FindHashEntry(&tree->gradientHash, name);
7735 if (hPtr != NULL) {
7736 TreeGradient gradient2;
7737 gradient = (TreeGradient) Tcl_GetHashValue(hPtr);
7738 if (gradient->deletePending == 0) {
7739 FormatResult(interp, "gradient \"%s\" already exists", name);
7740 return TCL_ERROR;
7741 }
7742 gradient2 = Gradient_CreateAndConfig(tree, name, objc - 4, objv + 4);
7743 if (gradient2 == NULL)
7744 return TCL_ERROR;
7745 Gradient_FreeResources(tree, gradient, 0);
7746 gradient->stopArrPtr = gradient2->stopArrPtr;
7747 gradient->steps = gradient2->steps;
7748 gradient->nStepColors = gradient2->nStepColors;
7749 gradient->stepColors = gradient2->stepColors;
7750 gradient->deletePending = 0;
7751 WFREE(gradient2, TreeGradient_);
7752 Gradient_Changed(tree, gradient);
7753 break;
7754 }
7755 gradient = Gradient_CreateAndConfig(tree, name, objc - 4, objv + 4);
7756 if (gradient == NULL)
7757 return TCL_ERROR;
7758 hPtr = Tcl_CreateHashEntry(&tree->gradientHash, name, &isNew);
7759 Tcl_SetHashValue(hPtr, gradient);
7760 Tcl_SetObjResult(interp, Gradient_ToObj((TreeGradient) gradient));
7761 break;
7762 }
7763 /* T gradient delete ?name ...? */
7764 case COMMAND_DELETE: {
7765 int i;
7766 TreeGradient gradient;
7767
7768 if (objc < 3) {
7769 Tcl_WrongNumArgs(interp, 3, objv, "?name ...?");
7770 return TCL_ERROR;
7771 }
7772 for (i = 3; i < objc; i++) {
7773 if (TreeGradient_FromObj(tree, objv[i], &gradient) != TCL_OK)
7774 return TCL_ERROR;
7775 if (gradient->refCount > 0) {
7776 gradient->deletePending = 1;
7777 } else {
7778 Gradient_Deleted(tree, gradient);
7779 Gradient_FreeResources(tree, gradient, 1);
7780 }
7781 }
7782 break;
7783 }
7784 /* T gradient names */
7785 case COMMAND_NAMES: {
7786 Tcl_Obj *listObj;
7787 Tcl_HashSearch search;
7788 Tcl_HashEntry *hPtr;
7789 TreeGradient gradient;
7790
7791 if (objc != 3) {
7792 Tcl_WrongNumArgs(interp, 3, objv, NULL);
7793 return TCL_ERROR;
7794 }
7795 listObj = Tcl_NewListObj(0, NULL);
7796 hPtr = Tcl_FirstHashEntry(&tree->gradientHash, &search);
7797 while (hPtr != NULL) {
7798 gradient = (TreeGradient) Tcl_GetHashValue(hPtr);
7799 if (gradient->deletePending == 0) {
7800 Tcl_ListObjAppendElement(interp, listObj, Gradient_ToObj(gradient));
7801 }
7802 hPtr = Tcl_NextHashEntry(&search);
7803 }
7804 Tcl_SetObjResult(interp, listObj);
7805 break;
7806 }
7807 /* T gradient native ?bool? */
7808 case COMMAND_NATIVE: {
7809 int native;
7810
7811 if (objc > 4) {
7812 Tcl_WrongNumArgs(interp, 3, objv, "?preference?");
7813 return TCL_ERROR;
7814 }
7815 if (objc == 4) {
7816 if (Tcl_GetBooleanFromObj(interp, objv[3], &native) != TCL_OK) {
7817 return TCL_ERROR;
7818 }
7819 if (native != tree->nativeGradients) {
7820 Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_OUT_OF_DATE
7821 | DINFO_DRAW_HEADER);
7822 tree->nativeGradients = native;
7823 }
7824 }
7825 native = Tree_HasNativeGradients(tree);
7826 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(native));
7827 break;
7828 }
7829 }
7830 return TCL_OK;
7831 }
7832
7833 /*
7834 *----------------------------------------------------------------------
7835 *
7836 * TreeGradient_IsOpaque --
7837 *
7838 * Test whether a gradient would be drawn fully opaque or not.
7839 *
7840 * Results:
7841 * 1 if opaque, 0 otherwise.
7842 *
7843 * Side effects:
7844 * None.
7845 *
7846 *----------------------------------------------------------------------
7847 */
7848
7849 int
TreeGradient_IsOpaque(TreeCtrl * tree,TreeGradient gradient)7850 TreeGradient_IsOpaque(
7851 TreeCtrl *tree, /* Widget info. */
7852 TreeGradient gradient /* Gradient token. */
7853 )
7854 {
7855 GradientStopArray *stopArrPtr = gradient->stopArrPtr;
7856 int i;
7857
7858 if (stopArrPtr->nstops < 2)
7859 return 0; /* no colors == fully transparent */
7860
7861 if (!tree->nativeGradients || !Tree_HasNativeGradients(tree))
7862 return 1;
7863
7864 for (i = 0; i < stopArrPtr->nstops; i++) {
7865 GradientStop *stop = stopArrPtr->stops[i];
7866 if (stop->opacity < 1.0f)
7867 return 0;
7868 }
7869
7870 return 1;
7871 }
7872
7873 /*
7874 *----------------------------------------------------------------------
7875 *
7876 * TreeGradient_InitWidget --
7877 *
7878 * Gradient-related package initialization.
7879 *
7880 * Results:
7881 * A standard Tcl result.
7882 *
7883 * Side effects:
7884 * None.
7885 *
7886 *----------------------------------------------------------------------
7887 */
7888
7889 void
TreeGradient_InitWidget(TreeCtrl * tree)7890 TreeGradient_InitWidget(
7891 TreeCtrl *tree /* Widget info. */
7892 )
7893 {
7894 tree->gradientOptionTable = Tk_CreateOptionTable(tree->interp,
7895 gradientOptionSpecs);
7896 tree->nativeGradients = 1; /* Preference, not availability */
7897 }
7898
7899 /*
7900 *----------------------------------------------------------------------
7901 *
7902 * TreeGradient_FreeWidget --
7903 *
7904 * Free gradient-related resources for a deleted TreeCtrl.
7905 *
7906 * Results:
7907 * None.
7908 *
7909 * Side effects:
7910 * Memory is freed.
7911 *
7912 *----------------------------------------------------------------------
7913 */
7914
7915 void
TreeGradient_FreeWidget(TreeCtrl * tree)7916 TreeGradient_FreeWidget(
7917 TreeCtrl *tree /* Widget info. */
7918 )
7919 {
7920 Tcl_HashEntry *hPtr;
7921 Tcl_HashSearch search;
7922 TreeGradient gradient;
7923
7924 while (1) {
7925 hPtr = Tcl_FirstHashEntry(&tree->gradientHash, &search);
7926 if (hPtr == NULL)
7927 break;
7928 gradient = (TreeGradient) Tcl_GetHashValue(hPtr);
7929 if (gradient->refCount != 0) {
7930 panic("TreeGradient_Free: one or more gradients still being used");
7931 }
7932 Gradient_FreeResources(tree, gradient, 1);
7933 }
7934
7935 Tcl_DeleteHashTable(&tree->gradientHash);
7936 }
7937
7938 /*
7939 *----------------------------------------------------------------------
7940 *
7941 * TreeGradient_FillRoundRectX11 --
7942 *
7943 * Paint a rectangle with a gradient using XFillRectangle.
7944 * We can't paint a rounded rectangle with a gradient on X11.
7945 * If I could clip drawing to arc's then I could do it.
7946 *
7947 * Results:
7948 * None.
7949 *
7950 * Side effects:
7951 * Drawing.
7952 *
7953 *----------------------------------------------------------------------
7954 */
7955
7956 void
TreeGradient_FillRoundRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr,int rx,int ry,int open)7957 TreeGradient_FillRoundRectX11(
7958 TreeCtrl *tree, /* Widget info. */
7959 TreeDrawable td, /* Where to draw. */
7960 TreeClip *clip, /* Clipping area or NULL. */
7961 TreeGradient gradient, /* Gradient token. */
7962 TreeRectangle trBrush, /* Brush bounds. */
7963 TreeRectangle tr, /* Rectangle to paint. */
7964 int rx, int ry, /* Corner radius */
7965 int open /* RECT_OPEN_x flags */
7966 )
7967 {
7968 if (tr.height <= 0 || tr.width <= 0 || gradient->nStepColors <= 0)
7969 return;
7970
7971 /* Use the first stop color */
7972 Tree_FillRoundRect(tree, td, clip, gradient->stopArrPtr->stops[0]->color,
7973 tr, rx, ry, open);
7974 }
7975
7976 /*
7977 *----------------------------------------------------------------------
7978 *
7979 * TreeGradient_FillRectX11 --
7980 *
7981 * Paint a rectangle with a gradient using XFillRectangle.
7982 *
7983 * Results:
7984 * None.
7985 *
7986 * Side effects:
7987 * Drawing.
7988 *
7989 *----------------------------------------------------------------------
7990 */
7991
7992 void
_TreeGradient_FillRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr)7993 _TreeGradient_FillRectX11(
7994 TreeCtrl *tree, /* Widget info. */
7995 TreeDrawable td, /* Where to draw. */
7996 TreeClip *clip, /* Clipping area or NULL. */
7997 TreeGradient gradient, /* Gradient token. */
7998 TreeRectangle trBrush, /* Brush bounds. */
7999 TreeRectangle tr /* Rectangle to paint. */
8000 )
8001 {
8002 TreeRectangle trSub, trPaint;
8003 int i;
8004 float delta;
8005 GC gc;
8006
8007 if (tr.height <= 0 || tr.width <= 0 || gradient->nStepColors <= 0)
8008 return;
8009
8010 trSub = trBrush;
8011
8012 if (gradient->vertical) {
8013 delta = ((float)trBrush.height) / gradient->nStepColors;
8014 for (i = 0; i < gradient->nStepColors; i++) {
8015 float y1 = trBrush.y + i * delta;
8016 float y2 = trBrush.y + (i+1) * delta;
8017 trSub.y = (int)y1;
8018 trSub.height = (int)(ceil(y2) - floor(y1));
8019 if (TreeRect_Intersect(&trPaint, &trSub, &tr)) {
8020 gc = Tk_GCForColor(gradient->stepColors[i], Tk_WindowId(tree->tkwin));
8021 Tree_FillRectangle(tree, td, clip, gc, trPaint);
8022 }
8023 }
8024 } else {
8025 delta = ((float)trBrush.width) / gradient->nStepColors;
8026 for (i = 0; i < gradient->nStepColors; i++) {
8027 float x1 = trBrush.x + i * delta;
8028 float x2 = trBrush.x + (i+1) * delta;
8029 trSub.x = (int)x1;
8030 trSub.width = (int)(ceil(x2) - floor(x1));
8031 if (TreeRect_Intersect(&trPaint, &trSub, &tr)) {
8032 gc = Tk_GCForColor(gradient->stepColors[i], Tk_WindowId(tree->tkwin));
8033 Tree_FillRectangle(tree, td, clip, gc, trPaint);
8034 }
8035 }
8036 }
8037 }
8038
8039 void
TreeGradient_FillRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr)8040 TreeGradient_FillRectX11(
8041 TreeCtrl *tree, /* Widget info. */
8042 TreeDrawable td, /* Where to draw. */
8043 TreeClip *clip, /* Clipping area or NULL. */
8044 TreeGradient gradient, /* Gradient token. */
8045 TreeRectangle trBrush, /* Brush bounds. */
8046 TreeRectangle tr /* Rectangle to paint. */
8047 )
8048 {
8049 TreeRectangle trSub;
8050 int oldX = trBrush.x, oldY = trBrush.y;
8051
8052 if (trBrush.height < 1 || trBrush.width < 1) return;
8053 if (tr.height < 1 || tr.width < 1) return;
8054
8055 /* This implements tiling of the gradient brush */
8056 while (tr.x < trBrush.x)
8057 trBrush.x -= trBrush.width;
8058 while (tr.x >= trBrush.x + trBrush.width)
8059 trBrush.x += trBrush.width;
8060 while (tr.y < trBrush.y)
8061 trBrush.y -= trBrush.height;
8062 while (tr.y >= trBrush.y + trBrush.height)
8063 trBrush.y += trBrush.height;
8064 oldX = trBrush.x, oldY = trBrush.y;
8065 while (trBrush.x < tr.x + tr.width) {
8066 trBrush.y = oldY;
8067 while (trBrush.y < tr.y + tr.height) {
8068 TreeRect_Intersect(&trSub, &trBrush, &tr);
8069 _TreeGradient_FillRectX11(tree, td, clip, gradient,
8070 trBrush, trSub);
8071 trBrush.y += trBrush.height;
8072 }
8073 trBrush.x += trBrush.width;
8074 }
8075 }
8076
8077 void
TreeGradient_DrawRectX11(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr,int outlineWidth,int open)8078 TreeGradient_DrawRectX11(
8079 TreeCtrl *tree, /* Widget info. */
8080 TreeDrawable td, /* Where to draw. */
8081 TreeClip *clip, /* Clipping area or NULL. */
8082 TreeGradient gradient, /* Gradient token. */
8083 TreeRectangle trBrush, /* Brush bounds. */
8084 TreeRectangle tr, /* Rectangle to outline. */
8085 int outlineWidth, /* Width of outline. */
8086 int open /* RECT_OPEN_x flags */
8087 )
8088 {
8089 TreeRectangle trEdge;
8090
8091 if (!(open & RECT_OPEN_W)) {
8092 trEdge.x = tr.x, trEdge.y = tr.y,
8093 trEdge.width = outlineWidth, trEdge.height = tr.height;
8094 TreeGradient_FillRectX11(tree, td, clip, gradient, trBrush, trEdge);
8095 }
8096 if (!(open & RECT_OPEN_N)) {
8097 trEdge.x = tr.x, trEdge.y = tr.y,
8098 trEdge.width = tr.width, trEdge.height = outlineWidth;
8099 TreeGradient_FillRectX11(tree, td, clip, gradient, trBrush, trEdge);
8100 }
8101 if (!(open & RECT_OPEN_E)) {
8102 trEdge.x = tr.x + tr.width - outlineWidth, trEdge.y = tr.y,
8103 trEdge.width = outlineWidth, trEdge.height = tr.height;
8104 TreeGradient_FillRectX11(tree, td, clip, gradient, trBrush, trEdge);
8105 }
8106 if (!(open & RECT_OPEN_S)) {
8107 trEdge.x = tr.x, trEdge.y = tr.y + tr.height - outlineWidth,
8108 trEdge.width = tr.width, trEdge.height = outlineWidth;
8109 TreeGradient_FillRectX11(tree, td, clip, gradient, trBrush, trEdge);
8110 }
8111 }
8112
8113 /*
8114 *----------------------------------------------------------------------
8115 *
8116 * TreeColor_DrawRect --
8117 *
8118 * Draw a rectangle with a gradient or solid color.
8119 *
8120 * Results:
8121 * If the gradient has <2 stops then nothing is drawn.
8122 *
8123 * Side effects:
8124 * Drawing.
8125 *
8126 *----------------------------------------------------------------------
8127 */
8128
8129 void
TreeColor_DrawRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeColor * tc,TreeRectangle trBrush,TreeRectangle tr,int outlineWidth,int open)8130 TreeColor_DrawRect(
8131 TreeCtrl *tree, /* Widget info. */
8132 TreeDrawable td, /* Where to draw. */
8133 TreeClip *clip, /* Clipping area or NULL. */
8134 TreeColor *tc, /* Color info. */
8135 TreeRectangle trBrush, /* Brush bounds. */
8136 TreeRectangle tr, /* Rectangle to outline. */
8137 int outlineWidth, /* Width of outline. */
8138 int open /* RECT_OPEN_x flags */
8139 )
8140 {
8141 if (tc == NULL || outlineWidth < 1 || open == RECT_OPEN_WNES)
8142 return;
8143 if (tc->gradient != NULL) {
8144 TreeGradient_DrawRect(tree, td, clip, tc->gradient, trBrush, tr,
8145 outlineWidth, open);
8146 }
8147 if (tc->color != NULL) {
8148 GC gc = Tk_GCForColor(tc->color, td.drawable);
8149 TreeRectangle trEdge;
8150 if (!(open & RECT_OPEN_W)) {
8151 trEdge.x = tr.x, trEdge.y = tr.y,
8152 trEdge.width = outlineWidth, trEdge.height = tr.height;
8153 Tree_FillRectangle(tree, td, clip, gc, trEdge);
8154 }
8155 if (!(open & RECT_OPEN_N)) {
8156 trEdge.x = tr.x, trEdge.y = tr.y,
8157 trEdge.width = tr.width, trEdge.height = outlineWidth;
8158 Tree_FillRectangle(tree, td, clip, gc, trEdge);
8159 }
8160 if (!(open & RECT_OPEN_E)) {
8161 trEdge.x = tr.x + tr.width - outlineWidth, trEdge.y = tr.y,
8162 trEdge.width = outlineWidth, trEdge.height = tr.height;
8163 Tree_FillRectangle(tree, td, clip, gc, trEdge);
8164 }
8165 if (!(open & RECT_OPEN_S)) {
8166 trEdge.x = tr.x, trEdge.y = tr.y + tr.height - outlineWidth,
8167 trEdge.width = tr.width, trEdge.height = outlineWidth;
8168 Tree_FillRectangle(tree, td, clip, gc, trEdge);
8169 }
8170 }
8171 }
8172
8173 /*
8174 *----------------------------------------------------------------------
8175 *
8176 * TreeColor_FillRect --
8177 *
8178 * Paint a rectangle with a gradient or solid color.
8179 *
8180 * Results:
8181 * If the gradient has <2 stops then nothing is drawn.
8182 *
8183 * Side effects:
8184 * Drawing.
8185 *
8186 *----------------------------------------------------------------------
8187 */
8188
8189 void
TreeColor_FillRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeColor * tc,TreeRectangle trBrush,TreeRectangle tr)8190 TreeColor_FillRect(
8191 TreeCtrl *tree, /* Widget info. */
8192 TreeDrawable td, /* Where to draw. */
8193 TreeClip *clip, /* Clipping area or NULL. */
8194 TreeColor *tc, /* Color info. */
8195 TreeRectangle trBrush, /* Brush bounds. */
8196 TreeRectangle tr /* Rectangle to paint. */
8197 )
8198 {
8199 if (tc == NULL)
8200 return;
8201 if (tc->gradient != NULL) {
8202 TreeGradient_FillRect(tree, td, clip, tc->gradient, trBrush, tr);
8203 }
8204 if (tc->color != NULL) {
8205 GC gc = Tk_GCForColor(tc->color, td.drawable);
8206 Tree_FillRectangle(tree, td, clip, gc, tr);
8207 }
8208 }
8209
8210 /*
8211 *----------------------------------------------------------------------
8212 *
8213 * TreeColor_DrawRoundRect --
8214 *
8215 * Draw a rounded rectangle with a gradient or solid color.
8216 *
8217 * Results:
8218 * If the gradient has <2 stops then nothing is drawn.
8219 *
8220 * Side effects:
8221 * Drawing.
8222 *
8223 *----------------------------------------------------------------------
8224 */
8225
8226 void
TreeColor_DrawRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeColor * tc,TreeRectangle trBrush,TreeRectangle tr,int outlineWidth,int rx,int ry,int open)8227 TreeColor_DrawRoundRect(
8228 TreeCtrl *tree, /* Widget info. */
8229 TreeDrawable td, /* Where to draw. */
8230 TreeClip *clip, /* Clipping area or NULL. */
8231 TreeColor *tc, /* Color info. */
8232 TreeRectangle trBrush, /* Brush bounds. */
8233 TreeRectangle tr, /* Rectangle to outline. */
8234 int outlineWidth, /* Width of outline. */
8235 int rx, int ry, /* Corner radius */
8236 int open /* RECT_OPEN_x flags */
8237 )
8238 {
8239 if (tc == NULL)
8240 return;
8241 if (tc->gradient != NULL) {
8242 TreeGradient_DrawRoundRect(tree, td, clip, tc->gradient, trBrush, tr,
8243 outlineWidth, rx, ry, open);
8244 }
8245 if (tc->color != NULL) {
8246 Tree_DrawRoundRect(tree, td, clip, tc->color, tr, outlineWidth,
8247 rx, ry, open);
8248 }
8249 }
8250
8251 /*
8252 *----------------------------------------------------------------------
8253 *
8254 * TreeColor_FillRoundRect --
8255 *
8256 * Paint a rounded rectangle with a gradient or solid color.
8257 *
8258 * Results:
8259 * If the gradient has <2 stops then nothing is drawn.
8260 *
8261 * Side effects:
8262 * Drawing.
8263 *
8264 *----------------------------------------------------------------------
8265 */
8266
8267 void
TreeColor_FillRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeColor * tc,TreeRectangle trBrush,TreeRectangle tr,int rx,int ry,int open)8268 TreeColor_FillRoundRect(
8269 TreeCtrl *tree, /* Widget info. */
8270 TreeDrawable td, /* Where to draw. */
8271 TreeClip *clip, /* Clipping area or NULL. */
8272 TreeColor *tc, /* Color info. */
8273 TreeRectangle trBrush, /* Brush bounds. */
8274 TreeRectangle tr, /* Rectangle to paint. */
8275 int rx, int ry, /* Corner radius */
8276 int open /* RECT_OPEN_x flags */
8277 )
8278 {
8279 if (tc == NULL)
8280 return;
8281 if (tc->gradient != NULL) {
8282 TreeGradient_FillRoundRect(tree, td, clip, tc->gradient, trBrush, tr,
8283 rx, ry, open);
8284 }
8285 if (tc->color != NULL) {
8286 Tree_FillRoundRect(tree, td, clip, tc->color, tr, rx, ry, open);
8287 }
8288 }
8289
8290