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