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