1 /*
2  * tkWinTree.c --
3  *
4  *	Platform-specific parts of TkTreeCtrl for Microsoft Windows.
5  *
6  * Copyright (c) 2010-2011 Tim Baker
7  */
8 
9 #define WINVER 0x0501 /* MingW32 */
10 #define _WIN32_WINNT 0x0501 /* ACTCTX stuff */
11 
12 #include "tkTreeCtrl.h"
13 #include "tkWinInt.h"
14 
15 /*
16  *----------------------------------------------------------------------
17  *
18  * Tree_HDotLine --
19  *
20  *	Draws a horizontal 1-pixel-tall dotted line.
21  *
22  * Results:
23  *	None.
24  *
25  * Side effects:
26  *	Stuff is drawn.
27  *
28  *----------------------------------------------------------------------
29  */
30 
31 void
Tree_HDotLine(TreeCtrl * tree,Drawable drawable,int x1,int y1,int x2)32 Tree_HDotLine(
33     TreeCtrl *tree,		/* Widget info. */
34     Drawable drawable,		/* Where to draw. */
35     int x1, int y1, int x2	/* Left, top and right coordinates. */
36     )
37 {
38     TkWinDCState state;
39     HDC dc;
40     HPEN pen, oldPen;
41     int nw;
42     int wx = x1 + tree->drawableXOrigin;
43     int wy = y1 + tree->drawableYOrigin;
44 
45     dc = TkWinGetDrawableDC(tree->display, drawable, &state);
46     SetROP2(dc, R2_COPYPEN);
47 
48     pen = CreatePen(PS_SOLID, 1, tree->lineGC[0]->foreground);
49     oldPen = SelectObject(dc, pen);
50 
51     nw = !(wx & 1) == !(wy & 1);
52     for (x1 += !nw; x1 < x2; x1 += 2) {
53 	MoveToEx(dc, x1, y1, NULL);
54 	LineTo(dc, x1 + 1, y1);
55     }
56 
57     SelectObject(dc, oldPen);
58     DeleteObject(pen);
59 
60     TkWinReleaseDrawableDC(drawable, dc, &state);
61 }
62 
63 /*
64  *----------------------------------------------------------------------
65  *
66  * Tree_VDotLine --
67  *
68  *	Draws a vertical 1-pixel-wide dotted line.
69  *
70  * Results:
71  *	None.
72  *
73  * Side effects:
74  *	Stuff is drawn.
75  *
76  *----------------------------------------------------------------------
77  */
78 
79 void
Tree_VDotLine(TreeCtrl * tree,Drawable drawable,int x1,int y1,int y2)80 Tree_VDotLine(
81     TreeCtrl *tree,		/* Widget info. */
82     Drawable drawable,		/* Where to draw. */
83     int x1, int y1, int y2)	/* Left, top, and bottom coordinates. */
84 {
85     TkWinDCState state;
86     HDC dc;
87     HPEN pen, oldPen;
88     int nw;
89     int wx = x1 + tree->drawableXOrigin;
90     int wy = y1 + tree->drawableYOrigin;
91 
92     dc = TkWinGetDrawableDC(tree->display, drawable, &state);
93     SetROP2(dc, R2_COPYPEN);
94 
95     pen = CreatePen(PS_SOLID, 1, tree->lineGC[0]->foreground);
96     oldPen = SelectObject(dc, pen);
97 
98     nw = !(wx & 1) == !(wy & 1);
99     for (y1 += !nw; y1 < y2; y1 += 2) {
100 	MoveToEx(dc, x1, y1, NULL);
101 	LineTo(dc, x1 + 1, y1);
102     }
103 
104     SelectObject(dc, oldPen);
105     DeleteObject(pen);
106 
107     TkWinReleaseDrawableDC(drawable, dc, &state);
108 }
109 
110 /*
111  *----------------------------------------------------------------------
112  *
113  * Tree_DrawActiveOutline --
114  *
115  *	Draws 0 or more sides of a rectangle, dot-on dot-off, XOR style.
116  *	This is used by rectangle Elements to indicate the "active"
117  *	item.
118  *
119  * Results:
120  *	None.
121  *
122  * Side effects:
123  *	Stuff is drawn.
124  *
125  *----------------------------------------------------------------------
126  */
127 
128 void
Tree_DrawActiveOutline(TreeCtrl * tree,Drawable drawable,int x,int y,int width,int height,int open)129 Tree_DrawActiveOutline(
130     TreeCtrl *tree,		/* Widget info. */
131     Drawable drawable,		/* Where to draw. */
132     int x, int y,		/* Left and top coordinates. */
133     int width, int height,	/* Size of rectangle. */
134     int open			/* RECT_OPEN_x flags */
135     )
136 {
137     int wx = x + tree->drawableXOrigin;
138     int wy = y + tree->drawableYOrigin;
139     int w = !(open & RECT_OPEN_W);
140     int n = !(open & RECT_OPEN_N);
141     int e = !(open & RECT_OPEN_E);
142     int s = !(open & RECT_OPEN_S);
143     int nw, ne, sw, se;
144     int i;
145     TkWinDCState state;
146     HDC dc;
147 
148     /* Dots on even pixels only */
149     nw = !(wx & 1) == !(wy & 1);
150     ne = !((wx + width - 1) & 1) == !(wy & 1);
151     sw = !(wx & 1) == !((wy + height - 1) & 1);
152     se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1);
153 
154     dc = TkWinGetDrawableDC(tree->display, drawable, &state);
155     SetROP2(dc, R2_NOT);
156 
157     if (w) /* left */
158     {
159 	for (i = !nw; i < height; i += 2) {
160 	    MoveToEx(dc, x, y + i, NULL);
161 	    LineTo(dc, x + 1, y + i);
162 	}
163     }
164     if (n) /* top */
165     {
166 	for (i = nw ? w * 2 : 1; i < width; i += 2) {
167 	    MoveToEx(dc, x + i, y, NULL);
168 	    LineTo(dc, x + i + 1, y);
169 	}
170     }
171     if (e) /* right */
172     {
173 	for (i = ne ? n * 2 : 1; i < height; i += 2) {
174 	    MoveToEx(dc, x + width - 1, y + i, NULL);
175 	    LineTo(dc, x + width, y + i);
176 	}
177     }
178     if (s) /* bottom */
179     {
180 	for (i = sw ? w * 2 : 1; i < width - (se && e); i += 2) {
181 	    MoveToEx(dc, x + i, y + height - 1, NULL);
182 	    LineTo(dc, x + i + 1, y + height - 1);
183 	}
184     }
185 
186     TkWinReleaseDrawableDC(drawable, dc, &state);
187 }
188 
189 /*
190  * The following structure is used when drawing a number of dotted XOR
191  * rectangles.
192  */
193 struct DotStatePriv
194 {
195     TreeCtrl *tree;
196     Drawable drawable;
197     HDC dc;
198     TkWinDCState dcState;
199     HRGN rgn;
200 };
201 
202 /*
203  *----------------------------------------------------------------------
204  *
205  * TreeDotRect_Setup --
206  *
207  *	Prepare a drawable for drawing a series of dotted XOR rectangles.
208  *
209  * Results:
210  *	State info is returned to be used by the other TreeDotRect_xxx()
211  *	procedures.
212  *
213  * Side effects:
214  *	On Win32 and OSX the device context/graphics port is altered
215  *	in preparation for drawing. On X11 a new graphics context is
216  *	created.
217  *
218  *----------------------------------------------------------------------
219  */
220 
221 void
TreeDotRect_Setup(TreeCtrl * tree,Drawable drawable,DotState * p)222 TreeDotRect_Setup(
223     TreeCtrl *tree,		/* Widget info. */
224     Drawable drawable,		/* Where to draw. */
225     DotState *p			/* Where to save state info. */
226     )
227 {
228     struct DotStatePriv *dotState = (struct DotStatePriv *) p;
229 
230     if (sizeof(*dotState) > sizeof(*p))
231 	panic("TreeDotRect_Setup: DotState hack is too small");
232 
233     dotState->tree = tree;
234     dotState->drawable = drawable;
235     dotState->dc = TkWinGetDrawableDC(tree->display, drawable, &dotState->dcState);
236 
237     /* XOR drawing */
238     SetROP2(dotState->dc, R2_NOT);
239 
240     /* Keep drawing inside the contentbox. */
241     dotState->rgn = CreateRectRgn(
242 	Tree_ContentLeft(tree),
243 	Tree_ContentTop(tree),
244 	Tree_ContentRight(tree),
245 	Tree_ContentBottom(tree));
246     SelectClipRgn(dotState->dc, dotState->rgn);
247 }
248 
249 /*
250  *----------------------------------------------------------------------
251  *
252  * TreeDotRect_Draw --
253  *
254  *	Draw a dotted XOR rectangle.
255  *
256  * Results:
257  *	None.
258  *
259  * Side effects:
260  *	Stuff is drawn.
261  *
262  *----------------------------------------------------------------------
263  */
264 
265 void
TreeDotRect_Draw(DotState * p,int x,int y,int width,int height)266 TreeDotRect_Draw(
267     DotState *p,		/* Info returned by TreeDotRect_Setup(). */
268     int x, int y,		/* Left and top coordinates. */
269     int width, int height	/* Size of rectangle. */
270     )
271 {
272     struct DotStatePriv *dotState = (struct DotStatePriv *) p;
273 #if 1
274     RECT rect;
275 
276     rect.left = x;
277     rect.right = x + width;
278     rect.top = y;
279     rect.bottom = y + height;
280     DrawFocusRect(dotState->dc, &rect);
281 #else
282     HDC dc = dotState->dc;
283     int i;
284     int wx = x + dotState->tree->drawableXOrigin;
285     int wy = y + dotState->tree->drawableYOrigin;
286     int nw, ne, sw, se;
287 
288     /* Dots on even pixels only */
289     nw = !(wx & 1) == !(wy & 1);
290     ne = !((wx + width - 1) & 1) == !(wy & 1);
291     sw = !(wx & 1) == !((wy + height - 1) & 1);
292     se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1);
293 
294     for (i = !nw; i < height; i += 2) {
295 	MoveToEx(dc, x, y + i, NULL);
296 	LineTo(dc, x + 1, y + i);
297     }
298     for (i = nw ? 2 : 1; i < width; i += 2) {
299 	MoveToEx(dc, x + i, y, NULL);
300 	LineTo(dc, x + i + 1, y);
301     }
302     for (i = ne ? 2 : 1; i < height; i += 2) {
303 	MoveToEx(dc, x + width - 1, y + i, NULL);
304 	LineTo(dc, x + width, y + i);
305     }
306     for (i = sw ? 2 : 1; i < width - se; i += 2) {
307 	MoveToEx(dc, x + i, y + height - 1, NULL);
308 	LineTo(dc, x + i + 1, y + height - 1);
309     }
310 #endif
311 }
312 
313 /*
314  *----------------------------------------------------------------------
315  *
316  * TreeDotRect_Restore --
317  *
318  *	Restore the drawing environment.
319  *
320  * Results:
321  *	None.
322  *
323  * Side effects:
324  *	On Win32 and OSX the device context/graphics port is restored.
325  *	On X11 a new graphics context is freed.
326  *
327  *----------------------------------------------------------------------
328  */
329 
330 void
TreeDotRect_Restore(DotState * p)331 TreeDotRect_Restore(
332     DotState *p			/* Info returned by TreeDotRect_Setup(). */
333     )
334 {
335     struct DotStatePriv *dotState = (struct DotStatePriv *) p;
336     SelectClipRgn(dotState->dc, NULL);
337     DeleteObject(dotState->rgn);
338     TkWinReleaseDrawableDC(dotState->drawable, dotState->dc, &dotState->dcState);
339 }
340 
341 /*
342  *----------------------------------------------------------------------
343  *
344  * Tree_FillRegion --
345  *
346  *	Paint a region with the foreground color of a graphics context.
347  *
348  * Results:
349  *	None.
350  *
351  * Side effects:
352  *	Stuff is drawn.
353  *
354  *----------------------------------------------------------------------
355  */
356 
357 void
Tree_FillRegion(Display * display,Drawable drawable,GC gc,TkRegion rgn)358 Tree_FillRegion(
359     Display *display,		/* Display. */
360     Drawable drawable,		/* Where to draw. */
361     GC gc,			/* Foreground color. */
362     TkRegion rgn		/* Region to paint. */
363     )
364 {
365     HDC dc;
366     TkWinDCState dcState;
367     HBRUSH brush;
368 
369     dc = TkWinGetDrawableDC(display, drawable, &dcState);
370     SetROP2(dc, R2_COPYPEN);
371     brush = CreateSolidBrush(gc->foreground);
372     FillRgn(dc, (HRGN) rgn, brush);
373     DeleteObject(brush);
374     TkWinReleaseDrawableDC(drawable, dc, &dcState);
375 }
376 
377 /*
378  *----------------------------------------------------------------------
379  *
380  * Tree_OffsetRegion --
381  *
382  *	Offset a region.
383  *
384  * Results:
385  *	None.
386  *
387  * Side effects:
388  *	None.
389  *
390  *----------------------------------------------------------------------
391  */
392 
393 void
Tree_OffsetRegion(TkRegion region,int xOffset,int yOffset)394 Tree_OffsetRegion(
395     TkRegion region,		/* Region to modify. */
396     int xOffset, int yOffset	/* Horizontal and vertical offsets. */
397     )
398 {
399     OffsetRgn((HRGN) region, xOffset, yOffset);
400 }
401 
402 /*
403  *----------------------------------------------------------------------
404  *
405  * Tree_UnionRegion --
406  *
407  *	Compute the union of 2 regions.
408  *
409  * Results:
410  *	None.
411  *
412  * Side effects:
413  *	None.
414  *
415  *----------------------------------------------------------------------
416  */
417 
418 void
Tree_UnionRegion(TkRegion rgnA,TkRegion rgnB,TkRegion rgnOut)419 Tree_UnionRegion(
420     TkRegion rgnA,
421     TkRegion rgnB,
422     TkRegion rgnOut
423     )
424 {
425     CombineRgn((HRGN) rgnA, (HRGN) rgnB, (HRGN) rgnOut, RGN_OR);
426 }
427 
428 /*
429  *----------------------------------------------------------------------
430  *
431  * Tree_ScrollWindow --
432  *
433  *	Wrapper around TkScrollWindow() to fix an apparent bug with the
434  *	Mac/OSX versions.
435  *
436  * Results:
437  *	None.
438  *
439  * Side effects:
440  *	Stuff is scrolled in a drawable.
441  *
442  *----------------------------------------------------------------------
443  */
444 
445 int
Tree_ScrollWindow(TreeCtrl * tree,GC gc,int x,int y,int width,int height,int dx,int dy,TkRegion damageRgn)446 Tree_ScrollWindow(
447     TreeCtrl *tree,		/* Widget info. */
448     GC gc,			/* Arg to TkScrollWindow(). */
449     int x, int y,		/* Arg to TkScrollWindow(). */
450     int width, int height,	/* Arg to TkScrollWindow(). */
451     int dx, int dy,		/* Arg to TkScrollWindow(). */
452     TkRegion damageRgn		/* Arg to TkScrollWindow(). */
453     )
454 {
455 #if 0
456     /* It would be best to call ScrollWindowEx with SW_SCROLLCHILDREN so
457      * that windows in window elements scroll smoothly with a minimum of
458      * redrawing. */
459     HWND hwnd = TkWinGetHWND(Tk_WindowId(tree->tkwin));
460     HWND hwndChild;
461     RECT scrollRect, childRect;
462     struct {
463 	int x;
464 	int y;
465 	TkWindow *winPtr;
466     } winInfo[128], *winInfoPtr;
467     TkWindow *winPtr = (TkWindow *) tree->tkwin;
468     int winCount = 0;
469     int result;
470 
471     winInfoPtr = winInfo;
472     for (winPtr = winPtr->childList; winPtr != NULL; winPtr = winPtr->nextPtr) {
473 	if (winPtr->window != None) {
474 	    hwndChild = TkWinGetHWND(winPtr->window);
475 	    GetWindowRect(hwndChild, &childRect);
476 	    winInfoPtr->x = childRect.left;
477 	    winInfoPtr->y = childRect.top;
478 	    winInfoPtr->winPtr = winPtr;
479 	    winInfoPtr++;
480 	    winCount++;
481 	}
482     }
483 
484     scrollRect.left = x;
485     scrollRect.top = y;
486     scrollRect.right = x + width;
487     scrollRect.bottom = y + height;
488     result = (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn,
489 	    NULL, SW_SCROLLCHILDREN) == NULLREGION) ? 0 : 1;
490 
491     winInfoPtr = winInfo;
492     while (winCount--) {
493 	winPtr = winInfoPtr->winPtr;
494 	hwndChild = TkWinGetHWND(winPtr->window);
495 	GetWindowRect(hwndChild, &childRect);
496 	if (childRect.left != winInfoPtr->x ||
497 		childRect.top != winInfoPtr->y) {
498 	    dbwin("moved window %s %d,%d\n", winPtr->pathName, childRect.left - winInfoPtr->x, childRect.top - winInfoPtr->y);
499 	    winPtr->changes.x += childRect.left - winInfoPtr->x;
500 	    winPtr->changes.y += childRect.top - winInfoPtr->y;
501 	    /* TkDoConfigureNotify(winPtr); */
502 	}
503 	winInfoPtr++;
504     }
505 #else
506     int result = TkScrollWindow(tree->tkwin, gc, x, y, width, height, dx, dy,
507 	damageRgn);
508 #endif
509 
510     return result;
511 }
512 
513 /*
514  *----------------------------------------------------------------------
515  *
516  * Tree_UnsetClipMask --
517  *
518  *	Wrapper around XSetClipMask(). On Win32 Tk_DrawChars() does
519  *	not clear the clipping region.
520  *
521  * Results:
522  *	None.
523  *
524  * Side effects:
525  *	None.
526  *
527  *----------------------------------------------------------------------
528  */
529 
530 void
Tree_UnsetClipMask(TreeCtrl * tree,Drawable drawable,GC gc)531 Tree_UnsetClipMask(
532     TreeCtrl *tree,		/* Widget info. */
533     Drawable drawable,		/* Where to draw. */
534     GC gc			/* Graphics context to modify. */
535     )
536 {
537     XSetClipMask(tree->display, gc, None);
538 
539     /* Tk_DrawChars does not clear the clip region */
540     if (drawable == Tk_WindowId(tree->tkwin)) {
541 	HDC dc;
542 	TkWinDCState dcState;
543 
544 	dc = TkWinGetDrawableDC(tree->display, drawable, &dcState);
545 	SelectClipRgn(dc, NULL);
546 	TkWinReleaseDrawableDC(drawable, dc, &dcState);
547     }
548 }
549 
550 /*
551  *----------------------------------------------------------------------
552  *
553  * Tree_DrawBitmapWithGC --
554  *
555  *	Draw part of a bitmap.
556  *
557  * Results:
558  *	None.
559  *
560  * Side effects:
561  *	Stuff is drawn.
562  *
563  *----------------------------------------------------------------------
564  */
565 
566 void
Tree_DrawBitmapWithGC(TreeCtrl * tree,Pixmap bitmap,Drawable drawable,GC gc,int src_x,int src_y,int width,int height,int dest_x,int dest_y)567 Tree_DrawBitmapWithGC(
568     TreeCtrl *tree,		/* Widget info. */
569     Pixmap bitmap,		/* Bitmap to draw. */
570     Drawable drawable,		/* Where to draw. */
571     GC gc,			/* Graphics context. */
572     int src_x, int src_y,	/* Left and top of part of bitmap to copy. */
573     int width, int height,	/* Width and height of part of bitmap to
574 				 * copy. */
575     int dest_x, int dest_y	/* Left and top coordinates to copy part of
576 				 * the bitmap to. */
577     )
578 {
579     TkpClipMask *clipPtr = (TkpClipMask *) gc->clip_mask;
580 
581     XSetClipOrigin(tree->display, gc, dest_x, dest_y);
582 
583     /*
584      * It seems as though the device context is not set up properly
585      * when drawing a transparent bitmap into a window. Normally Tk draws
586      * into an offscreen pixmap which gets a temporary device context.
587      * This fixes a bug with -doublebuffer none in the demo "Bitmaps".
588      */
589     if (drawable == Tk_WindowId(tree->tkwin)) {
590 	if ((clipPtr != NULL) &&
591 	    (clipPtr->type == TKP_CLIP_PIXMAP) &&
592 	    (clipPtr->value.pixmap == bitmap)) {
593 	    HDC dc;
594 	    TkWinDCState dcState;
595 
596 	    dc = TkWinGetDrawableDC(tree->display, drawable, &dcState);
597 	    SetTextColor(dc, RGB(0,0,0));
598 	    SetBkColor(dc, RGB(255,255,255));
599 	    TkWinReleaseDrawableDC(drawable, dc, &dcState);
600 	}
601     }
602     XCopyPlane(tree->display, bitmap, drawable, gc,
603 	src_x, src_y, (unsigned int) width, (unsigned int) height,
604 	dest_x, dest_y, 1);
605     XSetClipOrigin(tree->display, gc, 0, 0);
606 }
607 
608 /*
609  * TIP #116 altered Tk_PhotoPutBlock API to add interp arg.
610  * We need to remove that for compiling with 8.4.
611  */
612 #if (TK_MAJOR_VERSION == 8) && (TK_MINOR_VERSION < 5)
613 #define TK_PHOTOPUTBLOCK(interp, hdl, blk, x, y, w, h, cr) \
614 	Tk_PhotoPutBlock(hdl, blk, x, y, w, h, cr)
615 #define TK_PHOTOPUTZOOMEDBLOCK(interp, hdl, blk, x, y, w, h, \
616 		zx, zy, sx, sy, cr) \
617 	Tk_PhotoPutZoomedBlock(hdl, blk, x, y, w, h, \
618 		zx, zy, sx, sy, cr)
619 #else
620 #define TK_PHOTOPUTBLOCK	Tk_PhotoPutBlock
621 #define TK_PHOTOPUTZOOMEDBLOCK	Tk_PhotoPutZoomedBlock
622 #endif
623 
624 /*
625  *----------------------------------------------------------------------
626  *
627  * Tree_XImage2Photo --
628  *
629  *	Copy pixels from an XImage to a Tk photo image.
630  *
631  * Results:
632  *	None.
633  *
634  * Side effects:
635  *	The given photo image is blanked and all the pixels from the
636  *	XImage are put into the photo image.
637  *
638  *----------------------------------------------------------------------
639  */
640 
641 void
Tree_XImage2Photo(Tcl_Interp * interp,Tk_PhotoHandle photoH,XImage * ximage,unsigned long trans,int alpha)642 Tree_XImage2Photo(
643     Tcl_Interp *interp,		/* Current interpreter. */
644     Tk_PhotoHandle photoH,	/* Existing photo image. */
645     XImage *ximage,		/* XImage to copy pixels from. */
646     unsigned long trans,	/* Pixel value in ximage that should be
647 				 * considered transparent. */
648     int alpha			/* Desired transparency of photo image.*/
649     )
650 {
651     Tk_PhotoImageBlock photoBlock;
652     unsigned char *pixelPtr;
653     int x, y, w = ximage->width, h = ximage->height;
654 
655     Tk_PhotoBlank(photoH);
656 
657     /* See TkPoscriptImage */
658 
659     pixelPtr = (unsigned char *) Tcl_Alloc(ximage->width * ximage->height * 4);
660     photoBlock.pixelPtr  = pixelPtr;
661     photoBlock.width     = ximage->width;
662     photoBlock.height    = ximage->height;
663     photoBlock.pitch     = ximage->width * 4;
664     photoBlock.pixelSize = 4;
665     photoBlock.offset[0] = 0;
666     photoBlock.offset[1] = 1;
667     photoBlock.offset[2] = 2;
668     photoBlock.offset[3] = 3;
669 
670     for (y = 0; y < ximage->height; y++) {
671 	for (x = 0; x < ximage->width; x++) {
672 	    int r, g, b;
673 	    unsigned long pixel;
674 
675 	    /* FIXME: I think this blows up on classic Mac??? */
676 	    pixel = XGetPixel(ximage, x, y);
677 
678 	    /* Set alpha=0 for transparent pixel in the source XImage */
679 	    if (trans != 0 && pixel == trans) {
680 		pixelPtr[y * photoBlock.pitch + x * 4 + 3] = 0;
681 		continue;
682 	    }
683 
684 	    r = GetRValue(pixel);
685 	    g = GetGValue(pixel);
686 	    b = GetBValue(pixel);
687 
688 	    pixelPtr[y * photoBlock.pitch + x * 4 + 0] = r;
689 	    pixelPtr[y * photoBlock.pitch + x * 4 + 1] = g;
690 	    pixelPtr[y * photoBlock.pitch + x * 4 + 2] = b;
691 	    pixelPtr[y * photoBlock.pitch + x * 4 + 3] = alpha;
692 	}
693     }
694 
695     TK_PHOTOPUTBLOCK(interp, photoH, &photoBlock, 0, 0, w, h,
696 	    TK_PHOTO_COMPOSITE_SET);
697 
698     Tcl_Free((char *) pixelPtr);
699 }
700 
701 typedef struct {
702     TreeCtrl *tree;
703     TreeClip *clip;
704     HDC dc;
705     TkRegion region;
706 } TreeClipStateDC;
707 
708 static void
TreeClip_ToDC(TreeCtrl * tree,TreeClip * clip,HDC dc,TreeClipStateDC * state)709 TreeClip_ToDC(
710     TreeCtrl *tree,		/* Widget info. */
711     TreeClip *clip,		/* Clipping area or NULL. */
712     HDC dc,			/* Windows device context. */
713     TreeClipStateDC *state
714     )
715 {
716     state->tree = tree;
717     state->clip = clip;
718     state->dc = dc;
719     state->region = None;
720 
721     if (clip && clip->type == TREE_CLIP_RECT) {
722 	state->region = Tree_GetRectRegion(tree, &clip->tr);
723 	SelectClipRgn(dc, (HRGN) state->region);
724     }
725     if (clip && clip->type == TREE_CLIP_AREA) {
726 	TreeRectangle tr;
727 	if (Tree_AreaBbox(tree, clip->area, &tr) == 0)
728 	    return;
729 	state->region = Tree_GetRectRegion(tree, &tr);
730 	SelectClipRgn(dc, (HRGN) state->region);
731     }
732     if (clip && clip->type == TREE_CLIP_REGION) {
733 	SelectClipRgn(dc, (HRGN) clip->region);
734     }
735 }
736 
737 static void
TreeClip_FinishDC(TreeClipStateDC * state)738 TreeClip_FinishDC(
739     TreeClipStateDC *state
740     )
741 {
742     SelectClipRgn(state->dc, NULL);
743     if (state->region != NULL)
744 	Tree_FreeRegion(state->tree, state->region);
745 }
746 
747 #if USE_ITEM_PIXMAP == 0
748 /*
749  *----------------------------------------------------------------------
750  *
751  * DrawOrFillArc --
752  *	Copied from tkTwinDraw.c because the clip region is ignored on
753  *	Win32.
754  *
755  *	This function handles the rendering of drawn or filled arcs and
756  *	chords.
757  *
758  * Results:
759  *	None.
760  *
761  * Side effects:
762  *	Renders the requested arc.
763  *
764  *----------------------------------------------------------------------
765  */
766 
767 /*
768  * These macros convert between X's bizarre angle units to radians.
769  */
770 
771 #define PI 3.14159265358979
772 #define XAngleToRadians(a) ((double)(a) / 64 * PI / 180);
773 
774 static void
DrawOrFillArc(TreeCtrl * tree,Drawable d,TreeClip * clip,GC gc,int x,int y,unsigned int width,unsigned int height,int start,int extent,int fill)775 DrawOrFillArc(
776     TreeCtrl *tree,		/* Widget info. */
777     Drawable d,
778     TreeClip *clip,		/* Clipping area or NULL. */
779     GC gc,
780     int x, int y,		/* left top */
781     unsigned int width, unsigned int height,
782     int start,			/* start: three-o'clock (deg*64) */
783     int extent,			/* extent: relative (deg*64) */
784     int fill)			/* ==0 draw, !=0 fill */
785 {
786     HDC dc;
787     HBRUSH brush, oldBrush;
788     HPEN pen, oldPen;
789     TkWinDCState state;
790     TreeClipStateDC clipState;
791     int clockwise = (extent < 0); /* non-zero if clockwise */
792     int xstart, ystart, xend, yend;
793     double radian_start, radian_end, xr, yr;
794 
795     if (d == None) {
796 	return;
797     }
798 
799     dc = TkWinGetDrawableDC(tree->display, d, &state);
800     TreeClip_ToDC(tree, clip, dc, &clipState);
801 
802 /*    SetROP2(dc, tkpWinRopModes[gc->function]);*/
803 
804     /*
805      * Compute the absolute starting and ending angles in normalized radians.
806      * Swap the start and end if drawing clockwise.
807      */
808 
809     start = start % (64*360);
810     if (start < 0) {
811 	start += (64*360);
812     }
813     extent = (start+extent) % (64*360);
814     if (extent < 0) {
815 	extent += (64*360);
816     }
817     if (clockwise) {
818 	int tmp = start;
819 	start = extent;
820 	extent = tmp;
821     }
822     radian_start = XAngleToRadians(start);
823     radian_end = XAngleToRadians(extent);
824 
825     /*
826      * Now compute points on the radial lines that define the starting and
827      * ending angles. Be sure to take into account that the y-coordinate
828      * system is inverted.
829      */
830 
831     xr = x + width / 2.0;
832     yr = y + height / 2.0;
833     xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5);
834     ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5);
835     xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5);
836     yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5);
837 
838     /*
839      * Now draw a filled or open figure. Note that we have to increase the
840      * size of the bounding box by one to account for the difference in pixel
841      * definitions between X and Windows.
842      */
843 
844     pen = CreatePen((int) PS_SOLID, gc->line_width, gc->foreground);
845     oldPen = SelectObject(dc, pen);
846     if (!fill) {
847 	/*
848 	 * Note that this call will leave a gap of one pixel at the end of the
849 	 * arc for thin arcs. We can't use ArcTo because it's only supported
850 	 * under Windows NT.
851 	 */
852 
853 	SetBkMode(dc, TRANSPARENT);
854 	Arc(dc, x, y, (int) (x+width+1), (int) (y+height+1), xstart, ystart,
855 		xend, yend);
856     } else {
857 	brush = CreateSolidBrush(gc->foreground);
858 	oldBrush = SelectObject(dc, brush);
859 	if (gc->arc_mode == ArcChord) {
860 	    Chord(dc, x, y, (int) (x+width+1), (int) (y+height+1),
861 		    xstart, ystart, xend, yend);
862 	} else if (gc->arc_mode == ArcPieSlice) {
863 	    Pie(dc, x, y, (int) (x+width+1), (int) (y+height+1),
864 		    xstart, ystart, xend, yend);
865 	}
866 	DeleteObject(SelectObject(dc, oldBrush));
867     }
868     DeleteObject(SelectObject(dc, oldPen));
869     TreeClip_FinishDC(&clipState);
870     TkWinReleaseDrawableDC(d, dc, &state);
871 }
872 
873 /*
874  *----------------------------------------------------------------------
875  *
876  * Tree_DrawArc --
877  *
878  *	Wrapper around XDrawArc() because the clip region is
879  *	ignored on Win32.
880  *
881  * Results:
882  *	None.
883  *
884  * Side effects:
885  *	Draws onto the specified drawable.
886  *
887  *----------------------------------------------------------------------
888  */
889 
890 void
Tree_DrawArc(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,GC gc,int x,int y,unsigned int width,unsigned int height,int start,int extent)891 Tree_DrawArc(
892     TreeCtrl *tree,		/* Widget info. */
893     TreeDrawable td,		/* Where to draw. */
894     TreeClip *clip,		/* Clipping area or NULL. */
895     GC gc,
896     int x, int y,
897     unsigned int width, unsigned int height,
898     int start, int extent)
899 {
900     tree->display->request++;
901 
902     DrawOrFillArc(tree, td.drawable, clip, gc, x, y, width, height,
903 	start, extent, 0);
904 }
905 
906 /*
907  *----------------------------------------------------------------------
908  *
909  * Tree_FillArc --
910  *
911  *	Wrapper around XFillArc() because the clip region is
912  *	ignored on Win32.
913  *
914  * Results:
915  *	None.
916  *
917  * Side effects:
918  *	Draws onto the specified drawable.
919  *
920  *----------------------------------------------------------------------
921  */
922 
923 void
Tree_FillArc(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,GC gc,int x,int y,unsigned int width,unsigned int height,int start,int extent)924 Tree_FillArc(
925     TreeCtrl *tree,		/* Widget info. */
926     TreeDrawable td,		/* Where to draw. */
927     TreeClip *clip,		/* Clipping area or NULL. */
928     GC gc,
929     int x, int y,
930     unsigned int width, unsigned int height,
931     int start, int extent)
932 {
933     tree->display->request++;
934 
935     DrawOrFillArc(tree, td.drawable, clip, gc, x, y, width, height,
936 	start, extent, 1);
937 }
938 
939 #endif /* USE_ITEM_PIXMAP == 0 */
940 
941 /*
942  *----------------------------------------------------------------------
943  *
944  * Tree_FillRectangle --
945  *
946  *	Wrapper around XFillRectangle() because the clip region is
947  *	ignored on Win32.
948  *
949  * Results:
950  *	None.
951  *
952  * Side effects:
953  *	Draws onto the specified drawable.
954  *
955  *----------------------------------------------------------------------
956  */
957 
958 void
Tree_FillRectangle(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,GC gc,TreeRectangle tr)959 Tree_FillRectangle(
960     TreeCtrl *tree,		/* Widget info. */
961     TreeDrawable td,		/* Where to draw. */
962     TreeClip *clip,		/* Clipping area or NULL. */
963     GC gc,			/* Graphics context. */
964     TreeRectangle tr		/* Rectangle to paint. */
965     )
966 {
967 #if 1
968     HDC dc;
969     TkWinDCState dcState;
970     TreeClipStateDC clipState;
971     RECT rect;
972     COLORREF oldColor;
973 
974     dc = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
975     TreeClip_ToDC(tree, clip, dc, &clipState);
976 
977     rect.left = tr.x, rect.top = tr.y,
978 	rect.right = tr.x + tr.width, rect.bottom = tr.y + tr.height;
979 
980     oldColor = SetBkColor(dc, (COLORREF)gc->foreground);
981     SetBkMode(dc, OPAQUE);
982     ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
983     SetBkColor(dc, oldColor);
984 
985     TreeClip_FinishDC(&clipState);
986     TkWinReleaseDrawableDC(td.drawable, dc, &dcState);
987 #else
988     HDC dc;
989     TkWinDCState dcState;
990     HBRUSH brush;
991     RECT rect;
992     TreeClipStateDC clipState;
993 
994     dc = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
995     TreeClip_ToDC(tree, clip, dc, &clipState);
996 
997     brush = CreateSolidBrush(gc->foreground);
998     rect.left = tr.x, rect.top = tr.y,
999 	rect.right = tr.x + tr.width, rect.bottom = tr.y + tr.height;
1000     FillRect(dc, &rect, brush);
1001 
1002     TreeClip_FinishDC(&clipState);
1003     DeleteObject(brush);
1004     TkWinReleaseDrawableDC(td.drawable, dc, &dcState);
1005 #endif
1006 }
1007 
1008 #if USE_ITEM_PIXMAP == 0
1009 
1010 static void
FastFillRect(HDC dc,int x,int y,int width,int height,COLORREF pixel)1011 FastFillRect(
1012     HDC dc,
1013     int x, int y, int width, int height,
1014     COLORREF pixel
1015     )
1016 {
1017     RECT rect;
1018     COLORREF oldColor;
1019 
1020     rect.left = x, rect.top = y,
1021 	rect.right = x + width, rect.bottom = y + height;
1022 
1023     oldColor = SetBkColor(dc, pixel);
1024     SetBkMode(dc, OPAQUE);
1025     ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
1026     SetBkColor(dc, oldColor);
1027 }
1028 
1029 static void
_3DVerticalBevel(HDC dc,Tk_Window tkwin,Tk_3DBorder border,int x,int y,int width,int height,int leftBevel,int relief)1030 _3DVerticalBevel(
1031     HDC dc,
1032     Tk_Window tkwin,		/* Window for which border was allocated. */
1033     Tk_3DBorder border,		/* Token for border to draw. */
1034     int x, int y, int width, int height,
1035 				/* Area of vertical bevel. */
1036     int leftBevel,		/* Non-zero means this bevel forms the left
1037 				 * side of the object; 0 means it forms the
1038 				 * right side. */
1039     int relief)			/* Kind of bevel to draw. For example,
1040 				 * TK_RELIEF_RAISED means interior of object
1041 				 * should appear higher than exterior. */
1042 {
1043     COLORREF left, right;
1044     int half;
1045 
1046     switch (relief) {
1047     case TK_RELIEF_RAISED:
1048 	left = (leftBevel)
1049 		? TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC)
1050 		: TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1051 	right = (leftBevel)
1052 		? TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT2)
1053 		: TkWinGetBorderPixels(tkwin, border, TK_3D_DARK2);
1054 	break;
1055     case TK_RELIEF_SUNKEN:
1056 	left = (leftBevel)
1057 		? TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC)
1058 		: TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT2);
1059 	right = (leftBevel)
1060 		? TkWinGetBorderPixels(tkwin, border, TK_3D_DARK2)
1061 		: TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1062 	break;
1063     case TK_RELIEF_RIDGE:
1064 	left = TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1065 	right = TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1066 	break;
1067     case TK_RELIEF_GROOVE:
1068 	left = TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1069 	right = TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1070 	break;
1071     case TK_RELIEF_FLAT:
1072 	left = right = TkWinGetBorderPixels(tkwin, border, TK_3D_FLAT_GC);
1073 	break;
1074     case TK_RELIEF_SOLID:
1075     default:
1076 	left = right = RGB(0,0,0);
1077 	break;
1078     }
1079     half = width/2;
1080     if (leftBevel && (width & 1)) {
1081 	half++;
1082     }
1083     FastFillRect(dc, x, y, half, height, left);
1084     FastFillRect(dc, x+half, y, width-half, height, right);
1085 }
1086 
1087 static void
_3DHorizontalBevel(HDC dc,Tk_Window tkwin,Tk_3DBorder border,int x,int y,int width,int height,int leftIn,int rightIn,int topBevel,int relief)1088 _3DHorizontalBevel(
1089     HDC dc,
1090     Tk_Window tkwin,		/* Window for which border was allocated. */
1091     Tk_3DBorder border,		/* Token for border to draw. */
1092     int x, int y, int width, int height,
1093 				/* Bounding box of area of bevel. Height gives
1094 				 * width of border. */
1095     int leftIn, int rightIn,	/* Describes whether the left and right edges
1096 				 * of the bevel angle in or out as they go
1097 				 * down. For example, if "leftIn" is true, the
1098 				 * left side of the bevel looks like this:
1099 				 *	___________
1100 				 *	 __________
1101 				 *	  _________
1102 				 *	   ________
1103 				 */
1104     int topBevel,		/* Non-zero means this bevel forms the top
1105 				 * side of the object; 0 means it forms the
1106 				 * bottom side. */
1107     int relief)			/* Kind of bevel to draw. For example,
1108 				 * TK_RELIEF_RAISED means interior of object
1109 				 * should appear higher than exterior. */
1110 {
1111     int bottom, halfway, x1, x2, x1Delta, x2Delta;
1112     int topColor, bottomColor;
1113 
1114     switch (relief) {
1115     case TK_RELIEF_RAISED:
1116 	topColor = (topBevel)
1117 		? TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC)
1118 		: TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1119 	bottomColor = (topBevel)
1120 		? TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT2)
1121 		: TkWinGetBorderPixels(tkwin, border, TK_3D_DARK2);
1122 	break;
1123     case TK_RELIEF_SUNKEN:
1124 	topColor = (topBevel)
1125 		? TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC)
1126 		: TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT2);
1127 	bottomColor = (topBevel)
1128 		? TkWinGetBorderPixels(tkwin, border, TK_3D_DARK2)
1129 		: TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1130 	break;
1131     case TK_RELIEF_RIDGE:
1132 	topColor = TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1133 	bottomColor = TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1134 	break;
1135     case TK_RELIEF_GROOVE:
1136 	topColor = TkWinGetBorderPixels(tkwin, border, TK_3D_DARK_GC);
1137 	bottomColor = TkWinGetBorderPixels(tkwin, border, TK_3D_LIGHT_GC);
1138 	break;
1139     case TK_RELIEF_FLAT:
1140 	topColor = bottomColor = TkWinGetBorderPixels(tkwin, border, TK_3D_FLAT_GC);
1141 	break;
1142     case TK_RELIEF_SOLID:
1143     default:
1144 	topColor = bottomColor = RGB(0,0,0);
1145     }
1146 
1147     if (leftIn) {
1148 	x1 = x+1;
1149     } else {
1150 	x1 = x+height-1;
1151     }
1152     x2 = x+width;
1153     if (rightIn) {
1154 	x2--;
1155     } else {
1156 	x2 -= height;
1157     }
1158     x1Delta = (leftIn) ? 1 : -1;
1159     x2Delta = (rightIn) ? -1 : 1;
1160     halfway = y + height/2;
1161     if (topBevel && (height & 1)) {
1162 	halfway++;
1163     }
1164     bottom = y + height;
1165 
1166     for ( ; y < bottom; y++) {
1167 	if (x1 < x2) {
1168 	    FastFillRect(dc, x1, y, x2-x1, 1,
1169 		(y < halfway) ? topColor : bottomColor);
1170 	}
1171 	x1 += x1Delta;
1172 	x2 += x2Delta;
1173     }
1174 }
1175 
1176 /*
1177  *----------------------------------------------------------------------
1178  *
1179  * Tree_Draw3DRectangle --
1180  *
1181  *	Reimplementation of Tk_Draw3DRectangle() because the clip
1182  *	region is ignored on Win32.
1183  *
1184  * Results:
1185  *	None.
1186  *
1187  * Side effects:
1188  *	Draws onto the specified drawable.
1189  *
1190  *----------------------------------------------------------------------
1191  */
1192 
1193 void
Tree_Draw3DRectangle(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,Tk_3DBorder border,int x,int y,int width,int height,int borderWidth,int relief)1194 Tree_Draw3DRectangle(
1195     TreeCtrl *tree,		/* Widget info. */
1196     TreeDrawable td,		/* Where to draw. */
1197     TreeClip *clip,		/* Clipping area or NULL. */
1198     Tk_3DBorder border,		/* Token for border to draw. */
1199     int x, int y, int width, int height,
1200 				/* Outside area of region in which border will
1201 				 * be drawn. */
1202     int borderWidth,		/* Desired width for border, in pixels. */
1203     int relief			/* Type of relief: TK_RELIEF_RAISED,
1204 				 * TK_RELIEF_SUNKEN, TK_RELIEF_GROOVE, etc. */
1205     )
1206 {
1207     Tk_Window tkwin = tree->tkwin;
1208     HDC dc;
1209     TkWinDCState dcState;
1210     TreeClipStateDC clipState;
1211 
1212     dc = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
1213     TreeClip_ToDC(tree, clip, dc, &clipState);
1214 
1215     if (width < 2*borderWidth) {
1216 	borderWidth = width/2;
1217     }
1218     if (height < 2*borderWidth) {
1219 	borderWidth = height/2;
1220     }
1221     _3DVerticalBevel(dc, tkwin, border, x, y, borderWidth, height,
1222 	    1, relief);
1223     _3DVerticalBevel(dc, tkwin, border, x+width-borderWidth, y,
1224 	    borderWidth, height, 0, relief);
1225     _3DHorizontalBevel(dc, tkwin, border, x, y, width, borderWidth,
1226 	    1, 1, 1, relief);
1227     _3DHorizontalBevel(dc, tkwin, border, x, y+height-borderWidth,
1228 	    width, borderWidth, 0, 0, 0, relief);
1229 
1230     TreeClip_FinishDC(&clipState);
1231     TkWinReleaseDrawableDC(td.drawable, dc, &dcState);
1232 }
1233 
1234 /*
1235  *----------------------------------------------------------------------
1236  *
1237  * Tree_Fill3DRectangle --
1238  *
1239  *	Reimplementation of Tree_Fill3DRectangle() because the clip
1240  *	region is ignored on Win32.
1241  *
1242  * Results:
1243  *	None.
1244  *
1245  * Side effects:
1246  *	Draws onto the specified drawable.
1247  *
1248  *----------------------------------------------------------------------
1249  */
1250 
1251 void
Tree_Fill3DRectangle(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,Tk_3DBorder border,int x,int y,int width,int height,int borderWidth,int relief)1252 Tree_Fill3DRectangle(
1253     TreeCtrl *tree,		/* Widget info. */
1254     TreeDrawable td,		/* Where to draw. */
1255     TreeClip *clip,		/* Clipping area or NULL. */
1256     Tk_3DBorder border,		/* Token for border to draw. */
1257     int x, int y, int width, int height,
1258 				/* Outside area of rectangular region. */
1259     int borderWidth,		/* Desired width for border, in pixels. Border
1260 				 * will be *inside* region. */
1261     int relief			/* Indicates 3D effect: TK_RELIEF_FLAT,
1262 				 * TK_RELIEF_RAISED, or TK_RELIEF_SUNKEN. */
1263     )
1264 {
1265     Tk_Window tkwin = tree->tkwin;
1266     HDC dc;
1267     TkWinDCState dcState;
1268     TreeClipStateDC clipState;
1269     int doubleBorder;
1270 
1271     dc = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
1272     TreeClip_ToDC(tree, clip, dc, &clipState);
1273 
1274     if (relief == TK_RELIEF_FLAT) {
1275 	borderWidth = 0;
1276     } else {
1277 	if (width < 2*borderWidth) {
1278 	    borderWidth = width/2;
1279 	}
1280 	if (height < 2*borderWidth) {
1281 	    borderWidth = height/2;
1282 	}
1283     }
1284     doubleBorder = 2*borderWidth;
1285 
1286     if ((width > doubleBorder) && (height > doubleBorder)) {
1287 	FastFillRect(dc,
1288 		x + borderWidth, y + borderWidth,
1289 		(unsigned int) (width - doubleBorder),
1290 		(unsigned int) (height - doubleBorder),
1291 		TkWinGetBorderPixels(tkwin, border, TK_3D_FLAT_GC));
1292     }
1293 
1294     TreeClip_FinishDC(&clipState);
1295     TkWinReleaseDrawableDC(td.drawable, dc, &dcState);
1296 
1297     if (borderWidth) {
1298 	Tree_Draw3DRectangle(tree, td, clip, border, x, y, width,
1299 		height, borderWidth, relief);
1300     }
1301 }
1302 
1303 #endif /* USE_ITEM_PIXMAP == 0 */
1304 
1305 /*** Themes ***/
1306 
1307 #include <uxtheme.h>
1308 #ifdef __MINGW32__
1309 #include <tmschema.h>
1310 #else /* __MING32__ */
1311 /* <vsstyle.h> is part of the Windows SDK but not the Platform SDK. */
1312 /*#include <vsstyle.h>*/
1313 #ifndef HP_HEADERITEM
1314 #define HP_HEADERITEM 1
1315 #define HIS_NORMAL 1
1316 #define HIS_HOT 2
1317 #define HIS_PRESSED 3
1318 #define TVP_GLYPH 2
1319 #define TVP_HOTGLYPH 4
1320 #define GLPS_CLOSED 1
1321 #define GLPS_OPENED 2
1322 #define HGLPS_CLOSED 1
1323 #define HGLPS_OPENED 2
1324 #endif /* HP_HEADERITEM */
1325 #endif /* __MING32__ */
1326 #include <shlwapi.h>
1327 #include <basetyps.h> /* MingW32 */
1328 
1329 #ifdef __MINGW32__
1330 /* vsstyle.h */
1331 #define TVP_HOTGLYPH 4
1332 #define HGLPS_CLOSED 1
1333 #define HGLPS_OPENED 2
1334 #endif
1335 
1336 #ifndef TMT_CONTENTMARGINS
1337 #define TMT_CONTENTMARGINS 3602
1338 #endif
1339 
1340 typedef HTHEME (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd,
1341     LPCWSTR pszClassList);
1342 typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme);
1343 typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme,
1344     HDC hdc, int iPartId, int iStateId, const RECT *pRect,
1345     OPTIONAL const RECT *pClipRect);
1346 typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundExProc)(HTHEME hTheme,
1347     HDC hdc, int iPartId, int iStateId, const RECT *pRect,
1348     DTBGOPTS *PDTBGOPTS);
1349 typedef HRESULT (STDAPICALLTYPE DrawThemeParentBackgroundProc)(HWND hwnd,
1350     HDC hdc, OPTIONAL const RECT *prc);
1351 typedef HRESULT (STDAPICALLTYPE DrawThemeEdgeProc)(HTHEME hTheme, HDC hdc,
1352     int iPartId, int iStateId, const RECT *pDestRect,
1353     UINT uEdge, UINT uFlags, RECT *pContentRect);
1354 typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc,
1355     int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
1356     DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
1357 typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundContentRectProc)(
1358     HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
1359     const RECT *pBoundingRect, RECT *pContentRect);
1360 typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundExtentProc)(HTHEME hTheme,
1361     HDC hdc, int iPartId, int iStateId, const RECT *pContentRect,
1362     RECT *pExtentRect);
1363 typedef HRESULT (STDAPICALLTYPE GetThemeMarginsProc)(HTHEME, HDC,
1364     int iPartId, int iStateId, int iPropId, OPTIONAL RECT *prc,
1365     MARGINS *pMargins);
1366 typedef HRESULT (STDAPICALLTYPE GetThemePartSizeProc)(HTHEME, HDC, int iPartId,
1367     int iStateId, RECT *prc, enum THEMESIZE eSize, SIZE *psz);
1368 typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc,
1369     int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
1370     DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtentRect);
1371 typedef BOOL (STDAPICALLTYPE IsThemeActiveProc)(VOID);
1372 typedef BOOL (STDAPICALLTYPE IsAppThemedProc)(VOID);
1373 typedef BOOL (STDAPICALLTYPE IsThemePartDefinedProc)(HTHEME, int, int);
1374 typedef HRESULT (STDAPICALLTYPE IsThemeBackgroundPartiallyTransparentProc)(
1375     HTHEME, int, int);
1376 typedef HRESULT (STDAPICALLTYPE SetWindowThemeProc)(HWND, LPCWSTR, LPCWSTR);
1377 
1378 typedef struct
1379 {
1380     OpenThemeDataProc				*OpenThemeData;
1381     CloseThemeDataProc				*CloseThemeData;
1382     DrawThemeBackgroundProc			*DrawThemeBackground;
1383     DrawThemeBackgroundExProc			*DrawThemeBackgroundEx;
1384     DrawThemeParentBackgroundProc		*DrawThemeParentBackground;
1385     DrawThemeEdgeProc				*DrawThemeEdge;
1386     DrawThemeTextProc				*DrawThemeText;
1387     GetThemeBackgroundContentRectProc		*GetThemeBackgroundContentRect;
1388     GetThemeBackgroundExtentProc		*GetThemeBackgroundExtent;
1389     GetThemeMarginsProc				*GetThemeMargins;
1390     GetThemePartSizeProc			*GetThemePartSize;
1391     GetThemeTextExtentProc			*GetThemeTextExtent;
1392     IsThemeActiveProc				*IsThemeActive;
1393     IsAppThemedProc				*IsAppThemed;
1394     IsThemePartDefinedProc			*IsThemePartDefined;
1395     IsThemeBackgroundPartiallyTransparentProc 	*IsThemeBackgroundPartiallyTransparent;
1396     SetWindowThemeProc				*SetWindowTheme;
1397 } XPThemeProcs;
1398 
1399 typedef struct
1400 {
1401     HINSTANCE hlibrary;
1402     XPThemeProcs *procs;
1403     int registered;
1404     int themeEnabled;
1405 } XPThemeData;
1406 
1407 typedef struct TreeThemeData_
1408 {
1409     HTHEME hThemeHEADER;
1410     HTHEME hThemeTREEVIEW;
1411     SIZE buttonOpen;
1412     SIZE buttonClosed;
1413 } TreeThemeData_;
1414 
1415 static XPThemeProcs *procs = NULL;
1416 static XPThemeData *appThemeData = NULL;
1417 TCL_DECLARE_MUTEX(themeMutex)
1418 
1419 /* Functions imported from kernel32.dll requiring windows XP or greater. */
1420 /* But I already link to GetVersionEx so is this importing needed? */
1421 typedef HANDLE (STDAPICALLTYPE CreateActCtxAProc)(PCACTCTXA pActCtx);
1422 typedef BOOL (STDAPICALLTYPE ActivateActCtxProc)(HANDLE hActCtx, ULONG_PTR *lpCookie);
1423 typedef BOOL (STDAPICALLTYPE DeactivateActCtxProc)(DWORD dwFlags, ULONG_PTR ulCookie);
1424 typedef VOID (STDAPICALLTYPE ReleaseActCtxProc)(HANDLE hActCtx);
1425 
1426 typedef struct
1427 {
1428     CreateActCtxAProc *CreateActCtxA;
1429     ActivateActCtxProc *ActivateActCtx;
1430     DeactivateActCtxProc *DeactivateActCtx;
1431     ReleaseActCtxProc *ReleaseActCtx;
1432 } ActCtxProcs;
1433 
1434 static ActCtxProcs *
GetActCtxProcs(void)1435 GetActCtxProcs(void)
1436 {
1437     HINSTANCE hInst;
1438     ActCtxProcs *procs = (ActCtxProcs *) ckalloc(sizeof(ActCtxProcs));
1439 
1440     hInst = LoadLibrary("kernel32.dll"); /* FIXME: leak? */
1441     if (hInst != 0)
1442     {
1443  #define LOADPROC(name) \
1444 	(0 != (procs->name = (name ## Proc *)GetProcAddress(hInst, #name) ))
1445 
1446 	if (LOADPROC(CreateActCtxA) &&
1447 	    LOADPROC(ActivateActCtx) &&
1448 	    LOADPROC(DeactivateActCtx) &&
1449 	    LOADPROC(ReleaseActCtx))
1450 	{
1451 	    return procs;
1452 	}
1453 
1454 #undef LOADPROC
1455     }
1456 
1457     ckfree((char*)procs);
1458     return NULL;
1459 }
1460 
1461 static HMODULE thisModule = NULL;
1462 
1463 /* Return the HMODULE for this treectrl.dll. */
1464 static HMODULE
GetMyHandle(void)1465 GetMyHandle(void)
1466 {
1467 #if 1
1468     return thisModule;
1469 #else
1470     HMODULE hModule = NULL;
1471 
1472     /* FIXME: Only >=NT so I shouldn't link to it? But I already linked to
1473      * GetVersionEx so will it run on 95/98? */
1474     GetModuleHandleEx(
1475 	GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
1476 	(LPCTSTR)&appThemeData,
1477 	&hModule);
1478     return hModule;
1479 #endif
1480 }
1481 
1482 BOOL WINAPI
DllMain(HINSTANCE hInst,DWORD reason,LPVOID reserved)1483 DllMain(
1484     HINSTANCE hInst,	/* Library instance handle. */
1485     DWORD reason,	/* Reason this function is being called. */
1486     LPVOID reserved)	/* Not used. */
1487 {
1488     if (reason == DLL_PROCESS_ATTACH) {
1489 	thisModule = (HMODULE) hInst;
1490     }
1491     return TRUE;
1492 }
1493 
1494 static HANDLE
ActivateManifestContext(ActCtxProcs * procs,ULONG_PTR * ulpCookie)1495 ActivateManifestContext(ActCtxProcs *procs, ULONG_PTR *ulpCookie)
1496 {
1497     ACTCTXA actctx;
1498     HANDLE hCtx;
1499 #if 1
1500     char myPath[1024];
1501     DWORD len;
1502 
1503     if (procs == NULL)
1504 	return INVALID_HANDLE_VALUE;
1505 
1506     len = GetModuleFileName(GetMyHandle(),myPath,1024);
1507     myPath[len] = 0;
1508 
1509     ZeroMemory(&actctx, sizeof(actctx));
1510     actctx.cbSize = sizeof(actctx);
1511     actctx.lpSource = myPath;
1512     actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
1513 #else
1514 
1515     if (procs == NULL)
1516 	return INVALID_HANDLE_VALUE;
1517 
1518     ZeroMemory(&actctx, sizeof(actctx));
1519     actctx.cbSize = sizeof(actctx);
1520     actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID;
1521     actctx.hModule = GetMyHandle();
1522 #endif
1523     actctx.lpResourceName = MAKEINTRESOURCE(2);
1524 
1525     hCtx = procs->CreateActCtxA(&actctx);
1526     if (hCtx == INVALID_HANDLE_VALUE)
1527     {
1528 	char msg[1024];
1529 	DWORD err = GetLastError();
1530 	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
1531 		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)msg,
1532 		sizeof(msg), 0);
1533 	return INVALID_HANDLE_VALUE;
1534     }
1535 
1536     if (procs->ActivateActCtx(hCtx, ulpCookie))
1537 	return hCtx;
1538 
1539     return INVALID_HANDLE_VALUE;
1540 }
1541 
1542 static void
DeactivateManifestContext(ActCtxProcs * procs,HANDLE hCtx,ULONG_PTR ulpCookie)1543 DeactivateManifestContext(ActCtxProcs *procs, HANDLE hCtx, ULONG_PTR ulpCookie)
1544 {
1545     if (procs == NULL)
1546 	return;
1547 
1548     if (hCtx != INVALID_HANDLE_VALUE)
1549     {
1550 	procs->DeactivateActCtx(0, ulpCookie);
1551 	procs->ReleaseActCtx(hCtx);
1552     }
1553 
1554     ckfree((char*)procs);
1555 }
1556 
1557 /* http://www.manbu.net/Lib/En/Class5/Sub16/1/29.asp */
1558 static int
ComCtlVersionOK(void)1559 ComCtlVersionOK(void)
1560 {
1561     HINSTANCE handle;
1562     typedef HRESULT (STDAPICALLTYPE DllGetVersionProc)(DLLVERSIONINFO *);
1563     DllGetVersionProc *pDllGetVersion;
1564     int result = FALSE;
1565     ActCtxProcs *procs;
1566     HANDLE hCtx;
1567     ULONG_PTR ulpCookie;
1568 
1569     procs = GetActCtxProcs();
1570     hCtx = ActivateManifestContext(procs, &ulpCookie);
1571     handle = LoadLibrary("comctl32.dll");
1572     DeactivateManifestContext(procs, hCtx, ulpCookie);
1573     if (handle == NULL)
1574 	return FALSE;
1575     pDllGetVersion = (DllGetVersionProc *) GetProcAddress(handle,
1576 	    "DllGetVersion");
1577     if (pDllGetVersion != NULL) {
1578 	DLLVERSIONINFO dvi;
1579 
1580 	memset(&dvi, '\0', sizeof(dvi));
1581 	dvi.cbSize = sizeof(dvi);
1582 	if ((*pDllGetVersion)(&dvi) == NOERROR)
1583 	    result = dvi.dwMajorVersion >= 6;
1584     }
1585     FreeLibrary(handle);
1586     return result;
1587 }
1588 
1589 /*
1590  *----------------------------------------------------------------------
1591  *
1592  * LoadXPThemeProcs --
1593  *	Initialize XP theming support.
1594  *
1595  *	XP theme support is included in UXTHEME.DLL
1596  *	We dynamically load this DLL at runtime instead of linking
1597  *	to it at build-time.
1598  *
1599  * Returns:
1600  *	A pointer to an XPThemeProcs table if successful, NULL otherwise.
1601  *----------------------------------------------------------------------
1602  */
1603 
1604 static XPThemeProcs *
LoadXPThemeProcs(HINSTANCE * phlib)1605 LoadXPThemeProcs(HINSTANCE *phlib)
1606 {
1607     OSVERSIONINFO os;
1608 
1609     /*
1610      * We have to check whether we are running at least on Windows XP.
1611      * In order to determine this we call GetVersionEx directly, although
1612      * it would be a good idea to wrap it inside a function similar to
1613      * TkWinGetPlatformId...
1614      */
1615     ZeroMemory(&os, sizeof(os));
1616     os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
1617     GetVersionEx(&os);
1618     if ((os.dwMajorVersion >= 5 && os.dwMinorVersion >= 1) ||
1619 	    (os.dwMajorVersion > 5)) {
1620 	/*
1621 	 * We are running under Windows XP or a newer version.
1622 	 * Load the library "uxtheme.dll", where the native widget
1623 	 * drawing routines are implemented.
1624 	 */
1625 	HINSTANCE handle;
1626 	*phlib = handle = LoadLibrary("uxtheme.dll");
1627 	if (handle != 0) {
1628 	    /*
1629 	     * We have successfully loaded the library. Proceed in storing the
1630 	     * addresses of the functions we want to use.
1631 	     */
1632 	    XPThemeProcs *procs = (XPThemeProcs*)ckalloc(sizeof(XPThemeProcs));
1633 #define LOADPROC(name) \
1634 	(0 != (procs->name = (name ## Proc *)GetProcAddress(handle, #name) ))
1635 
1636 	    if (   LOADPROC(OpenThemeData)
1637 		&& LOADPROC(CloseThemeData)
1638 		&& LOADPROC(DrawThemeBackground)
1639 		&& LOADPROC(DrawThemeBackgroundEx)
1640 		&& LOADPROC(DrawThemeParentBackground)
1641 		&& LOADPROC(DrawThemeEdge)
1642 		&& LOADPROC(DrawThemeText)
1643 		&& LOADPROC(GetThemeBackgroundContentRect)
1644 		&& LOADPROC(GetThemeBackgroundExtent)
1645 		&& LOADPROC(GetThemeMargins)
1646 		&& LOADPROC(GetThemePartSize)
1647 		&& LOADPROC(GetThemeTextExtent)
1648 		&& LOADPROC(IsThemeActive)
1649 		&& LOADPROC(IsAppThemed)
1650 		&& LOADPROC(IsThemePartDefined)
1651 		&& LOADPROC(IsThemeBackgroundPartiallyTransparent)
1652 		&& LOADPROC(SetWindowTheme)
1653 		&& ComCtlVersionOK()
1654 	    ) {
1655 		return procs;
1656 	    }
1657 #undef LOADPROC
1658 	    ckfree((char*)procs);
1659 	}
1660     }
1661     return 0;
1662 }
1663 
1664 /*
1665  *----------------------------------------------------------------------
1666  *
1667  * TreeTheme_DrawHeaderItem --
1668  *
1669  *	Draws the background of a single column header.  On Mac OS X
1670  *	this also draws the sort arrow, if any.
1671  *
1672  * Results:
1673  *	TCL_OK if drawing occurred, TCL_ERROR if the X11 fallback
1674  *	should be used.
1675  *
1676  * Side effects:
1677  *	Drawing.
1678  *
1679  *----------------------------------------------------------------------
1680  */
1681 
1682 int
TreeTheme_DrawHeaderItem(TreeCtrl * tree,TreeDrawable td,int state,int arrow,int visIndex,int x,int y,int width,int height)1683 TreeTheme_DrawHeaderItem(
1684     TreeCtrl *tree,		/* Widget info. */
1685     TreeDrawable td,		/* Where to draw. */
1686     int state,			/* COLUMN_STATE_xxx flags. */
1687     int arrow,			/* COLUMN_ARROW_xxx flags. */
1688     int visIndex,		/* 0-based index in list of visible columns. */
1689     int x, int y,		/* Bounds of the header. */
1690     int width, int height	/* Bounds of the header. */
1691     )
1692 {
1693     HTHEME hTheme;
1694     HDC hDC;
1695     TkWinDCState dcState;
1696     RECT rc;
1697     HRESULT hr;
1698 
1699     int iPartId = HP_HEADERITEM;
1700     int iStateId = HIS_NORMAL;
1701 
1702     switch (state) {
1703 	case COLUMN_STATE_ACTIVE:  iStateId = HIS_HOT; break;
1704 	case COLUMN_STATE_PRESSED: iStateId = HIS_PRESSED; break;
1705     }
1706 
1707     if (!appThemeData->themeEnabled || !procs)
1708 	return TCL_ERROR;
1709 
1710     hTheme = tree->themeData->hThemeHEADER;
1711     if (!hTheme)
1712 	return TCL_ERROR;
1713 
1714 #if 0 /* Always returns FALSE */
1715     if (!procs->IsThemePartDefined(
1716 	hTheme,
1717 	iPartId,
1718 	iStateId)) {
1719 	return TCL_ERROR;
1720     }
1721 #endif
1722 
1723     rc.left = x;
1724     rc.top = y;
1725     rc.right = x + width;
1726     rc.bottom = y + height;
1727 
1728     /* Is transparent for the default XP style. */
1729     if (procs->IsThemeBackgroundPartiallyTransparent(
1730 	hTheme,
1731 	iPartId,
1732 	iStateId)) {
1733 #if 1
1734 	/* What color should I use? */
1735 	Tk_Fill3DRectangle(tree->tkwin, td.drawable, tree->border, x, y, width, height, 0, TK_RELIEF_FLAT);
1736 #else
1737 	/* This draws nothing, maybe because the parent window is not
1738 	 * themed */
1739 	procs->DrawThemeParentBackground(
1740 	    hwnd,
1741 	    hDC,
1742 	    &rc);
1743 #endif
1744     }
1745 
1746     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
1747 
1748 #if 0
1749     {
1750 	/* Default XP theme gives rect 3 pixels narrower than rc */
1751 	RECT contentRect, extentRect;
1752 	hr = procs->GetThemeBackgroundContentRect(
1753 	    hTheme,
1754 	    hDC,
1755 	    iPartId,
1756 	    iStateId,
1757 	    &rc,
1758 	    &contentRect
1759 	);
1760 	dbwin("GetThemeBackgroundContentRect width=%d height=%d\n",
1761 	    contentRect.right - contentRect.left,
1762 	    contentRect.bottom - contentRect.top);
1763 
1764 	/* Gives rc */
1765 	hr = procs->GetThemeBackgroundExtent(
1766 	    hTheme,
1767 	    hDC,
1768 	    iPartId,
1769 	    iStateId,
1770 	    &contentRect,
1771 	    &extentRect
1772 	);
1773 	dbwin("GetThemeBackgroundExtent width=%d height=%d\n",
1774 	    extentRect.right - extentRect.left,
1775 	    extentRect.bottom - extentRect.top);
1776     }
1777 #endif
1778 
1779     hr = procs->DrawThemeBackground(
1780 	hTheme,
1781 	hDC,
1782 	iPartId,
1783 	iStateId,
1784 	&rc,
1785 	NULL);
1786 
1787     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
1788 
1789     if (hr != S_OK)
1790 	return TCL_ERROR;
1791 
1792     return TCL_OK;
1793 }
1794 
1795 /*
1796  *----------------------------------------------------------------------
1797  *
1798  * TreeTheme_GetHeaderContentMargins --
1799  *
1800  *	Returns the padding inside the column header borders where
1801  *	text etc may be displayed.
1802  *
1803  * Results:
1804  *	TCL_OK if 'bounds' was set, TCL_ERROR otherwise.
1805  *
1806  * Side effects:
1807  *	None.
1808  *
1809  *----------------------------------------------------------------------
1810  */
1811 
1812 int
TreeTheme_GetHeaderContentMargins(TreeCtrl * tree,int state,int arrow,int bounds[4])1813 TreeTheme_GetHeaderContentMargins(
1814     TreeCtrl *tree,		/* Widget info. */
1815     int state,			/* COLUMN_STATE_xxx flags. */
1816     int arrow,			/* COLUMN_ARROW_xxx flags. */
1817     int bounds[4]		/* Returned left-top-right-bottom padding. */
1818     )
1819 {
1820     Window win = Tk_WindowId(tree->tkwin);
1821     HTHEME hTheme;
1822     HDC hDC;
1823     TkWinDCState dcState;
1824     HRESULT hr;
1825     MARGINS margins;
1826 
1827     int iPartId = HP_HEADERITEM;
1828     int iStateId = HIS_NORMAL;
1829 
1830     switch (state) {
1831 	case COLUMN_STATE_ACTIVE:  iStateId = HIS_HOT; break;
1832 	case COLUMN_STATE_PRESSED: iStateId = HIS_PRESSED; break;
1833     }
1834 
1835     if (!appThemeData->themeEnabled || !procs)
1836 	return TCL_ERROR;
1837 
1838     hTheme = tree->themeData->hThemeHEADER;
1839     if (!hTheme)
1840 	return TCL_ERROR;
1841 
1842     hDC = TkWinGetDrawableDC(tree->display, win, &dcState);
1843 
1844     /* The default XP themes give 3,0,0,0 which makes little sense since
1845      * it is the *right* side that should not be drawn over by text; the
1846      * 2-pixel wide header divider is on the right */
1847     hr = procs->GetThemeMargins(
1848 	hTheme,
1849 	hDC,
1850 	iPartId,
1851 	iStateId,
1852 	TMT_CONTENTMARGINS,
1853 	NULL,
1854 	&margins);
1855 
1856     TkWinReleaseDrawableDC(win, hDC, &dcState);
1857 
1858     if (hr != S_OK)
1859 	return TCL_ERROR;
1860 
1861     bounds[0] = margins.cxLeftWidth;
1862     bounds[1] = margins.cyTopHeight;
1863     bounds[2] = margins.cxRightWidth;
1864     bounds[3] = margins.cyBottomHeight;
1865 /*
1866 dbwin("margins %d %d %d %d\n", bounds[0], bounds[1], bounds[2], bounds[3]);
1867 */
1868     return TCL_OK;
1869 }
1870 
1871 /*
1872  *----------------------------------------------------------------------
1873  *
1874  * TreeTheme_DrawHeaderArrow --
1875  *
1876  *	Draws the sort arrow in a column header.
1877  *
1878  * Results:
1879  *	TCL_OK if drawing occurred, TCL_ERROR if the X11 fallback
1880  *	should be used.
1881  *
1882  * Side effects:
1883  *	Drawing.
1884  *
1885  *----------------------------------------------------------------------
1886  */
1887 
1888 int
TreeTheme_DrawHeaderArrow(TreeCtrl * tree,TreeDrawable td,int state,int up,int x,int y,int width,int height)1889 TreeTheme_DrawHeaderArrow(
1890     TreeCtrl *tree,		/* Widget info. */
1891     TreeDrawable td,		/* Where to draw. */
1892     int state,			/* COLUMN_STATE_xxx flags. */
1893     int up,			/* TRUE if up arrow, FALSE otherwise. */
1894     int x, int y,		/* Bounds of arrow.  Width and */
1895     int width, int height	/* height are the same as that returned */
1896 				/* by TreeTheme_GetArrowSize(). */
1897     )
1898 {
1899 #define THEME_ARROW 0
1900 #if THEME_ARROW==0
1901     XColor *color;
1902     GC gc;
1903     int i;
1904 
1905     if (!appThemeData->themeEnabled || !procs)
1906 	return TCL_ERROR;
1907 
1908     color = Tk_GetColor(tree->interp, tree->tkwin, "#ACA899");
1909     gc = Tk_GCForColor(color, td.drawable);
1910 
1911     if (up) {
1912 	for (i = 0; i < height; i++) {
1913 	    XDrawLine(tree->display, td.drawable, gc,
1914 		x + width / 2 - i, y + i,
1915 		x + width / 2 + i + 1, y + i);
1916 	}
1917     } else {
1918 	for (i = 0; i < height; i++) {
1919 	    XDrawLine(tree->display, td.drawable, gc,
1920 		x + width / 2 - i, y + (height - 1) - i,
1921 		x + width / 2 + i + 1, y + (height - 1) - i);
1922 	}
1923     }
1924 
1925     Tk_FreeColor(color);
1926     return TCL_OK;
1927 #else
1928     /* Doesn't seem that Microsoft actually implemented this */
1929     Window win = Tk_WindowId(tree->tkwin);
1930     HWND hwnd = Tk_GetHWND(win);
1931     HTHEME hTheme;
1932     HDC hDC;
1933     TkWinDCState dcState;
1934     RECT rc;
1935     HRESULT hr;
1936 
1937     int iPartId = HP_HEADERSORTARROW;
1938     int iStateId = up ? HSAS_SORTEDUP : HSAS_SORTEDDOWN;
1939 
1940     if (!appThemeData->themeEnabled || !procs)
1941 	return TCL_ERROR;
1942 
1943     hTheme = tree->themeData->hThemeHEADER;
1944     if (!hTheme)
1945 	return TCL_ERROR;
1946 
1947     if (!procs->IsThemePartDefined(
1948 	hTheme,
1949 	iPartId,
1950 	iStateId)) {
1951 	return TCL_ERROR;
1952     }
1953 
1954     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
1955 
1956     rc.left = x;
1957     rc.top = y;
1958     rc.right = x + width;
1959     rc.bottom = y + height;
1960 
1961     hr = procs->DrawThemeBackground(
1962 	hTheme,
1963 	hDC,
1964 	iPartId,
1965 	iStateId,
1966 	&rc,
1967 	NULL);
1968 
1969     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
1970     return TCL_OK;
1971 #endif /* THEME_ARROW==1 */
1972 }
1973 
1974 /*
1975  *----------------------------------------------------------------------
1976  *
1977  * TreeTheme_DrawButton --
1978  *
1979  *	Draws a single expand/collapse item button.
1980  *
1981  * Results:
1982  *	TCL_OK if drawing occurred, TCL_ERROR if the X11 fallback
1983  *	should be used.
1984  *
1985  * Side effects:
1986  *	Drawing.
1987  *
1988  *----------------------------------------------------------------------
1989  */
1990 
1991 int
TreeTheme_DrawButton(TreeCtrl * tree,TreeDrawable td,TreeItem item,int state,int x,int y,int width,int height)1992 TreeTheme_DrawButton(
1993     TreeCtrl *tree,		/* Widget info. */
1994     TreeDrawable td,		/* Where to draw. */
1995     TreeItem item,		/* Needed for animating. */
1996     int state,			/* STATE_xxx | BUTTON_STATE_xxx flags. */
1997     int x, int y,		/* Bounds of the button.  Width and height */
1998     int width, int height	/* are the same as that returned by */
1999 				/* TreeTheme_GetButtonSize(). */
2000     )
2001 {
2002     int open = state & STATE_ITEM_OPEN;
2003     int active = state & (BUTTON_STATE_ACTIVE|BUTTON_STATE_PRESSED); /* windows theme has no "pressed" state */
2004     HTHEME hTheme;
2005     HDC hDC;
2006     TkWinDCState dcState;
2007     RECT rc;
2008     HRESULT hr;
2009     int iPartId, iStateId;
2010 
2011     if (!appThemeData->themeEnabled || !procs)
2012 	return TCL_ERROR;
2013 
2014     hTheme = tree->themeData->hThemeTREEVIEW;
2015     if (!hTheme)
2016 	return TCL_ERROR;
2017 
2018     /* On Win7 IsThemePartDefined(TVP_HOTGLYPH) correctly returns
2019      * TRUE when SetWindowTheme("explorer") is called and FALSE when it
2020      * wasn't called. */
2021     if (active && procs->IsThemePartDefined(hTheme, TVP_HOTGLYPH, 0)) {
2022 	iPartId  = TVP_HOTGLYPH;
2023 	iStateId = open ? HGLPS_OPENED : HGLPS_CLOSED;
2024     } else {
2025 	iPartId  = TVP_GLYPH;
2026 	iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
2027     }
2028 
2029 #if 0 /* Always returns FALSE */
2030     if (!procs->IsThemePartDefined(
2031 	hTheme,
2032 	iPartId,
2033 	iStateId)) {
2034 	return TCL_ERROR;
2035     }
2036 #endif
2037 
2038     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
2039 
2040     rc.left = x;
2041     rc.top = y;
2042     rc.right = x + width;
2043     rc.bottom = y + height;
2044     hr = procs->DrawThemeBackground(
2045 	hTheme,
2046 	hDC,
2047 	iPartId,
2048 	iStateId,
2049 	&rc,
2050 	NULL);
2051 
2052     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
2053 
2054     if (hr != S_OK)
2055 	return TCL_ERROR;
2056 
2057     return TCL_OK;
2058 }
2059 
2060 /*
2061  *----------------------------------------------------------------------
2062  *
2063  * TreeTheme_GetButtonSize --
2064  *
2065  *	Returns the width and height of an expand/collapse item button.
2066  *
2067  * Results:
2068  *	TCL_OK if *widthPtr and *heightPtr were set, TCL_ERROR
2069  *	if themed buttons can't be drawn.
2070  *
2071  * Side effects:
2072  *	None.
2073  *
2074  *----------------------------------------------------------------------
2075  */
2076 
2077 int
TreeTheme_GetButtonSize(TreeCtrl * tree,Drawable drawable,int open,int * widthPtr,int * heightPtr)2078 TreeTheme_GetButtonSize(
2079     TreeCtrl *tree,		/* Widget info. */
2080     Drawable drawable,		/* Needed on MS Windows. */
2081     int open,			/* TRUE if expanded button. */
2082     int *widthPtr,		/* Returned width of button. */
2083     int *heightPtr		/* Returned height of button. */
2084     )
2085 {
2086     TreeThemeData themeData = tree->themeData;
2087     HTHEME hTheme;
2088     HDC hDC;
2089     TkWinDCState dcState;
2090     HRESULT hr;
2091     SIZE size;
2092     int iPartId, iStateId;
2093 
2094     if (!appThemeData->themeEnabled || !procs)
2095 	return TCL_ERROR;
2096 
2097     /* Use cached values */
2098     size = open ? themeData->buttonOpen : themeData->buttonClosed;
2099     if (size.cx > 1) {
2100 	*widthPtr = size.cx;
2101 	*heightPtr = size.cy;
2102 	return TCL_OK;
2103     }
2104 
2105     hTheme = themeData->hThemeTREEVIEW;
2106     if (!hTheme)
2107 	return TCL_ERROR;
2108 
2109     iPartId  = TVP_GLYPH;
2110     iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
2111 
2112 #if 0 /* Always returns FALSE */
2113     if (!procs->IsThemePartDefined(
2114 	hTheme,
2115 	iPartId,
2116 	iStateId)) {
2117 	return TCL_ERROR;
2118     }
2119 #endif
2120 
2121     hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
2122 
2123     /* Returns 9x9 for default XP style */
2124     hr = procs->GetThemePartSize(
2125 	hTheme,
2126 	hDC,
2127 	iPartId,
2128 	iStateId,
2129 	NULL,
2130 	TS_DRAW,
2131 	&size
2132     );
2133 
2134     TkWinReleaseDrawableDC(drawable, hDC, &dcState);
2135 
2136     /* With RandomN of 10000, I eventually get hr=E_HANDLE, invalid handle */
2137     /* Not any longer since I don't call OpenThemeData/CloseThemeData for
2138      * every call. */
2139     if (hr != S_OK)
2140 	return TCL_ERROR;
2141 
2142     /* Gave me 0,0 for a non-default theme, even though glyph existed */
2143     if ((size.cx <= 1) && (size.cy <= 1))
2144 	return TCL_ERROR;
2145 
2146     /* Cache the values */
2147     if (open)
2148 	themeData->buttonOpen = size;
2149     else
2150 	themeData->buttonClosed = size;
2151 
2152     *widthPtr = size.cx;
2153     *heightPtr = size.cy;
2154     return TCL_OK;
2155 }
2156 
2157 /*
2158  *----------------------------------------------------------------------
2159  *
2160  * TreeTheme_GetArrowSize --
2161  *
2162  *	Returns the width and height of a column header sort arrow.
2163  *
2164  * Results:
2165  *	TCL_OK if *widthPtr and *heightPtr were set, TCL_ERROR
2166  *	if themed sort arrows can't be drawn.
2167  *
2168  * Side effects:
2169  *	None.
2170  *
2171  *----------------------------------------------------------------------
2172  */
2173 
2174 int
TreeTheme_GetArrowSize(TreeCtrl * tree,Drawable drawable,int up,int * widthPtr,int * heightPtr)2175 TreeTheme_GetArrowSize(
2176     TreeCtrl *tree,		/* Widget info. */
2177     Drawable drawable,		/* Needed on MS Windows. */
2178     int up,			/* TRUE if up arrow. */
2179     int *widthPtr,		/* Returned width of arrow. */
2180     int *heightPtr		/* Returned height of arrow. */
2181     )
2182 {
2183 #if THEME_ARROW==0
2184     if (!appThemeData->themeEnabled || !procs)
2185 	return TCL_ERROR;
2186 
2187     *widthPtr = 9;
2188     *heightPtr = 5;
2189 
2190     return TCL_OK;
2191 #else
2192     TreeThemeData themeData = tree->themeData;
2193     HTHEME hTheme;
2194     HDC hDC;
2195     TkWinDCState dcState;
2196     HRESULT hr;
2197     SIZE size;
2198     int iPartId, iStateId;
2199 
2200     if (!appThemeData->themeEnabled || !procs)
2201 	return TCL_ERROR;
2202 
2203     hTheme = themeData->hThemeTREEVIEW;
2204     if (!hTheme)
2205 	return TCL_ERROR;
2206 
2207     iPartId = HP_HEADERSORTARROW;
2208     iStateId = up ? HSAS_SORTEDUP : HSAS_SORTEDDOWN;
2209 
2210 #if 0 /* Always returns FALSE */
2211     if (!procs->IsThemePartDefined(
2212 	hTheme,
2213 	iPartId,
2214 	iStateId)) {
2215 	return TCL_ERROR;
2216     }
2217 #endif
2218 
2219     hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
2220 
2221     hr = procs->GetThemePartSize(
2222 	hTheme,
2223 	hDC,
2224 	iPartId,
2225 	iStateId,
2226 	NULL,
2227 	TS_DRAW,
2228 	&size
2229     );
2230 
2231     TkWinReleaseDrawableDC(drawable, hDC, &dcState);
2232 
2233     if (hr != S_OK)
2234 	return TCL_ERROR;
2235 
2236     if ((size.cx <= 1) && (size.cy <= 1))
2237 	return TCL_ERROR;
2238 
2239     *widthPtr = size.cx;
2240     *heightPtr = size.cy;
2241 
2242     return TCL_OK;
2243 #endif /* THEME_ARROW==1 */
2244 }
2245 
2246 /*
2247  *----------------------------------------------------------------------
2248  *
2249  * TreeTheme_SetBorders --
2250  *
2251  *	Sets the TreeCtrl.inset pad values according to the needs of
2252  *	the system theme.
2253  *
2254  * Results:
2255  *	TCL_OK if the inset was set, TCL_ERROR if the -highlightthickness
2256  *	and -borderwidth values should be used.
2257  *
2258  * Side effects:
2259  *	None.
2260  *
2261  *----------------------------------------------------------------------
2262  */
2263 
2264 int
TreeTheme_SetBorders(TreeCtrl * tree)2265 TreeTheme_SetBorders(
2266     TreeCtrl *tree		/* Widget info. */
2267     )
2268 {
2269     return TCL_ERROR;
2270 }
2271 
2272 /*
2273  *----------------------------------------------------------------------
2274  *
2275  * TreeTheme_DrawBorders --
2276  *
2277  *	Draws themed borders around the edges of the treectrl.
2278  *
2279  * Results:
2280  *	TCL_OK if drawing occurred, TCL_ERROR if the Tk focus rectangle
2281  *	and 3D border should be drawn.
2282  *
2283  * Side effects:
2284  *	Drawing.
2285  *
2286  *----------------------------------------------------------------------
2287  */
2288 
2289 int
TreeTheme_DrawBorders(TreeCtrl * tree,Drawable drawable)2290 TreeTheme_DrawBorders(
2291     TreeCtrl *tree,		/* Widget info. */
2292     Drawable drawable		/* Where to draw. */
2293     )
2294 {
2295     return TCL_ERROR;
2296 }
2297 
2298 /*
2299  *----------------------------------------------------------------------
2300  *
2301  * TreeTheme_GetHeaderTextColor --
2302  *
2303  *	Returns the text fill color to display a column title with.
2304  *
2305  * Results:
2306  *	TCL_OK if the *colorPtrPtr was set, TCL_ERROR if a non-theme
2307  *	color should be used.
2308  *
2309  * Side effects:
2310  *	May allocate a new XColor.
2311  *
2312  *----------------------------------------------------------------------
2313  */
2314 
2315 int
TreeTheme_GetHeaderTextColor(TreeCtrl * tree,int columnState,XColor ** colorPtrPtr)2316 TreeTheme_GetHeaderTextColor(
2317     TreeCtrl *tree,		/* Widget info. */
2318     int columnState,		/* COLUMN_STATE_xxx flags. */
2319     XColor **colorPtrPtr	/* Returned text color. */
2320     )
2321 {
2322     return TCL_ERROR;
2323 }
2324 
2325 /*
2326  *----------------------------------------------------------------------
2327  *
2328  * TreeTheme_AnimateButtonStart --
2329  *
2330  *	Starts an expand/collapse item button animating from open to
2331  *	closed or vice versa.
2332  *
2333  * Results:
2334  *	TCL_OK.
2335  *
2336  * Side effects:
2337  *	May create a new Tcl_TimerToken.
2338  *
2339  *----------------------------------------------------------------------
2340  */
2341 
2342 int
TreeTheme_AnimateButtonStart(TreeCtrl * tree,TreeItem item)2343 TreeTheme_AnimateButtonStart(
2344     TreeCtrl *tree,		/* Widget info. */
2345     TreeItem item		/* The item whose button should animate. */
2346     )
2347 {
2348     TreeItem_OpenClose(tree, item, -1);
2349 #ifdef SELECTION_VISIBLE
2350     Tree_DeselectHidden(tree);
2351 #endif
2352     return TCL_OK;
2353 }
2354 
2355 /*
2356  *----------------------------------------------------------------------
2357  *
2358  * TreeTheme_ItemDeleted --
2359  *
2360  *	Cancels any item-button animation in progress.
2361  *
2362  * Results:
2363  *	TCL_OK.
2364  *
2365  * Side effects:
2366  *	May delete a TCL_TimerToken.
2367  *
2368  *----------------------------------------------------------------------
2369  */
2370 
2371 int
TreeTheme_ItemDeleted(TreeCtrl * tree,TreeItem item)2372 TreeTheme_ItemDeleted(
2373     TreeCtrl *tree,		/* Widget info. */
2374     TreeItem item		/* Item being deleted. */
2375     )
2376 {
2377     return TCL_OK;
2378 }
2379 
2380 /*
2381  *----------------------------------------------------------------------
2382  *
2383  * TreeTheme_Relayout --
2384  *
2385  *	This gets called when certain config options change and when
2386  *	the size of the widget changes.
2387  *
2388  * Results:
2389  *	None.
2390  *
2391  * Side effects:
2392  *	None.
2393  *
2394  *----------------------------------------------------------------------
2395  */
2396 
2397 void
TreeTheme_Relayout(TreeCtrl * tree)2398 TreeTheme_Relayout(
2399     TreeCtrl *tree		/* Widget info. */
2400     )
2401 {
2402 }
2403 
2404 /*
2405  *----------------------------------------------------------------------
2406  *
2407  * TreeTheme_IsDesktopComposited --
2408  *
2409  *	Determine if the OS windowing system is composited AKA
2410  *	double-buffered.
2411  *
2412  * Results:
2413  *	FALSE FALSE FALSE.
2414  *
2415  * Side effects:
2416  *	None.
2417  *
2418  *----------------------------------------------------------------------
2419  */
2420 
2421 int
TreeTheme_IsDesktopComposited(TreeCtrl * tree)2422 TreeTheme_IsDesktopComposited(
2423     TreeCtrl *tree		/* Widget info. */
2424     )
2425 {
2426     /* TODO:
2427 	Detect Vista/Win7 use of Desktop Window Manager using
2428 	  DwmIsCompositionEnabled().
2429 	WndProc should listen for WM_DWMCOMPOSITIONCHANGED.
2430     */
2431 #if 1
2432     /* On Win7 I see lots of flickering with the dragimage in the demo
2433      * "Explorer (Large Icons)", so Composition must not work quite how I
2434      * expected. */
2435     return FALSE;
2436 #elif 0
2437     HMODULE library = LoadLibrary("dwmapi.dll");
2438     int result = FALSE;
2439 
2440     if (0 != library) {
2441 	typedef BOOL (STDAPICALLTYPE DwmIsCompositionEnabledProc)(BOOL *pfEnabled);
2442 	DwmIsCompositionEnabledProc *proc;
2443 
2444 	if (0 != (proc = GetProcAddress(library, "DwmIsCompositionEnabled"))) {
2445 	    BOOL enabled = FALSE;
2446 	    result = SUCCEEDED(proc(&enabled)) && enabled;
2447 	}
2448 
2449 	FreeLibrary(library);
2450     }
2451 
2452     return result;
2453 #else
2454 /* http://weblogs.asp.net/kennykerr/archive/2006/08/10/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager.aspx */
2455 bool IsCompositionEnabled()
2456 {
2457     HMODULE library = ::LoadLibrary(L"dwmapi.dll");
2458     bool result = false;
2459 
2460     if (0 != library)
2461     {
2462         if (0 != ::GetProcAddress(library,
2463                                   "DwmIsCompositionEnabled"))
2464         {
2465             BOOL enabled = FALSE;
2466             result = SUCCEEDED(::DwmIsCompositionEnabled(&enabled)) && enabled;
2467         }
2468 
2469         VERIFY(::FreeLibrary(library));
2470     }
2471 
2472     return result;
2473 }
2474 #endif
2475     return FALSE;
2476 }
2477 
2478 #if !defined(WM_THEMECHANGED)
2479 #define WM_THEMECHANGED 0x031A
2480 #endif
2481 
2482 static LRESULT WINAPI
ThemeMonitorWndProc(HWND hwnd,UINT msg,WPARAM wp,LPARAM lp)2483 ThemeMonitorWndProc(
2484     HWND hwnd,
2485     UINT msg,
2486     WPARAM wp,
2487     LPARAM lp)
2488 {
2489     Tcl_Interp *interp = (Tcl_Interp *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
2490 
2491     switch (msg) {
2492 	case WM_THEMECHANGED:
2493 	    Tcl_MutexLock(&themeMutex);
2494 	    appThemeData->themeEnabled = procs->IsThemeActive() &&
2495 		    procs->IsAppThemed();
2496 	    Tcl_MutexUnlock(&themeMutex);
2497 	    Tree_TheWorldHasChanged(interp);
2498 	    /* FIXME: must get tree->themeData->hThemeHEADER etc for each widget */
2499 	    break;
2500     }
2501     return DefWindowProc(hwnd, msg, wp, lp);
2502 }
2503 
2504 static CHAR windowClassName[32] = "TreeCtrlMonitorClass";
2505 
2506 static BOOL
RegisterThemeMonitorWindowClass(HINSTANCE hinst)2507 RegisterThemeMonitorWindowClass(
2508     HINSTANCE hinst)
2509 {
2510     WNDCLASSEX wc;
2511 
2512     wc.cbSize        = sizeof(WNDCLASSEX);
2513     wc.style         = CS_HREDRAW | CS_VREDRAW;
2514     wc.lpfnWndProc   = (WNDPROC) ThemeMonitorWndProc;
2515     wc.cbClsExtra    = 0;
2516     wc.cbWndExtra    = 0;
2517     wc.hInstance     = hinst;
2518     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
2519     wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
2520     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
2521     wc.hbrBackground = (HBRUSH) COLOR_WINDOW;
2522     wc.lpszMenuName  = windowClassName;
2523     wc.lpszClassName = windowClassName;
2524 
2525     return RegisterClassEx(&wc);
2526 }
2527 
2528 static HWND
CreateThemeMonitorWindow(HINSTANCE hinst,Tcl_Interp * interp)2529 CreateThemeMonitorWindow(
2530     HINSTANCE hinst,
2531     Tcl_Interp *interp)
2532 {
2533     CHAR title[32] = "TreeCtrlMonitorWindow";
2534     HWND hwnd;
2535 
2536     hwnd = CreateWindow(windowClassName, title, WS_OVERLAPPEDWINDOW,
2537 	CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
2538 	NULL, NULL, hinst, NULL);
2539     if (!hwnd)
2540 	return NULL;
2541 
2542     SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)interp);
2543     ShowWindow(hwnd, SW_HIDE);
2544     UpdateWindow(hwnd);
2545 
2546     return hwnd;
2547 }
2548 
2549 typedef struct PerInterpData PerInterpData;
2550 struct PerInterpData
2551 {
2552     HWND hwnd;
2553 };
2554 
2555 static void
ThemeFreeAssocData(ClientData clientData,Tcl_Interp * interp)2556 ThemeFreeAssocData(
2557     ClientData clientData,
2558     Tcl_Interp *interp)
2559 {
2560     PerInterpData *data = (PerInterpData *) clientData;
2561 
2562     DestroyWindow(data->hwnd);
2563     ckfree((char *) data);
2564 }
2565 
2566 /*
2567  *----------------------------------------------------------------------
2568  *
2569  * TreeTheme_ThemeChanged --
2570  *
2571  *	Called after the system theme changes.
2572  *
2573  * Results:
2574  *	None.
2575  *
2576  * Side effects:
2577  *	None.
2578  *
2579  *----------------------------------------------------------------------
2580  */
2581 
2582 void
TreeTheme_ThemeChanged(TreeCtrl * tree)2583 TreeTheme_ThemeChanged(
2584     TreeCtrl *tree		/* Widget info. */
2585     )
2586 {
2587     Window win = Tk_WindowId(tree->tkwin);
2588     HWND hwnd = Tk_GetHWND(win);
2589 
2590     if (tree->themeData != NULL) {
2591 	if (tree->themeData->hThemeHEADER != NULL) {
2592 	    procs->CloseThemeData(tree->themeData->hThemeHEADER);
2593 	    tree->themeData->hThemeHEADER = NULL;
2594 	}
2595 	if (tree->themeData->hThemeTREEVIEW != NULL) {
2596 	    procs->CloseThemeData(tree->themeData->hThemeTREEVIEW);
2597 	    tree->themeData->hThemeTREEVIEW = NULL;
2598 	}
2599     }
2600 
2601     if (!appThemeData->themeEnabled || !procs)
2602 	return;
2603 
2604     if (tree->themeData == NULL)
2605 	tree->themeData = (TreeThemeData) ckalloc(sizeof(TreeThemeData_));
2606 
2607     tree->themeData->hThemeHEADER = procs->OpenThemeData(hwnd, L"HEADER");
2608     tree->themeData->hThemeTREEVIEW = procs->OpenThemeData(hwnd, L"TREEVIEW");
2609 
2610     tree->themeData->buttonClosed.cx = tree->themeData->buttonOpen.cx = -1;
2611 }
2612 
2613 /*
2614  *----------------------------------------------------------------------
2615  *
2616  * TreeTheme_InitWidget --
2617  *
2618  *	Performs theme-related initialization when a treectrl is
2619  *	created.
2620  *
2621  * Results:
2622  *	TCL_OK or TCL_ERROR, but result is ignored.
2623  *
2624  * Side effects:
2625  *	Depends on the platform.
2626  *
2627  *----------------------------------------------------------------------
2628  */
2629 
2630 int
TreeTheme_InitWidget(TreeCtrl * tree)2631 TreeTheme_InitWidget(
2632     TreeCtrl *tree		/* Widget info. */
2633     )
2634 {
2635     Window win = Tk_WindowId(tree->tkwin);
2636     HWND hwnd = Tk_GetHWND(win);
2637 
2638     if (!appThemeData->themeEnabled || !procs)
2639 	return TCL_ERROR;
2640 
2641     tree->themeData = (TreeThemeData) ckalloc(sizeof(TreeThemeData_));
2642 
2643     /* http://www.codeproject.com/cs/miscctrl/themedtabpage.asp?msg=1445385#xx1445385xx */
2644     /* http://msdn2.microsoft.com/en-us/library/ms649781.aspx */
2645 
2646     tree->themeData->hThemeHEADER = procs->OpenThemeData(hwnd, L"HEADER");
2647     tree->themeData->hThemeTREEVIEW = procs->OpenThemeData(hwnd, L"TREEVIEW");
2648 
2649     tree->themeData->buttonClosed.cx = tree->themeData->buttonOpen.cx = -1;
2650 
2651     return TCL_OK;
2652 }
2653 
2654 /*
2655  *----------------------------------------------------------------------
2656  *
2657  * TreeTheme_FreeWidget --
2658  *
2659  *	Performs theme-related cleanup a when a treectrl is destroyed.
2660  *
2661  * Results:
2662  *	None.
2663  *
2664  * Side effects:
2665  *	Depends on the platform.
2666  *
2667  *----------------------------------------------------------------------
2668  */
2669 
2670 int
TreeTheme_FreeWidget(TreeCtrl * tree)2671 TreeTheme_FreeWidget(
2672     TreeCtrl *tree		/* Widget info. */
2673     )
2674 {
2675     if (tree->themeData != NULL) {
2676 	if (tree->themeData->hThemeHEADER != NULL)
2677 	    procs->CloseThemeData(tree->themeData->hThemeHEADER);
2678 	if (tree->themeData->hThemeTREEVIEW != NULL)
2679 	    procs->CloseThemeData(tree->themeData->hThemeTREEVIEW);
2680 	ckfree((char *) tree->themeData);
2681     }
2682     return TCL_OK;
2683 }
2684 
2685 /*
2686  *----------------------------------------------------------------------
2687  *
2688  * TreeTheme_InitInterp --
2689  *
2690  *	Performs theme-related initialization when the TkTreeCtrl
2691  *	package is loaded into an interpreter.
2692  *
2693  * Results:
2694  *	TCL_OK or TCL_ERROR, but result is ignored.
2695  *
2696  * Side effects:
2697  *	Depends on the platform.
2698  *
2699  *----------------------------------------------------------------------
2700  */
2701 
2702 int
TreeTheme_InitInterp(Tcl_Interp * interp)2703 TreeTheme_InitInterp(
2704     Tcl_Interp *interp		/* Interp that loaded TkTreeCtrl pkg. */
2705     )
2706 {
2707     HWND hwnd;
2708     PerInterpData *data;
2709 
2710     Tcl_MutexLock(&themeMutex);
2711 
2712     /* This is done once per-application */
2713     if (appThemeData == NULL) {
2714 	appThemeData = (XPThemeData *) ckalloc(sizeof(XPThemeData));
2715 	appThemeData->procs = LoadXPThemeProcs(&appThemeData->hlibrary);
2716 	appThemeData->registered = FALSE;
2717 	appThemeData->themeEnabled = FALSE;
2718 
2719 	procs = appThemeData->procs;
2720 
2721 	if (appThemeData->procs) {
2722 	    /* Check this again if WM_THEMECHANGED arrives */
2723 	    appThemeData->themeEnabled = procs->IsThemeActive() &&
2724 		    procs->IsAppThemed();
2725 
2726 	    appThemeData->registered =
2727 		RegisterThemeMonitorWindowClass(Tk_GetHINSTANCE());
2728 	}
2729     }
2730 
2731     Tcl_MutexUnlock(&themeMutex);
2732 
2733     if (!procs || !appThemeData->registered)
2734 	return TCL_ERROR;
2735 
2736     /* Per-interp */
2737     hwnd = CreateThemeMonitorWindow(Tk_GetHINSTANCE(), interp);
2738     if (!hwnd)
2739 	return TCL_ERROR;
2740 
2741     data = (PerInterpData *) ckalloc(sizeof(PerInterpData));
2742     data->hwnd = hwnd;
2743     Tcl_SetAssocData(interp, "TreeCtrlTheme", ThemeFreeAssocData, (ClientData) data);
2744 
2745     return TCL_OK;
2746 }
2747 
2748 /*
2749  *----------------------------------------------------------------------
2750  *
2751  * TreeTheme_SetOptionDefault --
2752  *
2753  *	Sets the default value for an option.
2754  *
2755  * Results:
2756  *	Sets the defValue field if it wasn't done already.
2757  *
2758  * Side effects:
2759  *	Changes an existing option table.
2760  *
2761  *----------------------------------------------------------------------
2762  */
2763 
2764 void
TreeTheme_SetOptionDefault(Tk_OptionSpec * specPtr)2765 TreeTheme_SetOptionDefault(
2766     Tk_OptionSpec *specPtr
2767     )
2768 {
2769 #ifdef TREECTRL_DEBUG
2770     if (specPtr == NULL)
2771 	panic("TreeTheme_SetOptionDefault specPtr == NULL");
2772 #endif
2773 
2774     /* Only set the default value once per-application. */
2775     if (specPtr->defValue != NULL)
2776 	return;
2777 
2778     if (!strcmp(specPtr->optionName, "-buttontracking"))
2779 	specPtr->defValue = "0";
2780     else if (!strcmp(specPtr->optionName, "-showlines"))
2781 	specPtr->defValue = "1";
2782 
2783 #ifdef TREECTRL_DEBUG
2784     else
2785 	panic("TreeTheme_SetOptionDefault unhandled option \"%s\"",
2786 	    specPtr->optionName ? specPtr->optionName : "NULL");
2787 #endif
2788 }
2789 
2790 int
TreeThemeCmd(TreeCtrl * tree,int objc,Tcl_Obj * CONST objv[])2791 TreeThemeCmd(
2792     TreeCtrl *tree,		/* Widget info. */
2793     int objc,			/* Number of arguments. */
2794     Tcl_Obj *CONST objv[]	/* Argument values. */
2795     )
2796 {
2797     Tcl_Interp *interp = tree->interp;
2798     static CONST char *commandName[] = {
2799 	"platform", "setwindowtheme", (char *) NULL
2800     };
2801     enum {
2802 	COMMAND_PLATFORM, COMMAND_SETWINDOWTHEME
2803     };
2804     int index;
2805 
2806     if (objc < 3) {
2807 	Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
2808 	return TCL_ERROR;
2809     }
2810 
2811     if (Tcl_GetIndexFromObj(interp, objv[2], commandName, "command", 0,
2812 	    &index) != TCL_OK) {
2813 	return TCL_ERROR;
2814     }
2815 
2816     switch (index) {
2817 	/* T theme platform */
2818 	case COMMAND_PLATFORM: {
2819 	    char *platform = "X11"; /* X11, xlib, whatever */
2820 	    if (appThemeData->themeEnabled && appThemeData->procs)
2821 		platform = "visualstyles";
2822 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(platform, -1));
2823 	    break;
2824 	}
2825 	/* T theme setwindowtheme $appname */
2826 	case COMMAND_SETWINDOWTHEME: {
2827 	    LPCWSTR pszSubAppName; /* L"Explorer" for example */
2828 	    int length;
2829 	    Window win;
2830 	    HWND hwnd;
2831 
2832 	    if (objc != 4) {
2833 		Tcl_WrongNumArgs(interp, 3, objv, "appname");
2834 		return TCL_ERROR;
2835 	    }
2836 	    if (!appThemeData->themeEnabled || !appThemeData->procs)
2837 		break;
2838 	    win = Tk_WindowId(tree->tkwin);
2839 	    hwnd = Tk_GetHWND(win);
2840 	    pszSubAppName = Tcl_GetUnicodeFromObj(objv[3], &length);
2841 	    procs->SetWindowTheme(hwnd, length ? pszSubAppName : NULL, NULL);
2842 
2843 	    /* uxtheme.h says a WM_THEMECHANGED is sent to the window. */
2844 	    /* FIXME: only this window needs to be updated. */
2845 	    /* This calls TreeTheme_ThemeChanged which is needed. */
2846 	    Tree_TheWorldHasChanged(tree->interp);
2847 	    break;
2848 	}
2849     }
2850 
2851     return TCL_OK;
2852 }
2853 
2854 /*** Gradients ***/
2855 
2856 /*
2857  * GDI+ flat api
2858  */
2859 
2860 /* gdiplus.h is a C++ header file with MSVC. */
2861 /* gdiplus.h works with C and C++ with MinGW. */
2862 /* However gdiplus.h is not included with MinGW-w64 for some reason and
2863  * also does not come with the Linux i586-mingw32msvc cross-compiler. */
2864 #if 1
2865 #define WINGDIPAPI __stdcall
2866 #define GDIPCONST const
2867 #define VOID void
2868 typedef float REAL;
2869 typedef enum CombineMode {
2870     CombineModeReplace = 0
2871 } CombineMode;
2872 typedef enum GpFillMode
2873 {
2874     FillModeAlternate,
2875     FillModeWinding
2876 } GpFillMode;
2877 typedef enum GpLineCap {
2878     LineCapSquare = 1
2879 } GpLineCap;
2880 typedef enum GpUnit {
2881     UnitWorld = 0,
2882     UnitDisplay = 1,
2883     UnitPixel = 2,
2884     UnitPoint = 3,
2885     UnitInch = 4,
2886     UnitDocument = 5,
2887     UnitMillimeter = 6
2888 } GpUnit;
2889 typedef enum GpStatus {
2890     Ok = 0
2891 } GpStatus;
2892 typedef enum GpWrapMode
2893 {
2894     WrapModeTile = 0, /* repeat */
2895     WrapModeTileFlipXY = 3 /* reflect */
2896 } GpWrapMode;
2897 typedef enum LinearGradientMode
2898 {
2899     LinearGradientModeHorizontal,
2900     LinearGradientModeVertical
2901 } LinearGradientMode;
2902 typedef enum GpPenAlignment {
2903     PenAlignmentCenter = 0,
2904     PenAlignmentInset = 1
2905 } GpPenAlignment;
2906 typedef enum SmoothingMode {
2907     SmoothingModeHighQuality = 2,
2908     SmoothingModeAntiAlias = 4
2909 } SmoothingMode;
2910 typedef struct GdiplusStartupInput
2911 {
2912     UINT32 GdiplusVersion;
2913     /*DebugEventProc*/VOID* DebugEventCallback;
2914     BOOL SuppressBackgroundThread;
2915     BOOL SuppressExternalCodecs;
2916 } GdiplusStartupInput;
2917 typedef struct GdiplusStartupOutput
2918 {
2919     /*NotificationHookProc*/VOID* NotificationHook;
2920     /*NotificationUnhookProc*/VOID* NotificationUnhook;
2921 } GdiplusStartupOutput;
2922 typedef struct GpPoint {
2923     INT X;
2924     INT Y;
2925 } GpPoint;
2926 typedef struct GpPointF {
2927     REAL X;
2928     REAL Y;
2929 } GpPointF;
2930 typedef struct GpRect {
2931     INT X;
2932     INT Y;
2933     INT Width;
2934     INT Height;
2935 } GpRect;
2936 typedef DWORD ARGB;
2937 typedef void GpBrush;
2938 typedef void GpGraphics;
2939 typedef void GpLineGradient;
2940 typedef void GpPath;
2941 typedef void GpPen;
2942 typedef void GpSolidFill;
2943 #endif
2944 
2945 /* After gdiplus.dll is dynamically loaded, this structure is
2946  * filled in with pointers to functions that are used below. */
2947 static struct
2948 {
2949     HMODULE handle; /* gdiplus.dll */
2950 
2951     VOID* (WINGDIPAPI *_GdipAlloc)(size_t);
2952     VOID (WINGDIPAPI *_GdipFree)(VOID*);
2953 
2954     GpStatus (WINGDIPAPI *_GdiplusStartup)(ULONG_PTR*,GDIPCONST GdiplusStartupInput*,GdiplusStartupOutput*);
2955     VOID (WINGDIPAPI *_GdiplusShutdown)(ULONG_PTR);
2956 
2957     /* Graphics */
2958     GpStatus (WINGDIPAPI *_GdipCreateFromHDC)(HDC,GpGraphics**);
2959     GpStatus (WINGDIPAPI *_GdipFillRectangleI)(GpGraphics*,GpBrush*,INT,INT,INT,INT);
2960     GpStatus (WINGDIPAPI *_GdipDeleteGraphics)(GpGraphics*);
2961     GpStatus (WINGDIPAPI *_GdipDrawPath)(GpGraphics*,GpPen*,GpPath*);
2962     GpStatus (WINGDIPAPI *_GdipFillPath)(GpGraphics*,GpBrush*,GpPath*);
2963     GpStatus (WINGDIPAPI *_GdipSetClipHrgn)(GpGraphics*,HRGN,CombineMode);
2964     GpStatus (WINGDIPAPI *_GdipSetClipPath)(GpGraphics*,GpPath*,CombineMode);
2965     GpStatus (WINGDIPAPI *_GdipSetClipRectI)(GpGraphics*,INT,INT,INT,INT,CombineMode);
2966     GpStatus (WINGDIPAPI *_GdipSetSmoothingMode)(GpGraphics*,SmoothingMode);
2967 
2968     /* GraphicsPath */
2969     GpStatus (WINGDIPAPI *_GdipCreatePath)(GpFillMode,GpPath**);
2970     GpStatus (WINGDIPAPI *_GdipDeletePath)(GpPath*);
2971     GpStatus (WINGDIPAPI *_GdipResetPath)(GpPath*);
2972     GpStatus (WINGDIPAPI *_GdipAddPathArc)(GpPath*,REAL,REAL,REAL,REAL,REAL,REAL);
2973     GpStatus (WINGDIPAPI *_GdipAddPathLine)(GpPath*,REAL,REAL,REAL,REAL);
2974     GpStatus (WINGDIPAPI *_GdipAddPathRectangle)(GpPath*,REAL,REAL,REAL,REAL);
2975     GpStatus (WINGDIPAPI *_GdipStartPathFigure)(GpPath*);
2976     GpStatus (WINGDIPAPI *_GdipClosePathFigure)(GpPath*);
2977 
2978     /* Linear Gradient brush */
2979     GpStatus (WINGDIPAPI *_GdipCreateLineBrushFromRectI)(GDIPCONST GpRect*,ARGB,ARGB,LinearGradientMode,GpWrapMode,GpLineGradient**);
2980     GpStatus (WINGDIPAPI *_GdipSetLinePresetBlend)(GpLineGradient*,GDIPCONST ARGB*,GDIPCONST REAL*,INT);
2981     GpStatus (WINGDIPAPI *_GdipDeleteBrush)(GpBrush*);
2982 
2983     /* Pen */
2984     GpStatus (WINGDIPAPI *_GdipCreatePen1)(ARGB,REAL,GpUnit,GpPen**);
2985     GpStatus (WINGDIPAPI *_GdipCreatePen2)(GpBrush*,REAL,GpUnit,GpPen**);
2986     GpStatus (WINGDIPAPI *_GdipSetPenEndCap)(GpPen*,GpLineCap);
2987     GpStatus (WINGDIPAPI *_GdipSetPenStartCap)(GpPen*,GpLineCap);
2988     GpStatus (WINGDIPAPI *_GdipSetPenMode)(GpPen*,GpPenAlignment);
2989     GpStatus (WINGDIPAPI *_GdipDeletePen)(GpPen*);
2990 
2991     /* SolidFill brush */
2992     GpStatus (WINGDIPAPI *_GdipCreateSolidFill)(ARGB,GpSolidFill**);
2993 
2994 } DllExports = {0};
2995 
2996 /* Per-application global data */
2997 typedef struct
2998 {
2999     ULONG_PTR token;			/* Result of GdiplusStartup() */
3000 #if 0
3001     GdiplusStartupOutput output;	/* Result of GdiplusStartup() */
3002 #endif
3003 } TreeDrawAppData;
3004 
3005 static TreeDrawAppData *appDrawData = NULL;
3006 
3007 /* Tcl_CreateExitHandler() callback that shuts down GDI+ */
3008 static void
TreeDraw_ExitHandler(ClientData clientData)3009 TreeDraw_ExitHandler(
3010     ClientData clientData
3011     )
3012 {
3013     if (appDrawData != NULL) {
3014 	if (DllExports.handle != NULL)
3015 	    DllExports._GdiplusShutdown(appDrawData->token);
3016     }
3017 }
3018 
3019 /* Load gdiplus.dll (if it exists) and fill in the DllExports global */
3020 /* If gdiplus.dll can't be loaded DllExports.handle is set to NULL which
3021  * should be checked to test whether GDI+ can be used. */
3022 static int
LoadGdiplus(void)3023 LoadGdiplus(void)
3024 {
3025     DllExports.handle = LoadLibrary("gdiplus.dll");
3026     if (DllExports.handle != NULL) {
3027 #define LOADPROC(name) \
3028 	(0 != (DllExports._ ## name = (VOID *)GetProcAddress(DllExports.handle, #name) ))
3029 	if (   LOADPROC(GdipAlloc)
3030 	    && LOADPROC(GdipFree)
3031 	    && LOADPROC(GdiplusStartup)
3032 	    && LOADPROC(GdiplusShutdown)
3033 	    && LOADPROC(GdipCreateFromHDC)
3034 	    && LOADPROC(GdipFillRectangleI)
3035 	    && LOADPROC(GdipDeleteGraphics)
3036 	    && LOADPROC(GdipDrawPath)
3037 	    && LOADPROC(GdipFillPath)
3038 	    && LOADPROC(GdipSetClipHrgn)
3039 	    && LOADPROC(GdipSetClipPath)
3040 	    && LOADPROC(GdipSetClipRectI)
3041 	    && LOADPROC(GdipSetSmoothingMode)
3042 	    && LOADPROC(GdipCreatePath)
3043 	    && LOADPROC(GdipDeletePath)
3044 	    && LOADPROC(GdipResetPath)
3045 	    && LOADPROC(GdipAddPathArc)
3046 	    && LOADPROC(GdipAddPathLine)
3047 	    && LOADPROC(GdipAddPathRectangle)
3048 	    && LOADPROC(GdipStartPathFigure)
3049 	    && LOADPROC(GdipClosePathFigure)
3050 	    && LOADPROC(GdipCreateLineBrushFromRectI)
3051 	    && LOADPROC(GdipSetLinePresetBlend)
3052 	    && LOADPROC(GdipDeleteBrush)
3053 	    && LOADPROC(GdipCreatePen1)
3054 	    && LOADPROC(GdipCreatePen2)
3055 	    && LOADPROC(GdipSetPenEndCap)
3056 	    && LOADPROC(GdipSetPenStartCap)
3057 	    && LOADPROC(GdipSetPenMode)
3058 	    && LOADPROC(GdipDeletePen)
3059 	    && LOADPROC(GdipCreateSolidFill)
3060 	) {
3061 	    return 1;
3062 	}
3063 #undef LOADPROC
3064     }
3065     DllExports.handle = NULL;
3066     return 0;
3067 }
3068 
3069 /* Per-interp init */
3070 int
TreeDraw_InitInterp(Tcl_Interp * interp)3071 TreeDraw_InitInterp(
3072     Tcl_Interp *interp
3073     )
3074 {
3075     /* This is done once per-application */
3076     if (appDrawData == NULL) {
3077 	appDrawData = (TreeDrawAppData *) ckalloc(sizeof(TreeDrawAppData));
3078 	memset(appDrawData, '\0', sizeof(TreeDrawAppData));
3079 	if (LoadGdiplus()) {
3080 	    GdiplusStartupInput input;
3081 	    GpStatus status;
3082 	    input.GdiplusVersion = 1;
3083 	    input.DebugEventCallback = NULL;
3084 	    input.SuppressBackgroundThread = FALSE;
3085 	    input.SuppressExternalCodecs = FALSE;
3086 	    /* Not sure what happens when the main application or other
3087 	     * DLLs also call this, probably it is okay. */
3088 	    status = DllExports._GdiplusStartup(&appDrawData->token, &input,
3089 #if 1
3090 		NULL);
3091 #else
3092 		&appDrawData->output);
3093 #endif
3094 	    if (status != Ok) {
3095 		DllExports.handle = NULL;
3096 	    }
3097 	}
3098 	Tcl_CreateExitHandler(TreeDraw_ExitHandler, NULL);
3099     }
3100 
3101     return TCL_OK;
3102 }
3103 
3104 /*
3105  *----------------------------------------------------------------------
3106  *
3107  * Tree_HasNativeGradients --
3108  *
3109  *	Determine if this platform supports gradients natively.
3110  *
3111  * Results:
3112  *	1 if GDI+ is available,
3113  *	0 otherwise.
3114  *
3115  * Side effects:
3116  *	None.
3117  *
3118  *----------------------------------------------------------------------
3119  */
3120 
3121 int
Tree_HasNativeGradients(TreeCtrl * tree)3122 Tree_HasNativeGradients(
3123     TreeCtrl *tree)
3124 {
3125     return (DllExports.handle != NULL);
3126 }
3127 
3128 /* ARGB is a DWORD color used by GDI+ */
MakeARGB(BYTE a,BYTE r,BYTE g,BYTE b)3129 static ARGB MakeARGB(BYTE a, BYTE r, BYTE g, BYTE b)
3130 {
3131     return (ARGB) ((((DWORD) a) << 24) | (((DWORD) r) << 16)
3132 		 | (((DWORD) g) << 8) | ((DWORD) b));
3133 }
3134 
MakeGDIPlusColor(XColor * xc,double opacity)3135 static ARGB MakeGDIPlusColor(XColor *xc, double opacity)
3136 {
3137     return MakeARGB(
3138 	(BYTE)(opacity*255),
3139 	(BYTE)((xc)->pixel & 0xFF),
3140 	(BYTE)(((xc)->pixel >> 8) & 0xFF),
3141 	(BYTE)(((xc)->pixel >> 16) & 0xFF));
3142 }
3143 
3144 typedef struct {
3145     TreeCtrl *tree;
3146     TreeClip *clip;
3147     GpGraphics *graphics;
3148 } TreeClipStateGraphics;
3149 
3150 static GpStatus
TreeClip_ToGraphics(TreeCtrl * tree,TreeClip * clip,GpGraphics * graphics,TreeClipStateGraphics * state)3151 TreeClip_ToGraphics(
3152     TreeCtrl *tree,
3153     TreeClip *clip,
3154     GpGraphics *graphics,
3155     TreeClipStateGraphics *state
3156     )
3157 {
3158     GpStatus status = Ok;
3159 
3160     state->tree = tree;
3161     state->clip = clip;
3162     state->graphics = graphics;
3163 
3164     if (clip && clip->type == TREE_CLIP_RECT) {
3165 	status = DllExports._GdipSetClipRectI(graphics,
3166 	    clip->tr.x, clip->tr.y, clip->tr.width, clip->tr.height,
3167 	    CombineModeReplace);
3168     }
3169     if (clip && clip->type == TREE_CLIP_AREA) {
3170 	TreeRectangle tr;
3171 	if (Tree_AreaBbox(tree, clip->area, &tr) == 0) {
3172 	    TreeRect_SetXYWH(tr, 0, 0, 0, 0);
3173 	}
3174 	status = DllExports._GdipSetClipRectI(graphics,
3175 	    TreeRect_Left(tr), TreeRect_Top(tr),
3176 	    TreeRect_Width(tr), TreeRect_Height(tr),
3177 	    CombineModeReplace);
3178     }
3179     if (clip && clip->type == TREE_CLIP_REGION) {
3180 	status = DllExports._GdipSetClipHrgn(graphics, (HRGN) clip->region,
3181 	    CombineModeReplace);
3182     }
3183 
3184     return status;
3185 }
3186 
3187 static GpStatus
MakeLinearGradientBrush(TreeGradient gradient,TreeRectangle trBrush,GpLineGradient ** lgPtr)3188 MakeLinearGradientBrush(
3189     TreeGradient gradient,	/* Gradient token. */
3190     TreeRectangle trBrush,	/* Brush bounds. */
3191     GpLineGradient **lgPtr	/* Result. */
3192     )
3193 {
3194     GpLineGradient *lineGradient;
3195     GpStatus status;
3196     GpRect rect;
3197     GradientStop *stop;
3198     int i, nstops;
3199     ARGB color1, color2;
3200 
3201     (*lgPtr) = NULL;
3202 
3203     nstops = gradient->stopArrPtr->nstops;
3204 
3205     rect.X = trBrush.x, rect.Y = trBrush.y,
3206 	rect.Width = trBrush.width, rect.Height = trBrush.height;
3207 
3208     /* BUG BUG BUG: A linear gradient brush will *sometimes* wrap when it
3209      * shouldn't due to rounding errors or something, resulting in a line
3210      * of color2 where color1 starts. The recommended solution is to
3211      * make the brush 1-pixel larger on all sides than the area being
3212      * painted.  The downside of this is you will lose a bit of the gradient. */
3213     if (gradient->vertical)
3214 	rect.Y -= 1, rect.Height += 1;
3215     else
3216 	rect.X -= 1, rect.Width += 1;
3217 
3218     stop = gradient->stopArrPtr->stops[0];
3219     color1 = MakeGDIPlusColor(stop->color, stop->opacity);
3220     stop = gradient->stopArrPtr->stops[nstops-1];
3221     color2 = MakeGDIPlusColor(stop->color, stop->opacity);
3222 
3223     status = DllExports._GdipCreateLineBrushFromRectI(
3224 	&rect, color1, color2,
3225 	gradient->vertical ? LinearGradientModeVertical : LinearGradientModeHorizontal,
3226 	WrapModeTile, &lineGradient);
3227     if (status != Ok)
3228 	return status;
3229 
3230     if (nstops > 2) {
3231 	ARGB *col = DllExports._GdipAlloc(nstops * sizeof(ARGB));
3232 	if (col != NULL) {
3233 	    REAL *pos = DllExports._GdipAlloc(nstops * sizeof(REAL));
3234 	    if (pos != NULL) {
3235 		for (i = 0; i < nstops; i++) {
3236 		    stop = gradient->stopArrPtr->stops[i];
3237 		    col[i] = MakeGDIPlusColor(stop->color, stop->opacity);
3238 		    pos[i] = (REAL)stop->offset;
3239 		}
3240 		status = DllExports._GdipSetLinePresetBlend(lineGradient,
3241 		    col, pos, nstops);
3242 		DllExports._GdipFree((void*) pos);
3243 	    }
3244 	    DllExports._GdipFree((void*) col);
3245 	}
3246     }
3247 
3248     (*lgPtr) = lineGradient;
3249     return Ok;
3250 }
3251 
3252 /*
3253  *----------------------------------------------------------------------
3254  *
3255  * TreeGradient_FillRect --
3256  *
3257  *	Paint a rectangle with a gradient using GDI+.
3258  *
3259  * Results:
3260  *	If GDI+ isn't available then fall back to X11.  If the gradient
3261  *	has <2 stops then nothing is drawn.
3262  *
3263  * Side effects:
3264  *	Drawing.
3265  *
3266  *----------------------------------------------------------------------
3267  */
3268 
3269 void
TreeGradient_FillRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr)3270 TreeGradient_FillRect(
3271     TreeCtrl *tree,		/* Widget info. */
3272     TreeDrawable td,		/* Where to draw. */
3273     TreeClip *clip,		/* Clipping area or NULL. */
3274     TreeGradient gradient,	/* Gradient token. */
3275     TreeRectangle trBrush,	/* Brush bounds. */
3276     TreeRectangle tr		/* Rectangle to paint. */
3277     )
3278 {
3279     HDC hDC;
3280     TkWinDCState dcState;
3281     TreeClipStateGraphics clipState;
3282     GpGraphics *graphics;
3283     GpLineGradient *lineGradient = NULL;
3284     GpStatus status;
3285     GpRect rect;
3286     int nstops;
3287 
3288     /* Draw nothing if the brush is zero-sized. */
3289     if (trBrush.width <= 0 || trBrush.height <= 0)
3290 	return;
3291 
3292     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3293 	TreeGradient_FillRectX11(tree, td, clip, gradient, trBrush, tr);
3294 	return;
3295     }
3296 
3297     nstops = gradient->stopArrPtr ? gradient->stopArrPtr->nstops : 0;
3298     if (nstops < 2) /* can be 0, but < 2 isn't allowed */
3299 	return;
3300 
3301     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3302 
3303     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3304     if (status != Ok)
3305 	goto error1;
3306 
3307     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3308     if (status != Ok)
3309 	goto error2;
3310 
3311     status = MakeLinearGradientBrush(gradient, trBrush, &lineGradient);
3312     if (status != Ok)
3313 	goto error2;
3314 
3315     rect.X = tr.x, rect.Y = tr.y, rect.Width = tr.width, rect.Height = tr.height;
3316 
3317     DllExports._GdipFillRectangleI(graphics, lineGradient,
3318 	rect.X, rect.Y, rect.Width, rect.Height);
3319 
3320     DllExports._GdipDeleteBrush(lineGradient);
3321 
3322 error2:
3323     DllExports._GdipDeleteGraphics(graphics);
3324 
3325 error1:
3326     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3327 
3328 #ifdef TREECTRL_DEBUG
3329     if (status != Ok) dbwin("TreeGradient_FillRect gdiplus status != Ok");
3330 #endif
3331 }
3332 
3333 static void
GetRectPath_Outline(GpPath * path,TreeRectangle tr,int open,int outlineWidth)3334 GetRectPath_Outline(
3335     GpPath *path,		/* GDI+ path to set. */
3336     TreeRectangle tr,		/* Rectangle to draw. */
3337     int open,			/* RECT_OPEN_x flags. */
3338     int outlineWidth		/* Thickness of the outline. */
3339     )
3340 {
3341     REAL x = (REAL)tr.x, y = (REAL)tr.y, width = (REAL)tr.width, height = (REAL)tr.height;
3342     int drawW = (open & RECT_OPEN_W) == 0;
3343     int drawN = (open & RECT_OPEN_N) == 0;
3344     int drawE = (open & RECT_OPEN_E) == 0;
3345     int drawS = (open & RECT_OPEN_S) == 0;
3346 
3347     /* Weird issue, if outlineWidth == 1 the NW corner is missing a pixel,
3348      * even when calling GdipAddPathRectangle.  So don't draw on the 1/2
3349      * pixel boundary in that case. */
3350     if (outlineWidth > 1) {
3351 	x += outlineWidth / 2.0f, y += outlineWidth / 2.0f;
3352     }
3353     width -= outlineWidth, height -= outlineWidth;
3354 
3355     /* Simple case: draw all 4 edges */
3356     if (drawW && drawN && drawE && drawS) {
3357 	DllExports._GdipAddPathRectangle(path, x, y, width, height);
3358 
3359     /* Complicated case: some edges are "open" */
3360     } else {
3361 	if (drawN)
3362 	    DllExports._GdipAddPathLine(path, x, y, x + width, y);
3363 	if (drawE)
3364 	    DllExports._GdipAddPathLine(path, x + width, y, x + width, y + height);
3365 	else if (drawN)
3366 	    DllExports._GdipStartPathFigure(path);
3367 	if (drawS)
3368 	    DllExports._GdipAddPathLine(path, x + width, y + height, x, y + height);
3369 	else if (drawE)
3370 	    DllExports._GdipStartPathFigure(path);
3371 	if (drawW)
3372 	    DllExports._GdipAddPathLine(path, x, y + height, x, y);
3373     }
3374 }
3375 
3376 void
TreeGradient_DrawRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr,int outlineWidth,int open)3377 TreeGradient_DrawRect(
3378     TreeCtrl *tree,		/* Widget info. */
3379     TreeDrawable td,		/* Where to draw. */
3380     TreeClip *clip,		/* Clipping area or NULL. */
3381     TreeGradient gradient,	/* Gradient. */
3382     TreeRectangle trBrush,	/* Brush bounds. */
3383     TreeRectangle tr,		/* Rectangle to draw. */
3384     int outlineWidth,		/* Width of outline. */
3385     int open			/* RECT_OPEN_x flags */
3386     )
3387 {
3388     HDC hDC;
3389     TkWinDCState dcState;
3390     TreeClipStateGraphics clipState;
3391     GpGraphics *graphics;
3392     GpPath *path;
3393     GpLineGradient *lineGradient;
3394     GpPen *pen;
3395     GpStatus status;
3396 
3397     if (gradient->stopArrPtr == NULL || gradient->stopArrPtr->nstops < 2)
3398 	return;
3399 
3400     /* Draw nothing if the brush is zero-sized. */
3401     if (trBrush.width <= 0 || trBrush.height <= 0)
3402 	return;
3403 
3404     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3405 	TreeGradient_DrawRectX11(tree, td, clip, gradient, trBrush, tr, outlineWidth, open);
3406 	return;
3407     }
3408 
3409     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3410 
3411     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3412     if (status != Ok)
3413 	goto error1;
3414 
3415     status = DllExports._GdipCreatePath(FillModeAlternate, &path);
3416     if (status != Ok)
3417 	goto error2;
3418 
3419     status = MakeLinearGradientBrush(gradient, trBrush, &lineGradient);
3420     if (status != Ok)
3421 	goto error3;
3422 
3423     status = DllExports._GdipCreatePen2(lineGradient, (REAL) outlineWidth,
3424 	UnitPixel, &pen);
3425     if (status != Ok)
3426 	goto error4;
3427 
3428     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3429     if (status != Ok)
3430 	goto error5;
3431 
3432     GetRectPath_Outline(path, tr, open, outlineWidth);
3433     DllExports._GdipSetPenStartCap(pen, LineCapSquare);
3434     DllExports._GdipSetPenEndCap(pen, LineCapSquare);
3435     DllExports._GdipDrawPath(graphics, pen, path);
3436 
3437 error5:
3438     DllExports._GdipDeletePen(pen);
3439 
3440 error4:
3441     DllExports._GdipDeleteBrush(path);
3442 
3443 error3:
3444     DllExports._GdipDeletePath(path);
3445 
3446 error2:
3447     DllExports._GdipDeleteGraphics(graphics);
3448 
3449 error1:
3450     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3451 }
3452 
3453 static void
GetRoundRectPath_Outline(GpPath * path,TreeRectangle tr,int rx,int ry,int open,int fudgeX,int fudgeY,int outlineWidth)3454 GetRoundRectPath_Outline(
3455     GpPath *path,		/* GDI+ path to set. */
3456     TreeRectangle tr,		/* Rectangle to draw. */
3457     int rx, int ry,		/* Corner radius. */
3458     int open,			/* RECT_OPEN_x flags. */
3459     int fudgeX,			/* Fix for "open" edge endpoints when */
3460     int fudgeY,			/* outlineWidth>1. */
3461     int outlineWidth
3462     )
3463 {
3464     REAL x = (REAL)tr.x, y = (REAL)tr.y, width = (REAL)tr.width, height = (REAL)tr.height;
3465     int drawW = (open & RECT_OPEN_W) == 0;
3466     int drawN = (open & RECT_OPEN_N) == 0;
3467     int drawE = (open & RECT_OPEN_E) == 0;
3468     int drawS = (open & RECT_OPEN_S) == 0;
3469 
3470     if (outlineWidth > 0) {
3471 	x += outlineWidth / 2.0f, y += outlineWidth / 2.0f;
3472 	width -= outlineWidth, height -= outlineWidth;
3473     } else {
3474 	width -= 1, height -= 1;
3475     }
3476 
3477     /* Simple case: draw all 4 corners and 4 edges */
3478     if (drawW && drawN && drawE && drawS) {
3479 	DllExports._GdipAddPathArc(path, x, y, rx*2.0f, ry*2.0f, 180.0f, 90.0f); /* top-left */
3480 	DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y, rx*2.0f, ry*2.0f, 270.0f, 90.0f); /* top-right */
3481 	DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 0.0f, 90.0f); /* bottom-right */
3482 	DllExports._GdipAddPathArc(path, x, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 90.0f, 90.0f); /* bottom-left */
3483 	DllExports._GdipClosePathFigure(path);
3484 
3485     /* Complicated case: some edges are "open" */
3486     } else {
3487 	GpPointF start[4], end[4]; /* start and end points of line segments*/
3488 	start[0].X = x, start[0].Y = y;
3489 	end[3] = start[0];
3490 	if (drawW && drawN) {
3491 	    start[0].X += rx;
3492 	    end[3].Y += ry;
3493 	} else {
3494 	    start[0].X -= fudgeX;
3495 	    end[3].Y -= fudgeY;
3496 	}
3497 	end[0].X = x + width, end[0].Y = y;
3498 	start[1]= end[0];
3499 	if (drawE && drawN) {
3500 	    end[0].X -= rx;
3501 	    start[1].Y += ry;
3502 	} else {
3503 	    end[0].X += fudgeX;
3504 	    start[1].Y -= fudgeY;
3505 	}
3506 	end[1].X = x + width, end[1].Y = y + height;
3507 	start[2] = end[1];
3508 	if (drawE && drawS) {
3509 	    end[1].Y -= ry;
3510 	    start[2].X -= rx;
3511 	} else {
3512 	    end[1].Y += fudgeY;
3513 	    start[2].X += fudgeX;
3514 	}
3515 	end[2].X = x, end[2].Y = y + height;
3516 	start[3] = end[2];
3517 	if (drawW && drawS) {
3518 	    end[2].X += rx;
3519 	    start[3].Y -= ry;
3520 	} else {
3521 	    end[2].X -= fudgeX;
3522 	    start[3].Y += fudgeY;
3523 	}
3524 
3525 	if (drawW && drawN)
3526 	    DllExports._GdipAddPathArc(path, x, y, rx*2.0f, ry*2.0f, 180.0f, 90.0f); /* top-left */
3527 	if (drawN)
3528 	    DllExports._GdipAddPathLine(path, start[0].X, start[0].Y, end[0].X, end[0].Y);
3529 	if (drawE && drawN)
3530 	    DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y, rx*2.0f, ry*2.0f, 270.0f, 90.0f); /* top-right */
3531 	if (drawE)
3532 	    DllExports._GdipAddPathLine(path, start[1].X, start[1].Y, end[1].X, end[1].Y);
3533 	else if (drawN)
3534 	    DllExports._GdipStartPathFigure(path);
3535 	if (drawE && drawS)
3536 	    DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 0.0f, 90.0f); /* bottom-right */
3537 	if (drawS)
3538 	    DllExports._GdipAddPathLine(path, start[2].X, start[2].Y, end[2].X, end[2].Y);
3539 	else if (drawE)
3540 	    DllExports._GdipStartPathFigure(path);
3541 	if (drawW && drawS)
3542 	    DllExports._GdipAddPathArc(path, x, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 90.0f, 90.0f); /* bottom-left */
3543 	if (drawW)
3544 	    DllExports._GdipAddPathLine(path, start[3].X, start[3].Y, end[3].X, end[3].Y);
3545     }
3546 }
3547 
3548 void
Tree_DrawRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,XColor * xcolor,TreeRectangle tr,int outlineWidth,int rx,int ry,int open)3549 Tree_DrawRoundRect(
3550     TreeCtrl *tree,		/* Widget info. */
3551     TreeDrawable td,		/* Where to draw. */
3552     TreeClip *clip,		/* Clipping area or NULL. */
3553     XColor *xcolor,		/* Color. */
3554     TreeRectangle tr,		/* Rectangle to draw. */
3555     int outlineWidth,		/* Width of outline. */
3556     int rx, int ry,		/* Corner radius */
3557     int open			/* RECT_OPEN_x flags */
3558     )
3559 {
3560     HDC hDC;
3561     TkWinDCState dcState;
3562     TreeClipStateGraphics clipState;
3563     GpGraphics *graphics;
3564     GpPath *path;
3565     ARGB color;
3566     GpPen *pen;
3567     GpStatus status;
3568     int i;
3569 
3570     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3571 	GC gc = Tk_GCForColor(xcolor, td.drawable);
3572 	Tree_DrawRoundRectX11(tree, td, clip, gc, tr, outlineWidth, rx, ry, open);
3573 	return;
3574     }
3575 
3576     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3577 
3578     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3579     if (status != Ok)
3580 	goto error1;
3581 
3582     status = DllExports._GdipCreatePath(FillModeAlternate, &path);
3583     if (status != Ok)
3584 	goto error2;
3585 
3586     color = MakeGDIPlusColor(xcolor,1.0f);
3587     status = DllExports._GdipCreatePen1(color, 1, UnitPixel, &pen);
3588     if (status != Ok)
3589 	goto error3;
3590 
3591     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3592     if (status != Ok)
3593 	goto error3;
3594 
3595     GetRoundRectPath_Outline(path, tr, rx, ry, open, 0, 0, 0);
3596     DllExports._GdipDrawPath(graphics, pen, path);
3597 
3598     /* http://www.codeproject.com/KB/GDI-plus/RoundRect.aspx */
3599     for (i = 1; i < outlineWidth; i++) {
3600 	tr.x += 1, tr.width -= 2;
3601 	DllExports._GdipResetPath(path);
3602 	GetRoundRectPath_Outline(path, tr, rx, ry, open, i, i-1, 0);
3603 	DllExports._GdipDrawPath(graphics, pen, path);
3604 
3605 	tr.y += 1, tr.height -= 2;
3606 	DllExports._GdipResetPath(path);
3607 	GetRoundRectPath_Outline(path, tr, rx, ry, open, i, i, 0);
3608 	DllExports._GdipDrawPath(graphics, pen, path);
3609     }
3610 
3611     DllExports._GdipDeletePen(pen);
3612 
3613 error3:
3614     DllExports._GdipDeletePath(path);
3615 
3616 error2:
3617     DllExports._GdipDeleteGraphics(graphics);
3618 
3619 error1:
3620     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3621 }
3622 
3623 void
TreeGradient_DrawRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr,int outlineWidth,int rx,int ry,int open)3624 TreeGradient_DrawRoundRect(
3625     TreeCtrl *tree,		/* Widget info. */
3626     TreeDrawable td,		/* Where to draw. */
3627     TreeClip *clip,		/* Clipping area or NULL. */
3628     TreeGradient gradient,	/* Gradient. */
3629     TreeRectangle trBrush,	/* Brush bounds. */
3630     TreeRectangle tr,		/* Rectangle to draw. */
3631     int outlineWidth,		/* Width of outline. */
3632     int rx, int ry,		/* Corner radius */
3633     int open			/* RECT_OPEN_x flags */
3634     )
3635 {
3636     HDC hDC;
3637     TkWinDCState dcState;
3638     TreeClipStateGraphics clipState;
3639     GpGraphics *graphics;
3640     GpPath *path;
3641     GpLineGradient *lineGradient;
3642     GpPen *pen;
3643     GpStatus status;
3644     int i;
3645     int canRepairCorners;
3646 
3647     if (gradient->stopArrPtr == NULL || gradient->stopArrPtr->nstops < 2)
3648 	return;
3649 
3650     /* Draw nothing if the brush is zero-sized. */
3651     if (trBrush.width <= 0 || trBrush.height <= 0)
3652 	return;
3653 
3654     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3655 	XColor *xcolor = gradient->stopArrPtr->stops[0]->color; /* Use the first stop color */
3656 	GC gc = Tk_GCForColor(xcolor, td.drawable);
3657 	Tree_DrawRoundRectX11(tree, td, clip, gc, tr, outlineWidth, rx, ry, open);
3658 	return;
3659     }
3660 
3661     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3662 
3663     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3664     if (status != Ok)
3665 	goto error1;
3666 
3667     status = DllExports._GdipCreatePath(FillModeAlternate, &path);
3668     if (status != Ok)
3669 	goto error2;
3670 
3671     status = MakeLinearGradientBrush(gradient, trBrush, &lineGradient);
3672     if (status != Ok)
3673 	goto error3;
3674 
3675     canRepairCorners = (outlineWidth == 1) || TreeGradient_IsOpaque(tree, gradient);
3676 
3677     status = DllExports._GdipCreatePen2(lineGradient, canRepairCorners ?
3678 	1.0f : (REAL) outlineWidth, UnitPixel, &pen);
3679     if (status != Ok)
3680 	goto error4;
3681 
3682     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3683     if (status != Ok)
3684 	goto error5;
3685 
3686     GetRoundRectPath_Outline(path, tr, rx, ry, open, 0, 0,
3687 	canRepairCorners ? 0 : outlineWidth);
3688 
3689     DllExports._GdipDrawPath(graphics, pen, path);
3690 
3691     /* http://www.codeproject.com/KB/GDI-plus/RoundRect.aspx */
3692     if (canRepairCorners) {
3693 	for (i = 1; i < outlineWidth; i++) {
3694 	    tr.x += 1, tr.width -= 2;
3695 	    DllExports._GdipResetPath(path);
3696 	    GetRoundRectPath_Outline(path, tr, rx, ry, open, i, i-1, 0);
3697 	    DllExports._GdipDrawPath(graphics, pen, path);
3698 
3699 	    tr.y += 1, tr.height -= 2;
3700 	    DllExports._GdipResetPath(path);
3701 	    GetRoundRectPath_Outline(path, tr, rx, ry, open, i, i, 0);
3702 	    DllExports._GdipDrawPath(graphics, pen, path);
3703 	}
3704     }
3705 
3706 error5:
3707     DllExports._GdipDeletePen(pen);
3708 
3709 error4:
3710     DllExports._GdipDeleteBrush(path);
3711 
3712 error3:
3713     DllExports._GdipDeletePath(path);
3714 
3715 error2:
3716     DllExports._GdipDeleteGraphics(graphics);
3717 
3718 error1:
3719     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3720 }
3721 
3722 #define ROUND_RECT_SYMMETRY_HACK
3723 
3724 /* This returns a path 1-pixel smaller on the right and bottom edges than
3725  * it should be.
3726  * For some reason GdipFillPath produces different (and asymmetric) results
3727  * than GdipDrawPath.  So after filling the round rectangle with this path
3728  * GetRoundRectPath_Outline should be called to paint the right and bottom
3729  * edges. */
3730 /* http://www.codeproject.com/KB/GDI-plus/RoundRect.aspx */
3731 static void
GetRoundRectPath_Fill(GpPath * path,TreeRectangle tr,int rx,int ry,int open,int rrhack)3732 GetRoundRectPath_Fill(
3733     GpPath *path,		/* GDI+ path to set. */
3734     TreeRectangle tr,		/* Rectangle to paint. */
3735     int rx, int ry,		/* Corner radius. */
3736     int open			/* RECT_OPEN_x flags. */
3737 #ifdef ROUND_RECT_SYMMETRY_HACK
3738     , int rrhack
3739 #endif
3740     )
3741 {
3742     REAL x = (REAL)tr.x, y = (REAL)tr.y, width = (REAL)tr.width, height = (REAL)tr.height;
3743     int drawW = (open & RECT_OPEN_W) == 0;
3744     int drawN = (open & RECT_OPEN_N) == 0;
3745     int drawE = (open & RECT_OPEN_E) == 0;
3746     int drawS = (open & RECT_OPEN_S) == 0;
3747 
3748     /* Simple case: draw all 4 corners and 4 edges */
3749     if (drawW && drawN && drawE && drawS) {
3750 #ifdef ROUND_RECT_SYMMETRY_HACK
3751 	if (rrhack)
3752 	    width -= 1, height -= 1;
3753 #endif
3754 	DllExports._GdipAddPathArc(path, x, y, rx*2.0f, ry*2.0f, 180.0f, 90.0f); /* top-left */
3755 	DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y, rx*2.0f, ry*2.0f, 270.0f, 90.0f); /* top-right */
3756 	DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 0.0f, 90.0f); /* bottom-right */
3757 	DllExports._GdipAddPathArc(path, x, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 90.0f, 90.0f); /* bottom-left */
3758 	DllExports._GdipClosePathFigure(path);
3759 
3760     /* Complicated case: some edges are "open" */
3761     } else {
3762 	GpPointF start[4], end[4]; /* start and end points of line segments*/
3763 #ifdef ROUND_RECT_SYMMETRY_HACK
3764 	if (rrhack) {
3765 	    if (drawE)
3766 		width -= 1;
3767 	    if (drawS)
3768 		height -= 1;
3769 	}
3770 #endif
3771 	start[0].X = x, start[0].Y = y;
3772 	end[3] = start[0];
3773 	if (drawW && drawN) {
3774 	    start[0].X += rx;
3775 	    end[3].Y += ry;
3776 	}
3777 	end[0].X = x + width, end[0].Y = y;
3778 	start[1]= end[0];
3779 	if (drawE && drawN) {
3780 	    end[0].X -= rx;
3781 	    start[1].Y += ry;
3782 	}
3783 	end[1].X = x + width, end[1].Y = y + height;
3784 	start[2] = end[1];
3785 	if (drawE && drawS) {
3786 	    end[1].Y -= ry;
3787 	    start[2].X -= rx;
3788 	}
3789 	end[2].X = x, end[2].Y = y + height;
3790 	start[3] = end[2];
3791 	if (drawW && drawS) {
3792 	    end[2].X += rx;
3793 	    start[3].Y -= ry;
3794 	}
3795 
3796 	if (drawW && drawN)
3797 	    DllExports._GdipAddPathArc(path, x, y, rx*2.0f, ry*2.0f, 180.0f, 90.0f); /* top-left */
3798 	DllExports._GdipAddPathLine(path, start[0].X, start[0].Y, end[0].X, end[0].Y);
3799 	if (drawE && drawN)
3800 	    DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y, rx*2.0f, ry*2.0f, 270.0f, 90.0f); /* top-right */
3801 	DllExports._GdipAddPathLine(path, start[1].X, start[1].Y, end[1].X, end[1].Y);
3802 	if (drawE && drawS)
3803 	    DllExports._GdipAddPathArc(path, x + width - rx*2.0f, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 0.0f, 90.0f); /* bottom-right */
3804 	DllExports._GdipAddPathLine(path, start[2].X, start[2].Y, end[2].X, end[2].Y);
3805 	if (drawW && drawS)
3806 	    DllExports._GdipAddPathArc(path, x, y + height - ry*2.0f, rx*2.0f, ry*2.0f, 90.0f, 90.0f); /* bottom-left */
3807 	DllExports._GdipAddPathLine(path, start[3].X, start[3].Y, end[3].X, end[3].Y);
3808     }
3809 }
3810 
3811 void
Tree_FillRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,XColor * xcolor,TreeRectangle tr,int rx,int ry,int open)3812 Tree_FillRoundRect(
3813     TreeCtrl *tree,		/* Widget info. */
3814     TreeDrawable td,		/* Where to draw. */
3815     TreeClip *clip,		/* Clipping area or NULL. */
3816     XColor *xcolor,		/* Color. */
3817     TreeRectangle tr,		/* Recangle to paint. */
3818     int rx, int ry,		/* Corner radius */
3819     int open			/* RECT_OPEN_x flags */
3820     )
3821 {
3822     HDC hDC;
3823     TkWinDCState dcState;
3824     TreeClipStateGraphics clipState;
3825     GpGraphics *graphics;
3826     GpPath *path;
3827     ARGB color;
3828     GpSolidFill *brush;
3829 #ifdef ROUND_RECT_SYMMETRY_HACK
3830     GpPen *pen;
3831 #endif
3832     GpStatus status;
3833 
3834     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3835 	GC gc = Tk_GCForColor(xcolor, td.drawable);
3836 	Tree_FillRoundRectX11(tree, td, clip, gc, tr, rx, ry, open);
3837 	return;
3838     }
3839 
3840     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3841 
3842     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3843     if (status != Ok)
3844 	goto error1;
3845 
3846     status = DllExports._GdipCreatePath(FillModeAlternate, &path);
3847     if (status != Ok)
3848 	goto error2;
3849 
3850     color = MakeGDIPlusColor(xcolor,1.0f);
3851     status = DllExports._GdipCreateSolidFill(color, &brush);
3852     if (status != Ok)
3853 	goto error3;
3854 
3855 #if !defined(ROUND_RECT_SYMMETRY_HACK) && 0
3856     /* SmoothingModeHighQuality and SmoothingModeAntiAlias seem the same. */
3857     DllExports._GdipSetSmoothingMode(graphics, SmoothingModeHighQuality);
3858 
3859     /* Antialiasing paints outside the rectangle.  If I clip drawing to the
3860      * rectangle I still get artifacts on the "open" edges. */
3861 //    status = DllExports._GdipSetClipRectI(graphics,
3862 //	tr.x, tr.y, tr.width, tr.height, CombineModeReplace);
3863 #endif
3864 
3865     GetRoundRectPath_Fill(path, tr, rx, ry, open
3866 #ifdef ROUND_RECT_SYMMETRY_HACK
3867 	, 1
3868 #endif
3869     );
3870 
3871     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3872     if (status != Ok)
3873 	goto error4;
3874 
3875     DllExports._GdipFillPath(graphics, brush, path);
3876 
3877 #ifdef ROUND_RECT_SYMMETRY_HACK
3878     status = DllExports._GdipCreatePen1(color, 1, UnitPixel, &pen);
3879     if (status != Ok)
3880 	goto error4;
3881 
3882     /* See comments above for why this is done */
3883     DllExports._GdipResetPath(path);
3884     GetRoundRectPath_Outline(path, tr, rx, ry, open, 0, 0, 0);
3885     DllExports._GdipDrawPath(graphics, pen, path);
3886 
3887     DllExports._GdipDeletePen(pen);
3888 #endif /* ROUND_RECT_SYMMETRY_HACK */
3889 
3890 error4:
3891     DllExports._GdipDeleteBrush(brush);
3892 
3893 error3:
3894     DllExports._GdipDeletePath(path);
3895 
3896 error2:
3897     DllExports._GdipDeleteGraphics(graphics);
3898 
3899 error1:
3900     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3901 }
3902 
3903 void
TreeGradient_FillRoundRect(TreeCtrl * tree,TreeDrawable td,TreeClip * clip,TreeGradient gradient,TreeRectangle trBrush,TreeRectangle tr,int rx,int ry,int open)3904 TreeGradient_FillRoundRect(
3905     TreeCtrl *tree,		/* Widget info. */
3906     TreeDrawable td,		/* Where to draw. */
3907     TreeClip *clip,		/* Clipping area or NULL. */
3908     TreeGradient gradient,	/* Gradient token. */
3909     TreeRectangle trBrush,	/* Brush bounds. */
3910     TreeRectangle tr,		/* Rectangle to paint. */
3911     int rx, int ry,		/* Corner radius */
3912     int open			/* RECT_OPEN_x flags */
3913     )
3914 {
3915     HDC hDC;
3916     TkWinDCState dcState;
3917     TreeClipStateGraphics clipState;
3918     GpGraphics *graphics;
3919     GpPath *path;
3920     GpLineGradient *lineGradient;
3921     GpPen *pen;
3922     GpStatus status;
3923     int canRepairCorners;
3924 
3925     if (gradient->stopArrPtr == NULL || gradient->stopArrPtr->nstops < 2)
3926 	return;
3927 
3928     /* Draw nothing if the brush is zero-sized. */
3929     if (trBrush.width <= 0 || trBrush.height <= 0)
3930 	return;
3931 
3932     if (!tree->nativeGradients || (DllExports.handle == NULL)) {
3933 	TreeGradient_FillRoundRectX11(tree, td, NULL, gradient, trBrush, tr,
3934 	    rx, ry, open);
3935 	return;
3936     }
3937 
3938     hDC = TkWinGetDrawableDC(tree->display, td.drawable, &dcState);
3939 
3940     status = DllExports._GdipCreateFromHDC(hDC, &graphics);
3941     if (status != Ok)
3942 	goto error1;
3943 
3944     status = DllExports._GdipCreatePath(FillModeAlternate, &path);
3945     if (status != Ok)
3946 	goto error2;
3947 
3948     status = MakeLinearGradientBrush(gradient, trBrush, &lineGradient);
3949     if (status != Ok)
3950 	goto error3;
3951 
3952     /* Filling the rounded rectangle gives asymmetric results at the corners.
3953      * If the gradient is fully opaque then the corners can be "repaired" by
3954      * drawing a 1-pixel thick outline after filling the shape. */
3955     canRepairCorners = TreeGradient_IsOpaque(tree, gradient);
3956 
3957     GetRoundRectPath_Fill(path, tr, rx, ry, open
3958 #ifdef ROUND_RECT_SYMMETRY_HACK
3959 	, canRepairCorners
3960 #endif
3961     );
3962 
3963     status = TreeClip_ToGraphics(tree, clip, graphics, &clipState);
3964     if (status != Ok)
3965 	goto error4;
3966 
3967     DllExports._GdipFillPath(graphics, lineGradient, path);
3968 
3969     if (canRepairCorners) {
3970 	status = DllExports._GdipCreatePen2(lineGradient, 1, UnitPixel, &pen);
3971 	if (status != Ok)
3972 	    goto error4;
3973 
3974 	DllExports._GdipResetPath(path);
3975 	GetRoundRectPath_Outline(path, tr, rx, ry, open, 0, 0, 0);
3976 	DllExports._GdipDrawPath(graphics, pen, path);
3977 
3978 	DllExports._GdipDeletePen(pen);
3979     }
3980 
3981 error4:
3982     DllExports._GdipDeleteBrush(lineGradient);
3983 
3984 error3:
3985     DllExports._GdipDeletePath(path);
3986 
3987 error2:
3988     DllExports._GdipDeleteGraphics(graphics);
3989 
3990 error1:
3991     TkWinReleaseDrawableDC(td.drawable, hDC, &dcState);
3992 }
3993