1 /*
2 * Tk theme engine which uses the Windows XP "Visual Styles" API
3 * Adapted from Georgios Petasis' XP theme patch.
4 *
5 * Copyright © 2003 Georgios Petasis, petasis@iit.demokritos.gr.
6 * Copyright © 2003 Joe English
7 * Copyright © 2003 Pat Thoyts
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * See also:
13 *
14 * <URL: http://msdn.microsoft.com/library/en-us/
15 * shellcc/platform/commctls/userex/refentry.asp >
16 */
17
18 #include <tkWinInt.h>
19 #ifndef HAVE_UXTHEME_H
20 /* Stub for platforms that lack the XP theme API headers: */
TtkXPTheme_Init(Tcl_Interp * interp,HWND hwnd)21 int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd) { return TCL_OK; }
22 #else
23
24 #include <windows.h>
25 #include <uxtheme.h>
26 #if defined(HAVE_VSSYM32_H) || _MSC_VER > 1500
27 # include <vssym32.h>
28 #else
29 # include <tmschema.h>
30 #endif
31
32 #include "ttk/ttkTheme.h"
33
34 typedef HTHEME (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd,
35 LPCWSTR pszClassList);
36 typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme);
37 typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme,
38 HDC hdc, int iPartId, int iStateId, const RECT *pRect,
39 OPTIONAL const RECT *pClipRect);
40 typedef HRESULT (STDAPICALLTYPE GetThemePartSizeProc)(HTHEME,HDC,
41 int iPartId, int iStateId,
42 RECT *prc, enum THEMESIZE eSize, SIZE *psz);
43 typedef int (STDAPICALLTYPE GetThemeSysSizeProc)(HTHEME,int);
44 /* GetThemeTextExtent and DrawThemeText only used with BROKEN_TEXT_ELEMENT */
45 typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc,
46 int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
47 DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtent);
48 typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc,
49 int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
50 DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
51 typedef BOOL (STDAPICALLTYPE IsThemeActiveProc)(void);
52 typedef BOOL (STDAPICALLTYPE IsAppThemedProc)(void);
53
54 typedef struct
55 {
56 OpenThemeDataProc *OpenThemeData;
57 CloseThemeDataProc *CloseThemeData;
58 GetThemePartSizeProc *GetThemePartSize;
59 GetThemeSysSizeProc *GetThemeSysSize;
60 DrawThemeBackgroundProc *DrawThemeBackground;
61 DrawThemeTextProc *DrawThemeText;
62 GetThemeTextExtentProc *GetThemeTextExtent;
63 IsThemeActiveProc *IsThemeActive;
64 IsAppThemedProc *IsAppThemed;
65
66 HWND stubWindow;
67 } XPThemeProcs;
68
69 typedef struct
70 {
71 HINSTANCE hlibrary;
72 XPThemeProcs *procs;
73 } XPThemeData;
74
75 /*
76 *----------------------------------------------------------------------
77 *
78 * LoadXPThemeProcs --
79 * Initialize XP theming support.
80 *
81 * XP theme support is included in UXTHEME.DLL
82 * We dynamically load this DLL at runtime instead of linking
83 * to it at build-time.
84 *
85 * Returns:
86 * A pointer to an XPThemeProcs table if successful, NULL otherwise.
87 */
88
89 static XPThemeProcs *
LoadXPThemeProcs(HINSTANCE * phlib)90 LoadXPThemeProcs(HINSTANCE *phlib)
91 {
92 /*
93 * Load the library "uxtheme.dll", where the native widget
94 * drawing routines are implemented. This will only succeed
95 * if we are running at least on Windows XP.
96 */
97 HINSTANCE handle;
98 *phlib = handle = LoadLibraryW(L"uxtheme.dll");
99 if (handle != 0)
100 {
101 /*
102 * We have successfully loaded the library. Proceed in storing the
103 * addresses of the functions we want to use.
104 */
105 XPThemeProcs *procs = (XPThemeProcs *)ckalloc(sizeof(XPThemeProcs));
106 #define LOADPROC(name) \
107 (0 != (procs->name = (name ## Proc *)(void *)GetProcAddress(handle, #name) ))
108
109 if ( LOADPROC(OpenThemeData)
110 && LOADPROC(CloseThemeData)
111 && LOADPROC(GetThemePartSize)
112 && LOADPROC(GetThemeSysSize)
113 && LOADPROC(DrawThemeBackground)
114 && LOADPROC(GetThemeTextExtent)
115 && LOADPROC(DrawThemeText)
116 && LOADPROC(IsThemeActive)
117 && LOADPROC(IsAppThemed)
118 )
119 {
120 return procs;
121 }
122 #undef LOADPROC
123 ckfree(procs);
124 }
125 return 0;
126 }
127
128 /*
129 * XPThemeDeleteProc --
130 *
131 * Release any theme allocated resources.
132 */
133
134 static void
XPThemeDeleteProc(void * clientData)135 XPThemeDeleteProc(void *clientData)
136 {
137 XPThemeData *themeData = (XPThemeData *)clientData;
138 FreeLibrary(themeData->hlibrary);
139 ckfree(clientData);
140 }
141
142 static int
XPThemeEnabled(Ttk_Theme theme,void * clientData)143 XPThemeEnabled(Ttk_Theme theme, void *clientData)
144 {
145 XPThemeData *themeData = (XPThemeData *)clientData;
146 int active = themeData->procs->IsThemeActive();
147 int themed = themeData->procs->IsAppThemed();
148 (void)theme;
149
150 return (active && themed);
151 }
152
153 /*
154 * BoxToRect --
155 * Helper routine. Returns a RECT data structure.
156 */
157 static RECT
BoxToRect(Ttk_Box b)158 BoxToRect(Ttk_Box b)
159 {
160 RECT rc;
161 rc.top = b.y;
162 rc.left = b.x;
163 rc.bottom = b.y + b.height;
164 rc.right = b.x + b.width;
165 return rc;
166 }
167
168 /*
169 * Map Tk state bitmaps to XP style enumerated values.
170 */
171 static const Ttk_StateTable null_statemap[] = { {0,0,0} };
172
173 /*
174 * Pushbuttons (Tk: "Button")
175 */
176 static const Ttk_StateTable pushbutton_statemap[] =
177 {
178 { PBS_DISABLED, TTK_STATE_DISABLED, 0 },
179 { PBS_PRESSED, TTK_STATE_PRESSED, 0 },
180 { PBS_HOT, TTK_STATE_ACTIVE, 0 },
181 { PBS_DEFAULTED, TTK_STATE_ALTERNATE, 0 },
182 { PBS_NORMAL, 0, 0 }
183 };
184
185 /*
186 * Checkboxes (Tk: "Checkbutton")
187 */
188 static const Ttk_StateTable checkbox_statemap[] =
189 {
190 {CBS_MIXEDDISABLED, TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0},
191 {CBS_MIXEDPRESSED, TTK_STATE_ALTERNATE|TTK_STATE_PRESSED, 0},
192 {CBS_MIXEDHOT, TTK_STATE_ALTERNATE|TTK_STATE_ACTIVE, 0},
193 {CBS_MIXEDNORMAL, TTK_STATE_ALTERNATE, 0},
194 {CBS_CHECKEDDISABLED, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0},
195 {CBS_CHECKEDPRESSED, TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0},
196 {CBS_CHECKEDHOT, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0},
197 {CBS_CHECKEDNORMAL, TTK_STATE_SELECTED, 0},
198 {CBS_UNCHECKEDDISABLED, TTK_STATE_DISABLED, 0},
199 {CBS_UNCHECKEDPRESSED, TTK_STATE_PRESSED, 0},
200 {CBS_UNCHECKEDHOT, TTK_STATE_ACTIVE, 0},
201 {CBS_UNCHECKEDNORMAL, 0,0 }
202 };
203
204 /*
205 * Radiobuttons:
206 */
207 static const Ttk_StateTable radiobutton_statemap[] =
208 {
209 {RBS_UNCHECKEDDISABLED, TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0},
210 {RBS_UNCHECKEDNORMAL, TTK_STATE_ALTERNATE, 0},
211 {RBS_CHECKEDDISABLED, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0},
212 {RBS_CHECKEDPRESSED, TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0},
213 {RBS_CHECKEDHOT, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0},
214 {RBS_CHECKEDNORMAL, TTK_STATE_SELECTED, 0},
215 {RBS_UNCHECKEDDISABLED, TTK_STATE_DISABLED, 0},
216 {RBS_UNCHECKEDPRESSED, TTK_STATE_PRESSED, 0},
217 {RBS_UNCHECKEDHOT, TTK_STATE_ACTIVE, 0},
218 {RBS_UNCHECKEDNORMAL, 0,0 }
219 };
220
221 /*
222 * Groupboxes (tk: "frame")
223 */
224 static const Ttk_StateTable groupbox_statemap[] =
225 {
226 {GBS_DISABLED, TTK_STATE_DISABLED, 0},
227 {GBS_NORMAL, 0,0 }
228 };
229
230 /*
231 * Edit fields (tk: "entry")
232 */
233 static const Ttk_StateTable edittext_statemap[] =
234 {
235 { ETS_DISABLED, TTK_STATE_DISABLED, 0 },
236 { ETS_READONLY, TTK_STATE_READONLY, 0 },
237 { ETS_FOCUSED, TTK_STATE_FOCUS, 0 },
238 { ETS_HOT, TTK_STATE_ACTIVE, 0 },
239 { ETS_NORMAL, 0, 0 }
240 /* NOT USED: ETS_ASSIST, ETS_SELECTED */
241 };
242
243 /*
244 * Combobox text field statemap:
245 * Same as edittext_statemap, but doesn't use ETS_READONLY
246 * (fixes: #1032409)
247 */
248 static const Ttk_StateTable combotext_statemap[] =
249 {
250 { ETS_DISABLED, TTK_STATE_DISABLED, 0 },
251 { ETS_FOCUSED, TTK_STATE_FOCUS, 0 },
252 { ETS_HOT, TTK_STATE_ACTIVE, 0 },
253 { ETS_NORMAL, 0, 0 }
254 };
255
256 /*
257 * Combobox button: (CBP_DROPDOWNBUTTON)
258 */
259 static const Ttk_StateTable combobox_statemap[] = {
260 { CBXS_DISABLED, TTK_STATE_DISABLED, 0 },
261 { CBXS_PRESSED, TTK_STATE_PRESSED, 0 },
262 { CBXS_HOT, TTK_STATE_ACTIVE, 0 },
263 { CBXS_HOT, TTK_STATE_HOVER, 0 },
264 { CBXS_NORMAL, 0, 0 }
265 };
266
267 /*
268 * Toolbar buttons (TP_BUTTON):
269 */
270 static const Ttk_StateTable toolbutton_statemap[] = {
271 { TS_DISABLED, TTK_STATE_DISABLED, 0 },
272 { TS_PRESSED, TTK_STATE_PRESSED, 0 },
273 { TS_HOTCHECKED, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0 },
274 { TS_CHECKED, TTK_STATE_SELECTED, 0 },
275 { TS_HOT, TTK_STATE_ACTIVE, 0 },
276 { TS_NORMAL, 0,0 }
277 };
278
279 /*
280 * Scrollbars (Tk: "Scrollbar.thumb")
281 */
282 static const Ttk_StateTable scrollbar_statemap[] =
283 {
284 { SCRBS_DISABLED, TTK_STATE_DISABLED, 0 },
285 { SCRBS_PRESSED, TTK_STATE_PRESSED, 0 },
286 { SCRBS_HOT, TTK_STATE_ACTIVE, 0 },
287 { SCRBS_NORMAL, 0, 0 }
288 };
289
290 static const Ttk_StateTable uparrow_statemap[] =
291 {
292 { ABS_UPDISABLED, TTK_STATE_DISABLED, 0 },
293 { ABS_UPPRESSED, TTK_STATE_PRESSED, 0 },
294 { ABS_UPHOT, TTK_STATE_ACTIVE, 0 },
295 { ABS_UPNORMAL, 0, 0 }
296 };
297
298 static const Ttk_StateTable downarrow_statemap[] =
299 {
300 { ABS_DOWNDISABLED, TTK_STATE_DISABLED, 0 },
301 { ABS_DOWNPRESSED, TTK_STATE_PRESSED, 0 },
302 { ABS_DOWNHOT, TTK_STATE_ACTIVE, 0 },
303 { ABS_DOWNNORMAL, 0, 0 }
304 };
305
306 static const Ttk_StateTable leftarrow_statemap[] =
307 {
308 { ABS_LEFTDISABLED, TTK_STATE_DISABLED, 0 },
309 { ABS_LEFTPRESSED, TTK_STATE_PRESSED, 0 },
310 { ABS_LEFTHOT, TTK_STATE_ACTIVE, 0 },
311 { ABS_LEFTNORMAL, 0, 0 }
312 };
313
314 static const Ttk_StateTable rightarrow_statemap[] =
315 {
316 { ABS_RIGHTDISABLED,TTK_STATE_DISABLED, 0 },
317 { ABS_RIGHTPRESSED, TTK_STATE_PRESSED, 0 },
318 { ABS_RIGHTHOT, TTK_STATE_ACTIVE, 0 },
319 { ABS_RIGHTNORMAL, 0, 0 }
320 };
321
322 static const Ttk_StateTable spinbutton_statemap[] =
323 {
324 { DNS_DISABLED, TTK_STATE_DISABLED, 0 },
325 { DNS_PRESSED, TTK_STATE_PRESSED, 0 },
326 { DNS_HOT, TTK_STATE_ACTIVE, 0 },
327 { DNS_NORMAL, 0, 0 },
328 };
329
330 /*
331 * Trackbar thumb: (Tk: "scale slider")
332 */
333 static const Ttk_StateTable scale_statemap[] =
334 {
335 { TUS_DISABLED, TTK_STATE_DISABLED, 0 },
336 { TUS_PRESSED, TTK_STATE_PRESSED, 0 },
337 { TUS_FOCUSED, TTK_STATE_FOCUS, 0 },
338 { TUS_HOT, TTK_STATE_ACTIVE, 0 },
339 { TUS_NORMAL, 0, 0 }
340 };
341
342 static const Ttk_StateTable tabitem_statemap[] =
343 {
344 { TIS_DISABLED, TTK_STATE_DISABLED, 0 },
345 { TIS_SELECTED, TTK_STATE_SELECTED, 0 },
346 { TIS_HOT, TTK_STATE_ACTIVE, 0 },
347 { TIS_FOCUSED, TTK_STATE_FOCUS, 0 },
348 { TIS_NORMAL, 0, 0 },
349 };
350
351
352 /*
353 *----------------------------------------------------------------------
354 * +++ Element data:
355 *
356 * The following structure is passed as the 'clientData' pointer
357 * to most elements in this theme. It contains data relevant
358 * to a single XP Theme "part".
359 *
360 * <<NOTE-GetThemeMargins>>:
361 * In theory, we should be call GetThemeMargins(...TMT_CONTENTRECT...)
362 * to calculate the internal padding. In practice, this routine
363 * only seems to work properly for BP_PUSHBUTTON. So we hardcode
364 * the required padding at element registration time instead.
365 *
366 * The PAD_MARGINS flag bit determines whether the padding
367 * should be added on the inside (0) or outside (1) of the element.
368 *
369 * <<NOTE-GetThemePartSize>>:
370 * This gives bogus metrics for some parts (in particular,
371 * BP_PUSHBUTTONS). Set the IGNORE_THEMESIZE flag to skip this call.
372 */
373
374 typedef struct /* XP element specifications */
375 {
376 const char *elementName; /* Tk theme engine element name */
377 const Ttk_ElementSpec *elementSpec;
378 /* Element spec (usually GenericElementSpec) */
379 LPCWSTR className; /* Windows window class name */
380 int partId; /* BP_PUSHBUTTON, BP_CHECKBUTTON, etc. */
381 const Ttk_StateTable *statemap; /* Map Tk states to XP states */
382 Ttk_Padding padding; /* See NOTE-GetThemeMargins */
383 unsigned flags;
384 # define IGNORE_THEMESIZE 0x80000000U /* See NOTE-GetThemePartSize */
385 # define PAD_MARGINS 0x40000000U /* See NOTE-GetThemeMargins */
386 # define HEAP_ELEMENT 0x20000000U /* ElementInfo is on heap */
387 # define HALF_HEIGHT 0x10000000U /* Used by GenericSizedElements */
388 # define HALF_WIDTH 0x08000000U /* Used by GenericSizedElements */
389 } ElementInfo;
390
391 typedef struct
392 {
393 /*
394 * Static data, initialized when element is registered:
395 */
396 const ElementInfo *info;
397 XPThemeProcs *procs; /* Pointer to theme procedure table */
398
399 /*
400 * Dynamic data, allocated by InitElementData:
401 */
402 HTHEME hTheme;
403 HDC hDC;
404 HWND hwnd;
405
406 /* For TkWinDrawableReleaseDC: */
407 Drawable drawable;
408 TkWinDCState dcState;
409 } ElementData;
410
411 static ElementData *
NewElementData(XPThemeProcs * procs,const ElementInfo * info)412 NewElementData(XPThemeProcs *procs, const ElementInfo *info)
413 {
414 ElementData *elementData = (ElementData *)ckalloc(sizeof(ElementData));
415
416 elementData->procs = procs;
417 elementData->info = info;
418 elementData->hTheme = elementData->hDC = 0;
419
420 return elementData;
421 }
422
423 /*
424 * Destroy elements. If the element was created by the element factory
425 * then the info member is dynamically allocated. Otherwise it was
426 * static data from the C object and only the ElementData needs freeing.
427 */
DestroyElementData(void * clientData)428 static void DestroyElementData(void *clientData)
429 {
430 ElementData *elementData = (ElementData *)clientData;
431 if (elementData->info->flags & HEAP_ELEMENT) {
432 ckfree((char *)elementData->info->statemap);
433 ckfree((char *)elementData->info->className);
434 ckfree((char *)elementData->info->elementName);
435 ckfree((char *)elementData->info);
436 }
437 ckfree(clientData);
438 }
439
440 /*
441 * InitElementData --
442 * Looks up theme handle. If Drawable argument is non-NULL,
443 * also initializes DC.
444 *
445 * Returns:
446 * 1 on success, 0 on error.
447 * Caller must later call FreeElementData() so this element
448 * can be reused.
449 */
450
451 static int
InitElementData(ElementData * elementData,Tk_Window tkwin,Drawable d)452 InitElementData(ElementData *elementData, Tk_Window tkwin, Drawable d)
453 {
454 Window win = Tk_WindowId(tkwin);
455
456 if (win) {
457 elementData->hwnd = Tk_GetHWND(win);
458 } else {
459 elementData->hwnd = elementData->procs->stubWindow;
460 }
461
462 elementData->hTheme = elementData->procs->OpenThemeData(
463 elementData->hwnd, elementData->info->className);
464
465 if (!elementData->hTheme)
466 return 0;
467
468 elementData->drawable = d;
469 if (d != 0) {
470 elementData->hDC = TkWinGetDrawableDC(Tk_Display(tkwin), d,
471 &elementData->dcState);
472 }
473
474 return 1;
475 }
476
477 static void
FreeElementData(ElementData * elementData)478 FreeElementData(ElementData *elementData)
479 {
480 elementData->procs->CloseThemeData(elementData->hTheme);
481 if (elementData->drawable != 0) {
482 TkWinReleaseDrawableDC(
483 elementData->drawable, elementData->hDC, &elementData->dcState);
484 }
485 }
486
487 /*----------------------------------------------------------------------
488 * +++ Generic element implementation.
489 *
490 * Used for elements which are handled entirely by the XP Theme API,
491 * such as radiobutton and checkbutton indicators, scrollbar arrows, etc.
492 */
493
GenericElementSize(void * clientData,void * elementRecord,Tk_Window tkwin,int * widthPtr,int * heightPtr,Ttk_Padding * paddingPtr)494 static void GenericElementSize(
495 void *clientData, void *elementRecord, Tk_Window tkwin,
496 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
497 {
498 ElementData *elementData = (ElementData *)clientData;
499 HRESULT result;
500 SIZE size;
501 (void)elementRecord;
502
503 if (!InitElementData(elementData, tkwin, 0))
504 return;
505
506 if (!(elementData->info->flags & IGNORE_THEMESIZE)) {
507 result = elementData->procs->GetThemePartSize(
508 elementData->hTheme,
509 NULL,
510 elementData->info->partId,
511 Ttk_StateTableLookup(elementData->info->statemap, 0),
512 NULL /*RECT *prc*/,
513 TS_TRUE,
514 &size);
515
516 if (SUCCEEDED(result)) {
517 *widthPtr = size.cx;
518 *heightPtr = size.cy;
519 }
520 }
521
522 /* See NOTE-GetThemeMargins
523 */
524 *paddingPtr = elementData->info->padding;
525 if (elementData->info->flags & PAD_MARGINS) {
526 *widthPtr += Ttk_PaddingWidth(elementData->info->padding);
527 *heightPtr += Ttk_PaddingHeight(elementData->info->padding);
528 }
529 }
530
GenericElementDraw(void * clientData,void * elementRecord,Tk_Window tkwin,Drawable d,Ttk_Box b,unsigned int state)531 static void GenericElementDraw(
532 void *clientData, void *elementRecord, Tk_Window tkwin,
533 Drawable d, Ttk_Box b, unsigned int state)
534 {
535 ElementData *elementData = (ElementData *)clientData;
536 RECT rc;
537 (void)elementRecord;
538
539 if (!InitElementData(elementData, tkwin, d)) {
540 return;
541 }
542
543 if (elementData->info->flags & PAD_MARGINS) {
544 b = Ttk_PadBox(b, elementData->info->padding);
545 }
546 rc = BoxToRect(b);
547
548 elementData->procs->DrawThemeBackground(
549 elementData->hTheme,
550 elementData->hDC,
551 elementData->info->partId,
552 Ttk_StateTableLookup(elementData->info->statemap, state),
553 &rc,
554 NULL/*pContentRect*/);
555
556 FreeElementData(elementData);
557 }
558
559 static const Ttk_ElementSpec GenericElementSpec =
560 {
561 TK_STYLE_VERSION_2,
562 sizeof(NullElement),
563 TtkNullElementOptions,
564 GenericElementSize,
565 GenericElementDraw
566 };
567
568 /*----------------------------------------------------------------------
569 * +++ Sized element implementation.
570 *
571 * Used for elements which are handled entirely by the XP Theme API,
572 * but that require a fixed size adjustment.
573 * Note that GetThemeSysSize calls through to GetSystemMetrics
574 */
575
576 static void
GenericSizedElementSize(void * clientData,void * elementRecord,Tk_Window tkwin,int * widthPtr,int * heightPtr,Ttk_Padding * paddingPtr)577 GenericSizedElementSize(
578 void *clientData, void *elementRecord, Tk_Window tkwin,
579 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
580 {
581 ElementData *elementData = (ElementData *)clientData;
582
583 if (!InitElementData(elementData, tkwin, 0))
584 return;
585
586 GenericElementSize(clientData, elementRecord, tkwin,
587 widthPtr, heightPtr, paddingPtr);
588
589 *widthPtr = elementData->procs->GetThemeSysSize(NULL,
590 (elementData->info->flags >> 8) & 0xff);
591 *heightPtr = elementData->procs->GetThemeSysSize(NULL,
592 elementData->info->flags & 0xff);
593 if (elementData->info->flags & HALF_HEIGHT)
594 *heightPtr /= 2;
595 if (elementData->info->flags & HALF_WIDTH)
596 *widthPtr /= 2;
597 }
598
599 static const Ttk_ElementSpec GenericSizedElementSpec = {
600 TK_STYLE_VERSION_2,
601 sizeof(NullElement),
602 TtkNullElementOptions,
603 GenericSizedElementSize,
604 GenericElementDraw
605 };
606
607 /*----------------------------------------------------------------------
608 * +++ Spinbox arrow element.
609 * These are half-height scrollbar buttons.
610 */
611
612 static void
SpinboxArrowElementSize(void * clientData,void * elementRecord,Tk_Window tkwin,int * widthPtr,int * heightPtr,Ttk_Padding * paddingPtr)613 SpinboxArrowElementSize(
614 void *clientData, void *elementRecord, Tk_Window tkwin,
615 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
616 {
617 ElementData *elementData = (ElementData *)clientData;
618
619 if (!InitElementData(elementData, tkwin, 0))
620 return;
621
622 GenericSizedElementSize(clientData, elementRecord, tkwin,
623 widthPtr, heightPtr, paddingPtr);
624
625 /* force the arrow button height to half size */
626 *heightPtr /= 2;
627 }
628
629 static const Ttk_ElementSpec SpinboxArrowElementSpec = {
630 TK_STYLE_VERSION_2,
631 sizeof(NullElement),
632 TtkNullElementOptions,
633 SpinboxArrowElementSize,
634 GenericElementDraw
635 };
636
637 /*----------------------------------------------------------------------
638 * +++ Scrollbar thumb element.
639 * Same as a GenericElement, but don't draw in the disabled state.
640 */
641
ThumbElementDraw(void * clientData,void * elementRecord,Tk_Window tkwin,Drawable d,Ttk_Box b,unsigned int state)642 static void ThumbElementDraw(
643 void *clientData, void *elementRecord, Tk_Window tkwin,
644 Drawable d, Ttk_Box b, unsigned int state)
645 {
646 ElementData *elementData = (ElementData *)clientData;
647 unsigned stateId = Ttk_StateTableLookup(elementData->info->statemap, state);
648 RECT rc = BoxToRect(b);
649 (void)elementRecord;
650
651 /*
652 * Don't draw the thumb if we are disabled.
653 */
654 if (state & TTK_STATE_DISABLED)
655 return;
656
657 if (!InitElementData(elementData, tkwin, d))
658 return;
659
660 elementData->procs->DrawThemeBackground(elementData->hTheme,
661 elementData->hDC, elementData->info->partId, stateId,
662 &rc, NULL);
663
664 FreeElementData(elementData);
665 }
666
667 static const Ttk_ElementSpec ThumbElementSpec =
668 {
669 TK_STYLE_VERSION_2,
670 sizeof(NullElement),
671 TtkNullElementOptions,
672 GenericElementSize,
673 ThumbElementDraw
674 };
675
676 /*----------------------------------------------------------------------
677 * +++ Progress bar element.
678 * Increases the requested length of PP_CHUNK and PP_CHUNKVERT parts
679 * so that indeterminate progress bars show 3 bars instead of 1.
680 */
681
PbarElementSize(void * clientData,void * elementRecord,Tk_Window tkwin,int * widthPtr,int * heightPtr,Ttk_Padding * paddingPtr)682 static void PbarElementSize(
683 void *clientData, void *elementRecord, Tk_Window tkwin,
684 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
685 {
686 ElementData *elementData = (ElementData *)clientData;
687 int nBars = 3;
688
689 GenericElementSize(clientData, elementRecord, tkwin,
690 widthPtr, heightPtr, paddingPtr);
691
692 if (elementData->info->partId == PP_CHUNK) {
693 *widthPtr *= nBars;
694 } else if (elementData->info->partId == PP_CHUNKVERT) {
695 *heightPtr *= nBars;
696 }
697 }
698
699 static const Ttk_ElementSpec PbarElementSpec =
700 {
701 TK_STYLE_VERSION_2,
702 sizeof(NullElement),
703 TtkNullElementOptions,
704 PbarElementSize,
705 GenericElementDraw
706 };
707
708 /*----------------------------------------------------------------------
709 * +++ Notebook tab element.
710 * Same as generic element, with additional logic to select
711 * proper iPartID for the leftmost tab.
712 *
713 * Notes: TABP_TABITEMRIGHTEDGE (or TABP_TOPTABITEMRIGHTEDGE,
714 * which appears to be identical) should be used if the
715 * tab is exactly at the right edge of the notebook, but
716 * not if it's simply the rightmost tab. This information
717 * is not available.
718 *
719 * The TIS_* and TILES_* definitions are identical, so
720 * we can use the same statemap no matter what the partId.
721 */
TabElementDraw(void * clientData,void * elementRecord,Tk_Window tkwin,Drawable d,Ttk_Box b,unsigned int state)722 static void TabElementDraw(
723 void *clientData, void *elementRecord, Tk_Window tkwin,
724 Drawable d, Ttk_Box b, unsigned int state)
725 {
726 ElementData *elementData = (ElementData *)clientData;
727 int partId = elementData->info->partId;
728 RECT rc = BoxToRect(b);
729 (void)elementRecord;
730
731 if (!InitElementData(elementData, tkwin, d))
732 return;
733 if (state & TTK_STATE_USER1)
734 partId = TABP_TABITEMLEFTEDGE;
735 elementData->procs->DrawThemeBackground(
736 elementData->hTheme, elementData->hDC, partId,
737 Ttk_StateTableLookup(elementData->info->statemap, state), &rc, NULL);
738 FreeElementData(elementData);
739 }
740
741 static const Ttk_ElementSpec TabElementSpec =
742 {
743 TK_STYLE_VERSION_2,
744 sizeof(NullElement),
745 TtkNullElementOptions,
746 GenericElementSize,
747 TabElementDraw
748 };
749
750 /*----------------------------------------------------------------------
751 * +++ Tree indicator element.
752 *
753 * Generic element, but don't display at all if TTK_STATE_LEAF (=USER2) set
754 */
755
756 #define TTK_STATE_OPEN TTK_STATE_USER1
757 #define TTK_STATE_LEAF TTK_STATE_USER2
758
759 static const Ttk_StateTable header_statemap[] =
760 {
761 { HIS_PRESSED, TTK_STATE_PRESSED, 0 },
762 { HIS_HOT, TTK_STATE_ACTIVE, 0 },
763 { HIS_NORMAL, 0,0 },
764 };
765
766 static const Ttk_StateTable treeview_statemap[] =
767 {
768 { TREIS_DISABLED, TTK_STATE_DISABLED, 0 },
769 { TREIS_SELECTED, TTK_STATE_SELECTED, 0},
770 { TREIS_HOT, TTK_STATE_ACTIVE, 0 },
771 { TREIS_NORMAL, 0,0 },
772 };
773
774 static const Ttk_StateTable tvpglyph_statemap[] =
775 {
776 { GLPS_OPENED, TTK_STATE_OPEN, 0 },
777 { GLPS_CLOSED, 0,0 },
778 };
779
TreeIndicatorElementDraw(void * clientData,void * elementRecord,Tk_Window tkwin,Drawable d,Ttk_Box b,unsigned int state)780 static void TreeIndicatorElementDraw(
781 void *clientData, void *elementRecord, Tk_Window tkwin,
782 Drawable d, Ttk_Box b, unsigned int state)
783 {
784 if (!(state & TTK_STATE_LEAF)) {
785 GenericElementDraw(clientData,elementRecord,tkwin,d,b,state);
786 }
787 }
788
789 static const Ttk_ElementSpec TreeIndicatorElementSpec =
790 {
791 TK_STYLE_VERSION_2,
792 sizeof(NullElement),
793 TtkNullElementOptions,
794 GenericElementSize,
795 TreeIndicatorElementDraw
796 };
797
798 #ifdef BROKEN_TEXT_ELEMENT
799
800 /*
801 *----------------------------------------------------------------------
802 * Text element (does not work yet).
803 *
804 * According to "Using Windows XP Visual Styles", we need to select
805 * a font into the DC before calling DrawThemeText().
806 * There's just no easy way to get an HFONT out of a Tk_Font.
807 * Maybe GetThemeFont() would work?
808 *
809 */
810
811 typedef struct
812 {
813 Tcl_Obj *textObj;
814 Tcl_Obj *fontObj;
815 } TextElement;
816
817 static const Ttk_ElementOptionSpec TextElementOptions[] =
818 {
819 { "-text", TK_OPTION_STRING,
820 offsetof(TextElement,textObj), "" },
821 { "-font", TK_OPTION_FONT,
822 offsetof(TextElement,fontObj), DEFAULT_FONT },
823 { NULL }
824 };
825
TextElementSize(void * clientData,void * elementRecord,Tk_Window tkwin,int * widthPtr,int * heightPtr,Ttk_Padding * paddingPtr)826 static void TextElementSize(
827 void *clientData, void *elementRecord, Tk_Window tkwin,
828 int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
829 {
830 TextElement *element = elementRecord;
831 ElementData *elementData = clientData;
832 RECT rc = {0, 0};
833 HRESULT hr = S_OK;
834 const char *src;
835 TkSizeT len;
836 Tcl_DString ds;
837
838 if (!InitElementData(elementData, tkwin, 0))
839 return;
840
841 src = Tcl_GetStringFromObj(element->textObj, &len);
842 Tcl_DStringInit(&ds);
843 hr = elementData->procs->GetThemeTextExtent(
844 elementData->hTheme,
845 elementData->hDC,
846 elementData->info->partId,
847 Ttk_StateTableLookup(elementData->info->statemap, 0),
848 Tcl_UtfToWCharDString(src, len, &ds),
849 -1,
850 DT_LEFT /* | DT_BOTTOM | DT_NOPREFIX */,
851 NULL,
852 &rc);
853
854 if (SUCCEEDED(hr)) {
855 *widthPtr = rc.right - rc.left;
856 *heightPtr = rc.bottom - rc.top;
857 }
858 if (*widthPtr < 80) *widthPtr = 80;
859 if (*heightPtr < 20) *heightPtr = 20;
860
861 Tcl_DStringFree(&ds);
862 FreeElementData(elementData);
863 }
864
TextElementDraw(ClientData clientData,void * elementRecord,Tk_Window tkwin,Drawable d,Ttk_Box b,unsigned int state)865 static void TextElementDraw(
866 ClientData clientData, void *elementRecord, Tk_Window tkwin,
867 Drawable d, Ttk_Box b, unsigned int state)
868 {
869 TextElement *element = elementRecord;
870 ElementData *elementData = clientData;
871 RECT rc = BoxToRect(b);
872 HRESULT hr = S_OK;
873 const char *src;
874 TkSizeT len;
875 Tcl_DString ds;
876
877 if (!InitElementData(elementData, tkwin, d))
878 return;
879
880 src = Tcl_GetStringFromObj(element->textObj, &len);
881 Tcl_DStringInit(&ds);
882 hr = elementData->procs->DrawThemeText(
883 elementData->hTheme,
884 elementData->hDC,
885 elementData->info->partId,
886 Ttk_StateTableLookup(elementData->info->statemap, state),
887 Tcl_UtfToWCharDString(src, len, &ds),
888 -1,
889 DT_LEFT /* | DT_BOTTOM | DT_NOPREFIX */,
890 (state & TTK_STATE_DISABLED) ? DTT_GRAYED : 0,
891 &rc);
892
893 Tcl_DStringFree(&ds);
894 FreeElementData(elementData);
895 }
896
897 static const Ttk_ElementSpec TextElementSpec =
898 {
899 TK_STYLE_VERSION_2,
900 sizeof(TextElement),
901 TextElementOptions,
902 TextElementSize,
903 TextElementDraw
904 };
905
906 #endif /* BROKEN_TEXT_ELEMENT */
907
908 /*----------------------------------------------------------------------
909 * +++ Widget layouts:
910 */
911
912 TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
913
914 TTK_LAYOUT("TButton",
915 TTK_GROUP("Button.button", TTK_FILL_BOTH,
916 TTK_GROUP("Button.focus", TTK_FILL_BOTH,
917 TTK_GROUP("Button.padding", TTK_FILL_BOTH,
918 TTK_NODE("Button.label", TTK_FILL_BOTH)))))
919
920 TTK_LAYOUT("TMenubutton",
921 TTK_NODE("Menubutton.dropdown", TTK_PACK_RIGHT|TTK_FILL_Y)
922 TTK_GROUP("Menubutton.button", TTK_FILL_BOTH,
923 TTK_GROUP("Menubutton.padding", TTK_FILL_X,
924 TTK_NODE("Menubutton.label", 0))))
925
926 TTK_LAYOUT("Horizontal.TScrollbar",
927 TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X,
928 TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT)
929 TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT)
930 TTK_GROUP("Horizontal.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT,
931 TTK_NODE("Horizontal.Scrollbar.grip", 0))))
932
933 TTK_LAYOUT("Vertical.TScrollbar",
934 TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y,
935 TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP)
936 TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM)
937 TTK_GROUP("Vertical.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT,
938 TTK_NODE("Vertical.Scrollbar.grip", 0))))
939
940 TTK_LAYOUT("Horizontal.TScale",
941 TTK_GROUP("Scale.focus", TTK_FILL_BOTH,
942 TTK_GROUP("Horizontal.Scale.trough", TTK_FILL_BOTH,
943 TTK_NODE("Horizontal.Scale.track", TTK_FILL_X)
944 TTK_NODE("Horizontal.Scale.slider", TTK_PACK_LEFT) )))
945
946 TTK_LAYOUT("Vertical.TScale",
947 TTK_GROUP("Scale.focus", TTK_FILL_BOTH,
948 TTK_GROUP("Vertical.Scale.trough", TTK_FILL_BOTH,
949 TTK_NODE("Vertical.Scale.track", TTK_FILL_Y)
950 TTK_NODE("Vertical.Scale.slider", TTK_PACK_TOP) )))
951
952 TTK_END_LAYOUT_TABLE
953
954 /*----------------------------------------------------------------------
955 * +++ XP element info table:
956 */
957
958 #define PAD(l,t,r,b) {l,t,r,b}
959 #define NOPAD {0,0,0,0}
960
961 /* name spec className partId statemap padding flags */
962
963 static const ElementInfo ElementInfoTable[] = {
964 { "Checkbutton.indicator", &GenericElementSpec, L"BUTTON",
965 BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS },
966 { "Radiobutton.indicator", &GenericElementSpec, L"BUTTON",
967 BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS },
968 { "Button.button", &GenericElementSpec, L"BUTTON",
969 BP_PUSHBUTTON, pushbutton_statemap, PAD(3, 3, 3, 3), IGNORE_THEMESIZE },
970 { "Labelframe.border", &GenericElementSpec, L"BUTTON",
971 BP_GROUPBOX, groupbox_statemap, PAD(2, 2, 2, 2), 0 },
972 { "Entry.field", &GenericElementSpec, L"EDIT", EP_EDITTEXT,
973 edittext_statemap, PAD(1, 1, 1, 1), 0 },
974 { "Combobox.field", &GenericElementSpec, L"EDIT",
975 EP_EDITTEXT, combotext_statemap, PAD(1, 1, 1, 1), 0 },
976 { "Combobox.downarrow", &GenericSizedElementSpec, L"COMBOBOX",
977 CP_DROPDOWNBUTTON, combobox_statemap, NOPAD,
978 (SM_CXVSCROLL << 8) | SM_CYVSCROLL },
979 { "Vertical.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR",
980 SBP_UPPERTRACKVERT, scrollbar_statemap, NOPAD, 0 },
981 { "Vertical.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR",
982 SBP_THUMBBTNVERT, scrollbar_statemap, NOPAD, 0 },
983 { "Vertical.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR",
984 SBP_GRIPPERVERT, scrollbar_statemap, NOPAD, 0 },
985 { "Horizontal.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR",
986 SBP_UPPERTRACKHORZ, scrollbar_statemap, NOPAD, 0 },
987 { "Horizontal.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR",
988 SBP_THUMBBTNHORZ, scrollbar_statemap, NOPAD, 0 },
989 { "Horizontal.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR",
990 SBP_GRIPPERHORZ, scrollbar_statemap, NOPAD, 0 },
991 { "Scrollbar.uparrow", &GenericSizedElementSpec, L"SCROLLBAR",
992 SBP_ARROWBTN, uparrow_statemap, NOPAD,
993 (SM_CXVSCROLL << 8) | SM_CYVSCROLL },
994 { "Scrollbar.downarrow", &GenericSizedElementSpec, L"SCROLLBAR",
995 SBP_ARROWBTN, downarrow_statemap, NOPAD,
996 (SM_CXVSCROLL << 8) | SM_CYVSCROLL },
997 { "Scrollbar.leftarrow", &GenericSizedElementSpec, L"SCROLLBAR",
998 SBP_ARROWBTN, leftarrow_statemap, NOPAD,
999 (SM_CXHSCROLL << 8) | SM_CYHSCROLL },
1000 { "Scrollbar.rightarrow", &GenericSizedElementSpec, L"SCROLLBAR",
1001 SBP_ARROWBTN, rightarrow_statemap, NOPAD,
1002 (SM_CXHSCROLL << 8) | SM_CYHSCROLL },
1003 { "Horizontal.Scale.slider", &GenericElementSpec, L"TRACKBAR",
1004 TKP_THUMB, scale_statemap, NOPAD, 0 },
1005 { "Vertical.Scale.slider", &GenericElementSpec, L"TRACKBAR",
1006 TKP_THUMBVERT, scale_statemap, NOPAD, 0 },
1007 { "Horizontal.Scale.track", &GenericElementSpec, L"TRACKBAR",
1008 TKP_TRACK, scale_statemap, NOPAD, 0 },
1009 { "Vertical.Scale.track", &GenericElementSpec, L"TRACKBAR",
1010 TKP_TRACKVERT, scale_statemap, NOPAD, 0 },
1011 /* ttk::progressbar elements */
1012 { "Horizontal.Progressbar.pbar", &PbarElementSpec, L"PROGRESS",
1013 PP_CHUNK, null_statemap, NOPAD, 0 },
1014 { "Vertical.Progressbar.pbar", &PbarElementSpec, L"PROGRESS",
1015 PP_CHUNKVERT, null_statemap, NOPAD, 0 },
1016 { "Horizontal.Progressbar.trough", &GenericElementSpec, L"PROGRESS",
1017 PP_BAR, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE },
1018 { "Vertical.Progressbar.trough", &GenericElementSpec, L"PROGRESS",
1019 PP_BARVERT, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE },
1020 /* ttk::notebook */
1021 { "tab", &TabElementSpec, L"TAB",
1022 TABP_TABITEM, tabitem_statemap, PAD(3,3,3,0), 0 },
1023 { "client", &GenericElementSpec, L"TAB",
1024 TABP_PANE, null_statemap, PAD(1,1,3,3), 0 },
1025 { "NotebookPane.background", &GenericElementSpec, L"TAB",
1026 TABP_BODY, null_statemap, NOPAD, 0 },
1027 { "Toolbutton.border", &GenericElementSpec, L"TOOLBAR",
1028 TP_BUTTON, toolbutton_statemap, NOPAD,0 },
1029 { "Menubutton.button", &GenericElementSpec, L"TOOLBAR",
1030 TP_SPLITBUTTON,toolbutton_statemap, NOPAD,0 },
1031 { "Menubutton.dropdown", &GenericElementSpec, L"TOOLBAR",
1032 TP_SPLITBUTTONDROPDOWN,toolbutton_statemap, NOPAD,0 },
1033 { "Treeview.field", &GenericElementSpec, L"TREEVIEW",
1034 TVP_TREEITEM, treeview_statemap, PAD(1, 1, 1, 1), IGNORE_THEMESIZE },
1035 { "Treeitem.indicator", &TreeIndicatorElementSpec, L"TREEVIEW",
1036 TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS },
1037 { "Treeheading.border", &GenericElementSpec, L"HEADER",
1038 HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 },
1039 { "sizegrip", &GenericElementSpec, L"STATUS",
1040 SP_GRIPPER, null_statemap, NOPAD,0 },
1041 { "Spinbox.field", &GenericElementSpec, L"EDIT",
1042 EP_EDITTEXT, edittext_statemap, PAD(1, 1, 1, 1), 0 },
1043 { "Spinbox.uparrow", &SpinboxArrowElementSpec, L"SPIN",
1044 SPNP_UP, spinbutton_statemap, NOPAD,
1045 PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
1046 { "Spinbox.downarrow", &SpinboxArrowElementSpec, L"SPIN",
1047 SPNP_DOWN, spinbutton_statemap, NOPAD,
1048 PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
1049 #ifdef BROKEN_TEXT_ELEMENT
1050 { "Labelframe.text", &TextElementSpec, L"BUTTON",
1051 BP_GROUPBOX, groupbox_statemap, NOPAD,0 },
1052 #endif
1053 { 0,0,0,0,0,NOPAD,0 }
1054 };
1055 #undef PAD
1056
1057
1058 static int
GetSysFlagFromObj(Tcl_Interp * interp,Tcl_Obj * objPtr,int * resultPtr)1059 GetSysFlagFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *resultPtr)
1060 {
1061 static const char *const names[] = {
1062 "SM_CXBORDER", "SM_CYBORDER", "SM_CXVSCROLL", "SM_CYVSCROLL",
1063 "SM_CXHSCROLL", "SM_CYHSCROLL", "SM_CXMENUCHECK", "SM_CYMENUCHECK",
1064 "SM_CXMENUSIZE", "SM_CYMENUSIZE", "SM_CXSIZE", "SM_CYSIZE", "SM_CXSMSIZE",
1065 "SM_CYSMSIZE", NULL
1066 };
1067 int flags[] = {
1068 SM_CXBORDER, SM_CYBORDER, SM_CXVSCROLL, SM_CYVSCROLL,
1069 SM_CXHSCROLL, SM_CYHSCROLL, SM_CXMENUCHECK, SM_CYMENUCHECK,
1070 SM_CXMENUSIZE, SM_CYMENUSIZE, SM_CXSIZE, SM_CYSIZE, SM_CXSMSIZE,
1071 SM_CYSMSIZE
1072 };
1073
1074 Tcl_Obj **objv;
1075 int i, objc;
1076
1077 if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK)
1078 return TCL_ERROR;
1079 if (objc != 2) {
1080 Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args", -1));
1081 Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL);
1082 return TCL_ERROR;
1083 }
1084 for (i = 0; i < objc; ++i) {
1085 int option;
1086 if (Tcl_GetIndexFromObjStruct(interp, objv[i], names,
1087 sizeof(char *), "system constant", 0, &option) != TCL_OK)
1088 return TCL_ERROR;
1089 *resultPtr |= (flags[option] << (8 * (1 - i)));
1090 }
1091 return TCL_OK;
1092 }
1093
1094 /*----------------------------------------------------------------------
1095 * Windows Visual Styles API Element Factory
1096 *
1097 * The Vista release has shown that the Windows Visual Styles can be
1098 * extended with additional elements. This element factory can permit
1099 * the programmer to create elements for use with script-defined layouts
1100 *
1101 * eg: to create the small close button:
1102 * style element create smallclose vsapi \
1103 * WINDOW 19 {disabled 4 pressed 3 active 2 {} 1}
1104 */
1105
1106 static int
Ttk_CreateVsapiElement(Tcl_Interp * interp,void * clientData,Ttk_Theme theme,const char * elementName,int objc,Tcl_Obj * const objv[])1107 Ttk_CreateVsapiElement(
1108 Tcl_Interp *interp,
1109 void *clientData,
1110 Ttk_Theme theme,
1111 const char *elementName,
1112 int objc,
1113 Tcl_Obj *const objv[])
1114 {
1115 XPThemeData *themeData = (XPThemeData *)clientData;
1116 ElementInfo *elementPtr = NULL;
1117 ClientData elementData;
1118 LPCWSTR className;
1119 int partId = 0;
1120 Ttk_StateTable *stateTable;
1121 Ttk_Padding pad = {0, 0, 0, 0};
1122 int flags = 0;
1123 TkSizeT length = 0;
1124 char *name;
1125 LPWSTR wname;
1126 const Ttk_ElementSpec *elementSpec = &GenericElementSpec;
1127 Tcl_DString classBuf;
1128
1129 static const char *const optionStrings[] =
1130 { "-padding","-width","-height","-margins", "-syssize",
1131 "-halfheight", "-halfwidth", NULL };
1132 enum { O_PADDING, O_WIDTH, O_HEIGHT, O_MARGINS, O_SYSSIZE,
1133 O_HALFHEIGHT, O_HALFWIDTH };
1134
1135 if (objc < 2) {
1136 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1137 "missing required arguments 'class' and/or 'partId'", -1));
1138 Tcl_SetErrorCode(interp, "TTK", "VSAPI", "REQUIRED", NULL);
1139 return TCL_ERROR;
1140 }
1141
1142 if (Tcl_GetIntFromObj(interp, objv[1], &partId) != TCL_OK) {
1143 return TCL_ERROR;
1144 }
1145 name = Tcl_GetStringFromObj(objv[0], &length);
1146 Tcl_DStringInit(&classBuf);
1147 className = Tcl_UtfToWCharDString(name, length, &classBuf);
1148
1149 /* flags or padding */
1150 if (objc > 3) {
1151 int i = 3, option = 0;
1152 for (i = 3; i < objc; i += 2) {
1153 int tmp = 0;
1154 if (i == objc -1) {
1155 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
1156 "Missing value for \"%s\".",
1157 Tcl_GetString(objv[i])));
1158 Tcl_SetErrorCode(interp, "TTK", "VSAPI", "MISSING", NULL);
1159 goto retErr;
1160 }
1161 if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings,
1162 sizeof(char *), "option", 0, &option) != TCL_OK)
1163 goto retErr;
1164 switch (option) {
1165 case O_PADDING:
1166 if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) {
1167 goto retErr;
1168 }
1169 break;
1170 case O_MARGINS:
1171 if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) {
1172 goto retErr;
1173 }
1174 flags |= PAD_MARGINS;
1175 break;
1176 case O_WIDTH:
1177 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1178 goto retErr;
1179 }
1180 pad.left = pad.right = tmp;
1181 flags |= IGNORE_THEMESIZE;
1182 break;
1183 case O_HEIGHT:
1184 if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1185 goto retErr;
1186 }
1187 pad.top = pad.bottom = tmp;
1188 flags |= IGNORE_THEMESIZE;
1189 break;
1190 case O_SYSSIZE:
1191 if (GetSysFlagFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1192 goto retErr;
1193 }
1194 elementSpec = &GenericSizedElementSpec;
1195 flags |= (tmp & 0xFFFF);
1196 break;
1197 case O_HALFHEIGHT:
1198 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1199 goto retErr;
1200 }
1201 if (tmp)
1202 flags |= HALF_HEIGHT;
1203 break;
1204 case O_HALFWIDTH:
1205 if (Tcl_GetBooleanFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1206 goto retErr;
1207 }
1208 if (tmp)
1209 flags |= HALF_WIDTH;
1210 break;
1211 }
1212 }
1213 }
1214
1215 /* convert a statemap into a state table */
1216 if (objc > 2) {
1217 Tcl_Obj **specs;
1218 int n,j,count, status = TCL_OK;
1219 if (Tcl_ListObjGetElements(interp, objv[2], &count, &specs) != TCL_OK)
1220 goto retErr;
1221 /* we over-allocate to ensure there is a terminating entry */
1222 stateTable = (Ttk_StateTable *)ckalloc(sizeof(Ttk_StateTable) * (count + 1));
1223 memset(stateTable, 0, sizeof(Ttk_StateTable) * (count + 1));
1224 for (n = 0, j = 0; status == TCL_OK && n < count; n += 2, ++j) {
1225 Ttk_StateSpec spec = {0,0};
1226 status = Ttk_GetStateSpecFromObj(interp, specs[n], &spec);
1227 if (status == TCL_OK) {
1228 stateTable[j].onBits = spec.onbits;
1229 stateTable[j].offBits = spec.offbits;
1230 status = Tcl_GetIntFromObj(interp, specs[n+1],
1231 &stateTable[j].index);
1232 }
1233 }
1234 if (status != TCL_OK) {
1235 ckfree(stateTable);
1236 Tcl_DStringFree(&classBuf);
1237 return status;
1238 }
1239 } else {
1240 stateTable = (Ttk_StateTable *)ckalloc(sizeof(Ttk_StateTable));
1241 memset(stateTable, 0, sizeof(Ttk_StateTable));
1242 }
1243
1244 elementPtr = (ElementInfo *)ckalloc(sizeof(ElementInfo));
1245 elementPtr->elementSpec = elementSpec;
1246 elementPtr->partId = partId;
1247 elementPtr->statemap = stateTable;
1248 elementPtr->padding = pad;
1249 elementPtr->flags = HEAP_ELEMENT | flags;
1250
1251 /* set the element name to an allocated copy */
1252 name = (char *)ckalloc(strlen(elementName) + 1);
1253 strcpy(name, elementName);
1254 elementPtr->elementName = name;
1255
1256 /* set the class name to an allocated copy */
1257 wname = (LPWSTR)ckalloc(Tcl_DStringLength(&classBuf) + sizeof(WCHAR));
1258 wcscpy(wname, className);
1259 elementPtr->className = wname;
1260
1261 elementData = NewElementData(themeData->procs, elementPtr);
1262 Ttk_RegisterElementSpec(
1263 theme, elementName, elementPtr->elementSpec, elementData);
1264
1265 Ttk_RegisterCleanup(interp, elementData, DestroyElementData);
1266 Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1));
1267 Tcl_DStringFree(&classBuf);
1268 return TCL_OK;
1269
1270 retErr:
1271 Tcl_DStringFree(&classBuf);
1272 return TCL_ERROR;
1273 }
1274
1275 /*----------------------------------------------------------------------
1276 * +++ Initialization routine:
1277 */
1278
TtkXPTheme_Init(Tcl_Interp * interp,HWND hwnd)1279 MODULE_SCOPE int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd)
1280 {
1281 XPThemeData *themeData;
1282 XPThemeProcs *procs;
1283 HINSTANCE hlibrary;
1284 Ttk_Theme themePtr, parentPtr, vistaPtr;
1285 const ElementInfo *infoPtr;
1286
1287 procs = LoadXPThemeProcs(&hlibrary);
1288 if (!procs)
1289 return TCL_ERROR;
1290 procs->stubWindow = hwnd;
1291
1292 /*
1293 * Create the new style engine.
1294 */
1295 parentPtr = Ttk_GetTheme(interp, "winnative");
1296 themePtr = Ttk_CreateTheme(interp, "xpnative", parentPtr);
1297
1298 if (!themePtr)
1299 return TCL_ERROR;
1300
1301 /*
1302 * Set theme data and cleanup proc
1303 */
1304
1305 themeData = (XPThemeData *)ckalloc(sizeof(XPThemeData));
1306 themeData->procs = procs;
1307 themeData->hlibrary = hlibrary;
1308
1309 Ttk_SetThemeEnabledProc(themePtr, XPThemeEnabled, themeData);
1310 Ttk_RegisterCleanup(interp, themeData, XPThemeDeleteProc);
1311 Ttk_RegisterElementFactory(interp, "vsapi", Ttk_CreateVsapiElement, themeData);
1312
1313 /*
1314 * Create the vista theme on suitable platform versions and set the theme
1315 * enable function. The theme itself is defined in script.
1316 */
1317
1318 if (TkWinGetPlatformTheme() == TK_THEME_WIN_VISTA) {
1319 vistaPtr = Ttk_CreateTheme(interp, "vista", themePtr);
1320 if (vistaPtr) {
1321 Ttk_SetThemeEnabledProc(vistaPtr, XPThemeEnabled, themeData);
1322 }
1323 }
1324
1325 /*
1326 * New elements:
1327 */
1328 for (infoPtr = ElementInfoTable; infoPtr->elementName != 0; ++infoPtr) {
1329 ClientData clientData = NewElementData(procs, infoPtr);
1330 Ttk_RegisterElementSpec(
1331 themePtr, infoPtr->elementName, infoPtr->elementSpec, clientData);
1332 Ttk_RegisterCleanup(interp, clientData, DestroyElementData);
1333 }
1334
1335 Ttk_RegisterElementSpec(themePtr, "Scale.trough", &ttkNullElementSpec, 0);
1336
1337 /*
1338 * Layouts:
1339 */
1340 Ttk_RegisterLayouts(themePtr, LayoutTable);
1341
1342 Tcl_PkgProvide(interp, "ttk::theme::xpnative", TTK_VERSION);
1343
1344 return TCL_OK;
1345 }
1346
1347 #endif /* HAVE_UXTHEME_H */
1348