]> Pileus Git - ~andy/gtk/blob - modules/engines/ms-windows/xp_theme.c
xp theme definitions for platforms NOT winxp, catch system settings changes, disable...
[~andy/gtk] / modules / engines / ms-windows / xp_theme.c
1 /* Wimp "Windows Impersonator" Engine
2  *
3  * Copyright (C) 2003 Raymond Penners <raymond@dotsphinx.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "xp_theme.h"
22
23 #include <windows.h>
24 #include <uxtheme.h>
25 #include <tmschema.h>
26 #include <math.h>
27 #include <string.h>
28 #include <gdk/gdkwin32.h>
29
30 #include <stdio.h>
31
32 /* MS defines this when it includes its schema definitions */
33 #ifndef TMSCHEMA_H
34 #include "xp_theme_dfns.h"
35 #endif
36
37 static const LPCWSTR class_descriptors[] =
38 {
39   L"Scrollbar",
40   L"Button",
41   L"Header",
42   L"ComboBox",
43   L"Tab",
44   L"Edit",
45   L"TreeView",
46   L"Spin",
47   L"Progress",
48   L"Tooltip",
49   L"Rebar",
50   L"Toolbar",
51   L"Globals"
52 };
53
54 static const short element_part_map[]=
55 {
56   BP_CHECKBOX,
57   BP_CHECKBOX,
58   BP_PUSHBUTTON,
59   HP_HEADERITEM,
60   CP_DROPDOWNBUTTON,
61   TABP_BODY,
62   TABP_TABITEM,
63   TABP_TABITEMLEFTEDGE,
64   TABP_PANE,
65   SBP_THUMBBTNHORZ,
66   SBP_THUMBBTNVERT,
67   SBP_ARROWBTN,
68   SBP_ARROWBTN,
69   SBP_ARROWBTN,
70   SBP_ARROWBTN,
71   SBP_GRIPPERHORZ,
72   SBP_GRIPPERVERT,
73   SBP_LOWERTRACKHORZ,
74   SBP_LOWERTRACKVERT,
75   EP_EDITTEXT,
76   BP_PUSHBUTTON,
77   SPNP_UP,
78   SPNP_DOWN,
79   BP_RADIOBUTTON,
80   TVP_GLYPH,
81   TVP_GLYPH,
82   PP_CHUNK,
83   PP_CHUNKVERT,
84   PP_BAR,
85   PP_BARVERT,
86   TTP_STANDARD,
87   RP_BAND,
88   RP_GRIPPER,
89   RP_GRIPPERVERT,
90   RP_CHEVRON,
91   TP_BUTTON
92
93 #if UXTHEME_HAS_LINES
94   ,
95   GP_LINEHORZ,
96   GP_LINEVERT
97 #endif
98 };
99
100 static HINSTANCE uxtheme_dll = NULL;
101 static HTHEME open_themes[XP_THEME_CLASS__SIZEOF];
102
103 typedef HRESULT (FAR PASCAL *GetThemeSysFontFunc)
104      (HTHEME hTheme, int iFontID, FAR LOGFONT *plf);
105 typedef HTHEME (FAR PASCAL *OpenThemeDataFunc)
106      (HWND hwnd, LPCWSTR pszClassList);
107 typedef HRESULT (FAR PASCAL *CloseThemeDataFunc)(HTHEME theme);
108 typedef HRESULT (FAR PASCAL *DrawThemeBackgroundFunc)
109      (HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
110       const RECT *pRect, const RECT *pClipRect);
111 typedef HRESULT (FAR PASCAL *EnableThemeDialogTextureFunc)(HWND hwnd, DWORD dwFlags);
112
113 static GetThemeSysFontFunc get_theme_sys_font_func = NULL;
114 static OpenThemeDataFunc open_theme_data_func = NULL;
115 static CloseThemeDataFunc close_theme_data_func = NULL;
116 static DrawThemeBackgroundFunc draw_theme_background_func = NULL;
117 static EnableThemeDialogTextureFunc enable_theme_dialog_texture_func = NULL;
118
119 void
120 xp_theme_init(void)
121 {
122   if (uxtheme_dll)
123     return;
124
125   uxtheme_dll = LoadLibrary("uxtheme.dll");
126   memset(open_themes, 0, sizeof(open_themes));
127
128   open_theme_data_func = (OpenThemeDataFunc) GetProcAddress(uxtheme_dll, "OpenThemeData");
129   close_theme_data_func = (CloseThemeDataFunc) GetProcAddress(uxtheme_dll, "CloseThemeData");
130   draw_theme_background_func = (DrawThemeBackgroundFunc) GetProcAddress(uxtheme_dll, "DrawThemeBackground");
131   enable_theme_dialog_texture_func = (EnableThemeDialogTextureFunc) GetProcAddress(uxtheme_dll, "EnableThemeDialogTexture");
132   get_theme_sys_font_func = (GetThemeSysFontFunc) GetProcAddress(uxtheme_dll, "GetThemeSysFont");
133 }
134
135 void
136 xp_theme_exit(void)
137 {
138   int i;
139
140   if(!uxtheme_dll)
141     return;
142
143   for (i=0; i < XP_THEME_CLASS__SIZEOF; i++)
144     {
145       if (open_themes[i])
146         {
147           close_theme_data_func(open_themes[i]);
148           open_themes[i] = NULL;
149         }
150     }
151
152   FreeLibrary(uxtheme_dll);
153   uxtheme_dll = NULL;
154
155   open_theme_data_func = NULL;
156   close_theme_data_func = NULL;
157   draw_theme_background_func = NULL;
158   enable_theme_dialog_texture_func = NULL;
159   get_theme_sys_font_func = NULL;
160 }
161
162 static HTHEME
163 xp_theme_get_handle_by_class(XpThemeClass klazz)
164 {
165   if (!open_themes[klazz] && open_theme_data_func)
166     {
167       open_themes[klazz] = open_theme_data_func(NULL, class_descriptors[klazz]);
168     }
169   return open_themes[klazz];
170 }
171
172 static HTHEME
173 xp_theme_get_handle_by_element(XpThemeElement element)
174 {
175   HTHEME ret = NULL;
176   XpThemeClass klazz = XP_THEME_CLASS__SIZEOF;
177
178   switch(element)
179     {
180     case XP_THEME_ELEMENT_TOOLTIP:
181       klazz = XP_THEME_CLASS_TOOLTIP;
182       break;
183
184     case XP_THEME_ELEMENT_REBAR:
185     case XP_THEME_ELEMENT_GRIPPER_H:
186     case XP_THEME_ELEMENT_GRIPPER_V:
187     case XP_THEME_ELEMENT_CHEVRON:
188       klazz = XP_THEME_CLASS_REBAR;
189       break;
190
191     case XP_THEME_ELEMENT_TOOLBAR:
192       klazz = XP_THEME_CLASS_TOOLBAR;
193       break;
194
195     case XP_THEME_ELEMENT_HLINE:
196     case XP_THEME_ELEMENT_VLINE:
197       klazz = XP_THEME_CLASS_GLOBALS;
198       break;
199
200     case XP_THEME_ELEMENT_PRESSED_CHECKBOX:
201     case XP_THEME_ELEMENT_CHECKBOX:
202     case XP_THEME_ELEMENT_BUTTON:
203     case XP_THEME_ELEMENT_DEFAULT_BUTTON:
204     case XP_THEME_ELEMENT_RADIO_BUTTON:
205       klazz = XP_THEME_CLASS_BUTTON;
206       break;
207
208     case XP_THEME_ELEMENT_LIST_HEADER:
209       klazz = XP_THEME_CLASS_HEADER;
210       break;
211
212     case XP_THEME_ELEMENT_COMBOBUTTON:
213       klazz = XP_THEME_CLASS_COMBOBOX;
214       break;
215
216     case XP_THEME_ELEMENT_BODY:
217     case XP_THEME_ELEMENT_TAB_ITEM:
218     case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE:
219     case XP_THEME_ELEMENT_TAB_PANE:
220       klazz = XP_THEME_CLASS_TAB;
221       break;
222
223     case XP_THEME_ELEMENT_SCROLLBAR_V:
224     case XP_THEME_ELEMENT_SCROLLBAR_H:
225     case XP_THEME_ELEMENT_ARROW_UP:
226     case XP_THEME_ELEMENT_ARROW_DOWN:
227     case XP_THEME_ELEMENT_ARROW_LEFT:
228     case XP_THEME_ELEMENT_ARROW_RIGHT:
229     case XP_THEME_ELEMENT_GRIP_V:
230     case XP_THEME_ELEMENT_GRIP_H:
231     case XP_THEME_ELEMENT_TROUGH_V:
232     case XP_THEME_ELEMENT_TROUGH_H:
233       klazz = XP_THEME_CLASS_SCROLLBAR;
234       break;
235
236     case XP_THEME_ELEMENT_EDIT_TEXT:
237       klazz = XP_THEME_CLASS_EDIT;
238       break;
239
240     case XP_THEME_ELEMENT_SPIN_BUTTON_UP:
241     case XP_THEME_ELEMENT_SPIN_BUTTON_DOWN:
242       klazz = XP_THEME_CLASS_SPIN;
243       break;
244
245     case XP_THEME_ELEMENT_PROGRESS_BAR_H:
246     case XP_THEME_ELEMENT_PROGRESS_BAR_V:
247     case XP_THEME_ELEMENT_PROGRESS_TROUGH_H:
248     case XP_THEME_ELEMENT_PROGRESS_TROUGH_V:
249       klazz = XP_THEME_CLASS_PROGRESS;
250       break;
251
252     case XP_THEME_ELEMENT_TREEVIEW_EXPANDER_OPENED:
253     case XP_THEME_ELEMENT_TREEVIEW_EXPANDER_CLOSED:
254       klazz = XP_THEME_CLASS_TREEVIEW;
255       break;
256     }
257
258   if (klazz != XP_THEME_CLASS__SIZEOF)
259     {
260       ret = xp_theme_get_handle_by_class (klazz);
261     }
262   return ret;
263 }
264
265 static int
266 xp_theme_map_gtk_state(XpThemeElement element, GtkStateType state)
267 {
268   int ret;
269
270   switch(element)
271     {
272     case XP_THEME_ELEMENT_TOOLTIP:
273       ret = TTSS_NORMAL;
274       break;
275
276     case XP_THEME_ELEMENT_REBAR:
277     case XP_THEME_ELEMENT_GRIPPER_H:
278     case XP_THEME_ELEMENT_GRIPPER_V:
279       ret = 0;
280       break;
281
282     case XP_THEME_ELEMENT_CHEVRON:
283       switch (state)
284         {
285         case GTK_STATE_PRELIGHT:
286           ret =  CHEVS_HOT;
287           break;
288         case GTK_STATE_SELECTED:
289         case GTK_STATE_ACTIVE:
290           ret =  CHEVS_PRESSED;
291           break;
292         default:
293           ret =  CHEVS_NORMAL;
294         }
295       break;
296
297     case XP_THEME_ELEMENT_TOOLBAR:
298       ret = 1;
299       break;
300
301     case XP_THEME_ELEMENT_TAB_PANE:
302       ret = 1;
303       break;
304
305     case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE:
306     case XP_THEME_ELEMENT_TAB_ITEM:
307       switch(state)
308         {
309         case GTK_STATE_PRELIGHT:
310           ret =  TIS_HOT;
311           break;
312         case GTK_STATE_INSENSITIVE:
313           ret =  TIS_DISABLED;
314           break;
315         case GTK_STATE_SELECTED:
316         case GTK_STATE_ACTIVE:
317           ret =  TIS_NORMAL;
318           break;
319         default:
320           ret =  TIS_SELECTED;
321         }
322       break;
323
324     case XP_THEME_ELEMENT_EDIT_TEXT:
325       switch(state)
326         {
327         case GTK_STATE_PRELIGHT:
328           ret =  ETS_FOCUSED;
329           break;
330         case GTK_STATE_INSENSITIVE:
331           ret =  ETS_READONLY;
332           break;
333         case GTK_STATE_SELECTED:
334         case GTK_STATE_ACTIVE:
335         default:
336           ret =  ETS_NORMAL;
337         }
338       break;
339
340     case XP_THEME_ELEMENT_SCROLLBAR_H:
341     case XP_THEME_ELEMENT_SCROLLBAR_V:
342     case XP_THEME_ELEMENT_TROUGH_H:
343     case XP_THEME_ELEMENT_TROUGH_V:
344       switch(state)
345         {
346         case GTK_STATE_SELECTED:
347         case GTK_STATE_ACTIVE:
348           ret =  SCRBS_PRESSED;
349           break;
350         case GTK_STATE_PRELIGHT:
351           ret =  SCRBS_HOT;
352           break;
353         case GTK_STATE_INSENSITIVE:
354           ret =  SCRBS_DISABLED;
355           break;
356         default:
357           ret =  SCRBS_NORMAL;
358         }
359       break;
360
361     case XP_THEME_ELEMENT_ARROW_DOWN:
362       switch(state)
363         {
364         case GTK_STATE_ACTIVE:
365           ret =  ABS_DOWNPRESSED;
366           break;
367         case GTK_STATE_PRELIGHT:
368           ret =  ABS_DOWNHOT;
369           break;
370         case GTK_STATE_INSENSITIVE:
371           ret =  ABS_DOWNDISABLED;
372           break;
373         default:
374           ret =  ABS_DOWNNORMAL;
375         }
376       break;
377
378     case XP_THEME_ELEMENT_ARROW_UP:
379       switch(state)
380         {
381         case GTK_STATE_ACTIVE:
382           ret =  ABS_UPPRESSED;
383           break;
384         case GTK_STATE_PRELIGHT:
385           ret =  ABS_UPHOT;
386           break;
387         case GTK_STATE_INSENSITIVE:
388           ret =  ABS_UPDISABLED;
389           break;
390         default:
391           ret =  ABS_UPNORMAL;
392         }
393       break;
394
395     case XP_THEME_ELEMENT_ARROW_LEFT:
396       switch(state)
397         {
398         case GTK_STATE_ACTIVE:
399           ret =  ABS_LEFTPRESSED;
400           break;
401         case GTK_STATE_PRELIGHT:
402           ret =  ABS_LEFTHOT;
403           break;
404         case GTK_STATE_INSENSITIVE:
405           ret =  ABS_LEFTDISABLED;
406           break;
407         default:
408           ret =  ABS_LEFTNORMAL;
409         }
410       break;
411
412     case XP_THEME_ELEMENT_ARROW_RIGHT:
413       switch(state)
414         {
415         case GTK_STATE_ACTIVE:
416           ret =  ABS_RIGHTPRESSED;
417           break;
418         case GTK_STATE_PRELIGHT:
419           ret =  ABS_RIGHTHOT;
420           break;
421         case GTK_STATE_INSENSITIVE:
422           ret =  ABS_RIGHTDISABLED;
423           break;
424         default:
425           ret =  ABS_RIGHTNORMAL;
426         }
427       break;
428
429     case XP_THEME_ELEMENT_CHECKBOX:
430     case XP_THEME_ELEMENT_RADIO_BUTTON:
431       switch(state)
432         {
433         case GTK_STATE_SELECTED:
434           ret =  CBS_UNCHECKEDPRESSED;
435           break;
436         case GTK_STATE_PRELIGHT:
437           ret =  CBS_UNCHECKEDHOT;
438           break;
439         case GTK_STATE_INSENSITIVE:
440           ret =  CBS_UNCHECKEDDISABLED;
441           break;
442         default:
443           ret =  CBS_UNCHECKEDNORMAL;
444         }
445       break;
446
447     case XP_THEME_ELEMENT_PRESSED_CHECKBOX:
448       switch(state)
449         {
450         case GTK_STATE_SELECTED:
451           ret =  CBS_CHECKEDPRESSED;
452           break;
453         case GTK_STATE_PRELIGHT:
454           ret =  CBS_CHECKEDHOT;
455           break;
456         case GTK_STATE_INSENSITIVE:
457           ret =  CBS_CHECKEDDISABLED;
458           break;
459         default:
460           ret =  CBS_CHECKEDNORMAL;
461         }
462       break;
463
464     XP_THEME_ELEMENT_DEFAULT_BUTTON:
465       switch(state)
466         {
467         case GTK_STATE_ACTIVE:
468           ret =  PBS_PRESSED;
469           break;
470         case GTK_STATE_PRELIGHT:
471           ret =  PBS_HOT;
472           break;
473         case GTK_STATE_INSENSITIVE:
474           ret =  PBS_DISABLED;
475           break;
476         default:
477           ret =  PBS_DEFAULTED;
478         }
479       break;
480
481     case XP_THEME_ELEMENT_SPIN_BUTTON_DOWN:
482       switch(state)
483         {
484         case GTK_STATE_ACTIVE:
485           ret =  DNS_PRESSED;
486           break;
487         case GTK_STATE_PRELIGHT:
488           ret =  DNS_HOT;
489           break;
490         case GTK_STATE_INSENSITIVE:
491           ret =  DNS_DISABLED;
492           break;
493         default:
494           ret =  DNS_NORMAL;
495         }
496       break;
497
498     case XP_THEME_ELEMENT_SPIN_BUTTON_UP:
499       switch(state)
500         {
501         case GTK_STATE_ACTIVE:
502           ret =  UPS_PRESSED;
503           break;
504         case GTK_STATE_PRELIGHT:
505           ret =  UPS_HOT;
506           break;
507         case GTK_STATE_INSENSITIVE:
508           ret =  UPS_DISABLED;
509           break;
510         default:
511           ret =  UPS_NORMAL;
512         }
513       break;
514
515     case XP_THEME_ELEMENT_TREEVIEW_EXPANDER_OPENED:
516       ret = GLPS_OPENED;
517       break;
518
519     case XP_THEME_ELEMENT_TREEVIEW_EXPANDER_CLOSED:
520       ret = GLPS_CLOSED;
521       break;
522
523     case XP_THEME_ELEMENT_PROGRESS_BAR_H:
524     case XP_THEME_ELEMENT_PROGRESS_BAR_V:
525     case XP_THEME_ELEMENT_PROGRESS_TROUGH_H:
526     case XP_THEME_ELEMENT_PROGRESS_TROUGH_V:
527       ret = 1;
528       break;
529
530 #if UXTHEME_HAS_LINES
531
532     case XP_THEME_ELEMENT_HLINE:
533       switch(state) {
534       case GTK_STATE_ACTIVE:
535         ret = LHS_RAISED;
536         break;
537       case GTK_STATE_INSENSITIVE:
538         ret = LHS_SUNKEN;
539         break;
540       default:
541         ret = LHS_FLAT;
542       }
543       break;
544
545     case XP_THEME_ELEMENT_VLINE:
546       switch(state) {
547       case GTK_STATE_ACTIVE:
548         ret = LVS_RAISED;
549         break;
550       case GTK_STATE_INSENSITIVE:
551         ret = LVS_SUNKEN;
552         break;
553       default:
554         ret = LHS_FLAT;
555       }
556       break;
557
558 #endif
559
560     default:
561       switch(state)
562         {
563         case GTK_STATE_ACTIVE:
564           ret =  PBS_PRESSED;
565           break;
566         case GTK_STATE_PRELIGHT:
567           ret =  PBS_HOT;
568           break;
569         case GTK_STATE_INSENSITIVE:
570           ret =  PBS_DISABLED;
571           break;
572         default:
573           ret =  PBS_NORMAL;
574         }
575     }
576   return ret;
577 }
578
579 gboolean
580 xp_theme_draw(GdkWindow *win, XpThemeElement element, GtkStyle *style,
581               int x, int y, int width, int height, GtkStateType state_type,
582               GdkRectangle *area)
583 {
584   HTHEME theme;
585   RECT rect, clip, *pClip;
586   int xoff, yoff, state;
587   HDC dc;
588   GdkDrawable *drawable;
589   int part_state;
590
591   if (!uxtheme_dll)
592     return FALSE;
593
594   theme = xp_theme_get_handle_by_element(element);
595   if (!theme)
596     return FALSE;
597
598   /* FIXME: Recheck its function */
599   enable_theme_dialog_texture_func(GDK_WINDOW_HWND(win), ETDT_ENABLETAB);
600
601   if (!GDK_IS_WINDOW(win))
602     {
603       xoff = 0;
604       yoff = 0;
605       drawable = win;
606     }
607   else
608     {
609       gdk_window_get_internal_paint_info(win, &drawable, &xoff, &yoff);
610     }
611   rect.left = x - xoff;
612   rect.top = y - yoff;
613   rect.right = rect.left + width;
614   rect.bottom = rect.top + height;
615
616   if (area)
617     {
618       clip.left = area->x - xoff;
619       clip.top = area->y - yoff;
620       clip.right = clip.left + area->width;
621       clip.bottom = clip.top + area->height;
622
623       pClip = &clip;
624     }
625   else
626     {
627       pClip = NULL;
628     }
629
630   gdk_gc_set_clip_rectangle (style->dark_gc[state_type], NULL);
631   dc = gdk_win32_hdc_get(drawable, style->dark_gc[state_type], 0);
632   if (!dc)
633     return FALSE;
634
635   part_state = xp_theme_map_gtk_state(element, state_type);
636   draw_theme_background_func(theme, dc, element_part_map[element], part_state, &rect, pClip);
637   gdk_win32_hdc_release(drawable, style->dark_gc[state_type], 0);
638
639   return TRUE;
640 }
641
642 gboolean
643 xp_theme_is_drawable(XpThemeElement element)
644 {
645   if (uxtheme_dll)
646     {
647       return (xp_theme_get_handle_by_element(element) != NULL);
648     }
649   return FALSE;
650 }
651
652 gboolean
653 xp_theme_get_system_font(int fontId, LOGFONT *lf)
654 {
655   if (get_theme_sys_font_func != NULL)
656     {
657       return ((*get_theme_sys_font_func)(NULL, fontId, lf) == S_OK);
658     }
659   return FALSE;
660 }