]> Pileus Git - ~andy/gtk/blob - modules/engines/ms-windows/msw_style.c
Patch 939583
[~andy/gtk] / modules / engines / ms-windows / msw_style.c
1 /* MS-Windows Engine (aka GTK-Wimp)
2  *
3  * Copyright (C) 2003, 2004 Raymond Penners <raymond@dotsphinx.com>
4  * Includes code adapted from redmond95 by Owen Taylor, and
5  * gtk-nativewin by Evan Martin
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "msw_style.h"
24 #include "xp_theme.h"
25
26 #include <windows.h>
27 #include <math.h>
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkwin32.h>
32
33 #include <stdio.h>
34
35 /* Default values, not normally used
36  */
37 static const GtkRequisition default_option_indicator_size = { 9, 8 };
38 static const GtkBorder default_option_indicator_spacing = { 7, 5, 2, 2 };
39
40 static GtkStyleClass *parent_class;
41
42 typedef enum {
43   CHECK_AA,
44   CHECK_BASE,
45   CHECK_BLACK,
46   CHECK_DARK,
47   CHECK_LIGHT,
48   CHECK_MID,
49   CHECK_TEXT,
50   RADIO_BASE,
51   RADIO_BLACK,
52   RADIO_DARK,
53   RADIO_LIGHT,
54   RADIO_MID,
55   RADIO_TEXT
56 } Part;
57
58 #define PART_SIZE 13
59
60 static const char check_aa_bits[] = {
61  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
62  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
63 static const char check_base_bits[] = {
64  0x00,0x00,0x00,0x00,0xfc,0x07,0xfc,0x07,0xfc,0x07,0xfc,0x07,0xfc,0x07,0xfc,
65  0x07,0xfc,0x07,0xfc,0x07,0xfc,0x07,0x00,0x00,0x00,0x00};
66 static const char check_black_bits[] = {
67  0x00,0x00,0xfe,0x0f,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,
68  0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x00,0x00};
69 static const char check_dark_bits[] = {
70  0xff,0x1f,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
71  0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00};
72 static const char check_light_bits[] = {
73  0x00,0x00,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,
74  0x10,0x00,0x10,0x00,0x10,0x00,0x10,0x00,0x10,0xfe,0x1f};
75 static const char check_mid_bits[] = {
76  0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,
77  0x08,0x00,0x08,0x00,0x08,0x00,0x08,0xfc,0x0f,0x00,0x00};
78 static const char check_text_bits[] = {
79  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x03,0x88,0x03,0xd8,0x01,0xf8,
80  0x00,0x70,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
81 static const char radio_base_bits[] = {
82  0x00,0x00,0x00,0x00,0xf0,0x01,0xf8,0x03,0xfc,0x07,0xfc,0x07,0xfc,0x07,0xfc,
83  0x07,0xfc,0x07,0xf8,0x03,0xf0,0x01,0x00,0x00,0x00,0x00};
84 static const char radio_black_bits[] = {
85  0x00,0x00,0xf0,0x01,0x0c,0x02,0x04,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,
86  0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
87 static const char radio_dark_bits[] = {
88  0xf0,0x01,0x0c,0x06,0x02,0x00,0x02,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,
89  0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00,0x00};
90 static const char radio_light_bits[] = {
91  0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x10,0x00,0x10,0x00,0x10,0x00,
92  0x10,0x00,0x10,0x00,0x08,0x00,0x08,0x0c,0x06,0xf0,0x01};
93 static const char radio_mid_bits[] = {
94  0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x08,0x00,
95  0x08,0x00,0x08,0x00,0x04,0x0c,0x06,0xf0,0x01,0x00,0x00};
96 static const char radio_text_bits[] = {
97  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0xf0,0x01,0xf0,0x01,0xf0,
98  0x01,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
99
100 static struct {
101   const char *bits;
102   GdkBitmap  *bmap;
103 } parts[] = {
104   { check_aa_bits, NULL },
105   { check_base_bits, NULL },
106   { check_black_bits, NULL },
107   { check_dark_bits, NULL },
108   { check_light_bits, NULL },
109   { check_mid_bits, NULL },
110   { check_text_bits, NULL },
111   { radio_base_bits, NULL },
112   { radio_black_bits, NULL },
113   { radio_dark_bits, NULL },
114   { radio_light_bits, NULL },
115   { radio_mid_bits, NULL },
116   { radio_text_bits, NULL }
117 };
118
119 static gboolean
120 get_system_font(XpThemeClass klazz, XpThemeFont type, LOGFONT *out_lf)
121 {
122 #if 0
123   /* TODO: this crashes. need to figure out why and how to fix it */
124   if (xp_theme_get_system_font(klazz, type, out_lf)) {
125           return TRUE;
126   } else
127 #endif
128   {
129           NONCLIENTMETRICS ncm;
130           ncm.cbSize = sizeof(NONCLIENTMETRICS);
131
132           if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
133                                    sizeof(NONCLIENTMETRICS), &ncm, 0))
134             {
135               if (type == XP_THEME_FONT_CAPTION)
136                 *out_lf = ncm.lfCaptionFont;
137               else if (type == XP_THEME_FONT_MENU)
138                 *out_lf = ncm.lfMenuFont;
139               else if (type == XP_THEME_FONT_STATUS)
140                 *out_lf = ncm.lfStatusFont;
141               else
142                 *out_lf = ncm.lfMessageFont;
143
144               return TRUE;
145             }
146   }
147   return FALSE;
148 }
149
150 /***************************** STOLEN FROM PANGO *****************************/
151
152 /*
153         This code is stolen from Pango 1.4. It attempts to address the following problems:
154
155         http://bugzilla.gnome.org/show_bug.cgi?id=135098
156         http://sourceforge.net/tracker/index.php?func=detail&aid=895762&group_id=76416&atid=547655
157
158         As Owen suggested in bug 135098, once Pango 1.6 is released, we need to get rid of this code.
159 */
160
161 #define PING(printlist)
162
163 /* TrueType defines: */
164
165 #define MAKE_TT_TABLE_NAME(c1, c2, c3, c4) \
166    (((guint32)c4) << 24 | ((guint32)c3) << 16 | ((guint32)c2) << 8 | ((guint32)c1))
167
168 #define CMAP (MAKE_TT_TABLE_NAME('c','m','a','p'))
169 #define CMAP_HEADER_SIZE 4
170
171 #define NAME (MAKE_TT_TABLE_NAME('n','a','m','e'))
172 #define NAME_HEADER_SIZE 6
173
174 #define ENCODING_TABLE_SIZE 8
175
176 #define APPLE_UNICODE_PLATFORM_ID 0
177 #define MACINTOSH_PLATFORM_ID 1
178 #define ISO_PLATFORM_ID 2
179 #define MICROSOFT_PLATFORM_ID 3
180
181 #define SYMBOL_ENCODING_ID 0
182 #define UNICODE_ENCODING_ID 1
183 #define UCS4_ENCODING_ID 10
184
185 struct name_header
186 {
187   guint16 format_selector;
188   guint16 num_records;
189   guint16 string_storage_offset;
190 };
191
192 struct name_record
193 {
194   guint16 platform_id;
195   guint16 encoding_id;
196   guint16 language_id;
197   guint16 name_id;
198   guint16 string_length;
199   guint16 string_offset;
200 };
201
202 gboolean
203 pango_win32_get_name_header (HDC                 hdc,
204                              struct name_header *header)
205 {
206   if (GetFontData (hdc, NAME, 0, header, sizeof (*header)) != sizeof (*header))
207     return FALSE;
208
209   header->num_records = GUINT16_FROM_BE (header->num_records);
210   header->string_storage_offset = GUINT16_FROM_BE (header->string_storage_offset);
211
212   return TRUE;
213 }
214
215 gboolean
216 pango_win32_get_name_record (HDC                 hdc,
217                              gint                i,
218                              struct name_record *record)
219 {
220   if (GetFontData (hdc, NAME, 6 + i * sizeof (*record),
221                    record, sizeof (*record)) != sizeof (*record))
222     return FALSE;
223
224   record->platform_id = GUINT16_FROM_BE (record->platform_id);
225   record->encoding_id = GUINT16_FROM_BE (record->encoding_id);
226   record->language_id = GUINT16_FROM_BE (record->language_id);
227   record->name_id = GUINT16_FROM_BE (record->name_id);
228   record->string_length = GUINT16_FROM_BE (record->string_length);
229   record->string_offset = GUINT16_FROM_BE (record->string_offset);
230
231   return TRUE;
232 }
233
234 static gchar *
235 get_family_name (LOGFONT *lfp, HDC pango_win32_hdc)
236 {
237   HFONT hfont;
238   HFONT oldhfont;
239
240   struct name_header header;
241   struct name_record record;
242
243   gint unicode_ix = -1, mac_ix = -1, microsoft_ix = -1;
244   gint name_ix;
245   gchar *codeset;
246
247   gchar *string = NULL;
248   gchar *name;
249
250   gint i, l;
251   gsize nbytes;
252
253   /* If lfFaceName is ASCII, assume it is the common (English) name
254    * for the font. Is this valid? Do some TrueType fonts have
255    * different names in French, German, etc, and does the system
256    * return these if the locale is set to use French, German, etc?
257    */
258   l = strlen (lfp->lfFaceName);
259   for (i = 0; i < l; i++)
260     if (lfp->lfFaceName[i] < ' ' || lfp->lfFaceName[i] > '~')
261       break;
262
263   if (i == l)
264     return g_strdup (lfp->lfFaceName);
265
266   if ((hfont = CreateFontIndirect (lfp)) == NULL)
267     goto fail0;
268
269   if ((oldhfont = SelectObject (pango_win32_hdc, hfont)) == NULL)
270     goto fail1;
271
272   if (!pango_win32_get_name_header (pango_win32_hdc, &header))
273     goto fail2;
274
275   PING (("%d name records", header.num_records));
276
277   for (i = 0; i < header.num_records; i++)
278     {
279       if (!pango_win32_get_name_record (pango_win32_hdc, i, &record))
280         goto fail2;
281
282       if ((record.name_id != 1 && record.name_id != 16) || record.string_length <= 0)
283         continue;
284
285       PING(("platform:%d encoding:%d language:%04x name_id:%d",
286             record.platform_id, record.encoding_id, record.language_id, record.name_id));
287
288       if (record.platform_id == APPLE_UNICODE_PLATFORM_ID ||
289           record.platform_id == ISO_PLATFORM_ID)
290         unicode_ix = i;
291       else if (record.platform_id == MACINTOSH_PLATFORM_ID &&
292                record.encoding_id == 0 && /* Roman */
293                record.language_id == 0) /* English */
294         mac_ix = i;
295       else if (record.platform_id == MICROSOFT_PLATFORM_ID)
296         if ((microsoft_ix == -1 ||
297              PRIMARYLANGID (record.language_id) == LANG_ENGLISH) &&
298             (record.encoding_id == SYMBOL_ENCODING_ID ||
299              record.encoding_id == UNICODE_ENCODING_ID ||
300              record.encoding_id == UCS4_ENCODING_ID))
301           microsoft_ix = i;
302     }
303
304   if (microsoft_ix >= 0)
305     name_ix = microsoft_ix;
306   else if (mac_ix >= 0)
307     name_ix = mac_ix;
308   else if (unicode_ix >= 0)
309     name_ix = unicode_ix;
310   else
311     goto fail2;
312
313   if (!pango_win32_get_name_record (pango_win32_hdc, name_ix, &record))
314     goto fail2;
315
316   string = g_malloc (record.string_length + 1);
317   if (GetFontData (pango_win32_hdc, NAME,
318                    header.string_storage_offset + record.string_offset,
319                    string, record.string_length) != record.string_length)
320     goto fail2;
321
322   string[record.string_length] = '\0';
323
324   if (name_ix == microsoft_ix)
325     if (record.encoding_id == SYMBOL_ENCODING_ID ||
326         record.encoding_id == UNICODE_ENCODING_ID)
327       codeset = "UTF-16BE";
328     else
329       codeset = "UCS-4BE";
330   else if (name_ix == mac_ix)
331     codeset = "MacRoman";
332   else /* name_ix == unicode_ix */
333     codeset = "UCS-4BE";
334
335   name = g_convert (string, record.string_length, "UTF-8", codeset, NULL, &nbytes, NULL);
336   if (name == NULL)
337     goto fail2;
338   g_free (string);
339
340   PING(("%s", name));
341
342   SelectObject (pango_win32_hdc, oldhfont);
343   DeleteObject (hfont);
344
345   return name;
346
347  fail2:
348   g_free (string);
349   SelectObject (pango_win32_hdc, oldhfont);
350
351  fail1:
352   DeleteObject (hfont);
353
354  fail0:
355   return g_locale_to_utf8 (lfp->lfFaceName, -1, NULL, NULL, NULL);
356 }
357
358 /***************************** STOLEN FROM PANGO *****************************/
359
360 static char *
361 sys_font_to_pango_font (XpThemeClass klazz, XpThemeFont type, char * buf, size_t bufsiz)
362 {
363   HDC hDC;
364   HWND hwnd;
365   LOGFONT lf;
366   int pt_size;
367   const char * weight;
368   const char * style;
369   char * font;
370
371   if (get_system_font(klazz, type, &lf))
372     {
373       switch (lf.lfWeight) {
374       case FW_THIN:
375       case FW_EXTRALIGHT:
376         weight = "Ultra-Light";
377         break;
378
379       case FW_LIGHT:
380         weight = "Light";
381         break;
382
383       case FW_BOLD:
384         weight = "Bold";
385         break;
386
387       case FW_SEMIBOLD:
388         weight = "Semi-Bold";
389         break;
390
391       case FW_ULTRABOLD:
392         weight = "Ultra-Bold";
393         break;
394
395       case FW_HEAVY:
396         weight = "Heavy";
397         break;
398
399       default:
400         weight = "";
401         break;
402       }
403
404       if (lf.lfItalic)
405         style="Italic";
406       else
407         style="";
408
409         hwnd = GetDesktopWindow();
410         hDC = GetDC(hwnd);
411         if (hDC) {
412                 pt_size = -MulDiv(lf.lfHeight, 72,
413                               GetDeviceCaps(hDC,LOGPIXELSY));
414                 ReleaseDC(hwnd, hDC);
415         } else
416                 pt_size = 10;
417
418         font = get_family_name(&lf, hDC);
419     g_snprintf(buf, bufsiz, "%s %s %s %d", font, style, weight, pt_size);
420         g_free(font);
421
422     return buf;
423    }
424
425   return NULL;
426 }
427
428 /* missing from ms's header files */
429 #ifndef SPI_GETMENUSHOWDELAY
430 #define SPI_GETMENUSHOWDELAY 106
431 #endif
432
433 /* I don't know the proper XP theme class for things like
434    HIGHLIGHTTEXT, so we'll just define it to be "BUTTON"
435    for now */
436 #define XP_THEME_CLASS_TEXT XP_THEME_CLASS_BUTTON
437
438 static void
439 setup_menu_settings (void)
440 {
441   int menu_delay;
442   gboolean win95 = FALSE;
443
444   GtkSettings * settings;
445   OSVERSIONINFOEX osvi;
446
447   settings = gtk_settings_get_default ();
448   if (!settings)
449     return;
450
451   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
452   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
453
454   if (!GetVersionEx ( (OSVERSIONINFO *) &osvi))
455     win95 = TRUE; /* assume the worst */
456
457   if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
458     if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
459       win95 = TRUE;
460
461   if (!win95) {
462     if (SystemParametersInfo (SPI_GETMENUSHOWDELAY, 0, &menu_delay, 0)) {
463       GObjectClass * klazz = G_OBJECT_GET_CLASS(G_OBJECT(settings));
464
465       if (klazz) {
466         if (g_object_class_find_property (klazz, "gtk-menu-bar-popup-delay")) {
467           g_object_set (G_OBJECT (settings), "gtk-menu-bar-popup-delay",
468                         0, NULL);
469         }
470         if (g_object_class_find_property (klazz, "gtk-menu-popup-delay")) {
471           g_object_set (G_OBJECT (settings), "gtk-menu-popup-delay",
472                         menu_delay, NULL);
473         }
474         if (g_object_class_find_property (klazz, "gtk-menu-popdown-delay")) {
475           g_object_set (G_OBJECT (settings), "gtk-menu-popdown-delay",
476                         menu_delay, NULL);
477         }
478       }
479     }
480   }
481 }
482
483 void
484 msw_style_setup_system_settings (void)
485 {
486   GtkSettings * settings;
487   int cursor_blink_time;
488
489   settings = gtk_settings_get_default ();
490   if (!settings)
491     return;
492
493   cursor_blink_time = GetCaretBlinkTime ();
494   g_object_set (G_OBJECT (settings), "gtk-cursor-blink",
495                         cursor_blink_time > 0, NULL);
496
497   if (cursor_blink_time > 0)
498   {
499         g_object_set (G_OBJECT (settings), "gtk-cursor-blink-time",
500                                         2*cursor_blink_time, NULL);
501   }
502
503   g_object_set (G_OBJECT (settings), "gtk-double-click-time",
504                 GetDoubleClickTime(), NULL);
505   g_object_set (G_OBJECT (settings), "gtk-dnd-drag-threshold",
506                 GetSystemMetrics(SM_CXDRAG), NULL);
507
508   setup_menu_settings ();
509
510   /*
511      http://developer.gnome.org/doc/API/2.0/gtk/GtkSettings.html
512      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/systemparametersinfo.asp
513      http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getsystemmetrics.asp
514   */
515 }
516
517 static void
518 setup_system_font(GtkStyle *style)
519 {
520   char buf[256], * font; /* It's okay, lfFaceName is smaller than 32 chars */
521
522   if ((font = sys_font_to_pango_font(XP_THEME_CLASS_TEXT,
523                                      XP_THEME_FONT_MESSAGE,
524                                      buf, sizeof (buf))) != NULL)
525     {
526       if (style->font_desc)
527         {
528           pango_font_description_free (style->font_desc);
529         }
530       style->font_desc = pango_font_description_from_string(font);
531     }
532 }
533
534 static void
535 sys_color_to_gtk_color(XpThemeClass klazz, int id, GdkColor *pcolor)
536 {
537   DWORD color;
538
539   if (!xp_theme_get_system_color (klazz, id, &color))
540         color = GetSysColor(id);
541
542   pcolor->pixel = color;
543   pcolor->red   = (GetRValue(color) << 8) | GetRValue(color);
544   pcolor->green = (GetGValue(color) << 8) | GetGValue(color);
545   pcolor->blue  = (GetBValue(color) << 8) | GetBValue(color);
546 }
547
548 static int
549 get_system_metric(XpThemeClass klazz, int id)
550 {
551   int rval;
552
553   if (!xp_theme_get_system_metric(klazz, id, &rval))
554     rval = GetSystemMetrics (id);
555
556   return rval;
557 }
558
559 #if 0
560 static void
561 setup_default_style (void)
562 {
563         GdkColor btnface;
564         GdkColor highlight;
565         GdkColor window;
566         GdkColor windowtext;
567         GdkColor highlighttext;
568         GdkColor graytext;
569         GdkColor btntext;
570         GdkColor dark;
571         GdkColor light;
572         GdkColor mid;
573         GdkColor text_aa[5];
574
575         char buf[2048];
576
577         sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,       &btnface);
578         sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHT,     &highlight);
579         sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOW,        &window);
580         sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOWTEXT,    &windowtext);
581         sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHTTEXT, &highlighttext);
582         sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_GRAYTEXT,      &graytext);
583         sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNTEXT,       &btntext);
584         sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_3DSHADOW,      &dark);
585         sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_3DHILIGHT,     &light);
586
587         mid.red = (light.red + dark.red) / 2;
588         mid.green = (light.green + dark.green) / 2;
589         mid.blue = (light.blue + dark.blue) / 2;
590
591         text_aa[0].red = (windowtext.red + window.red) / 2;
592         text_aa[0].green = (windowtext.green + window.green) / 2;
593         text_aa[0].blue = (windowtext.blue + window.blue) / 2;
594         text_aa[1].red = (highlighttext.red + highlight.red) / 2;
595         text_aa[1].green = (highlighttext.green + highlight.green) / 2;
596         text_aa[1].blue = (highlighttext.blue + highlight.blue) / 2;
597
598         text_aa[2].red = (graytext.red + btnface.red) / 2;
599         text_aa[2].green = (graytext.green + btnface.green) / 2;
600         text_aa[2].blue = (graytext.blue + btnface.blue) / 2;
601
602         text_aa[3].red = (btntext.red + btnface.red) / 2;
603         text_aa[3].green = (btntext.green + btnface.green) / 2;
604         text_aa[3].blue = (btntext.blue + btnface.blue) / 2;
605
606         text_aa[4].red = (windowtext.red + window.red) / 2;
607         text_aa[4].green = (windowtext.green + window.green) / 2;
608         text_aa[4].blue = (windowtext.blue + window.blue) / 2;
609
610         g_snprintf(buf, sizeof (buf),
611                   "style \"msw-base\" = \"msw-default\"\n"
612                   "{\n"
613                   "bg[NORMAL] = { %d, %d, %d }\n"
614                   "bg[SELECTED] = { %d, %d, %d }\n"
615                   "bg[INSENSITIVE] = { %d, %d, %d }\n"
616                   "bg[ACTIVE] = { %d, %d, %d }\n"
617                   "bg[PRELIGHT] = { %d, %d, %d }\n"
618                   "base[NORMAL] = { %d, %d, %d }\n"
619                   "base[SELECTED] = { %d, %d, %d }\n"
620                   "base[INSENSITIVE] = { %d, %d, %d }\n"
621                   "base[ACTIVE] = { %d, %d, %d }\n"
622                   "base[PRELIGHT] = { %d, %d, %d }\n"
623                   "text[NORMAL] = { %d, %d, %d }\n"
624                   "text[SELECTED] = { %d, %d, %d }\n"
625                   "text[INSENSITIVE] = { %d, %d, %d }\n"
626                   "text[ACTIVE] = { %d, %d, %d }\n"
627                   "text[PRELIGHT] = { %d, %d, %d }\n"
628                   "fg[NORMAL] = { %d, %d, %d }\n"
629                   "fg[SELECTED] = { %d, %d, %d }\n"
630                   "fg[INSENSITIVE] = { %d, %d, %d }\n"
631                   "fg[ACTIVE] = { %d, %d, %d }\n"
632                   "fg[PRELIGHT] = { %d, %d, %d }\n"
633                   "dark[NORMAL] = { %d, %d, %d }\n"
634                   "dark[SELECTED] = { %d, %d, %d }\n"
635                   "dark[INSENSITIVE] = { %d, %d, %d }\n"
636                   "dark[ACTIVE] = { %d, %d, %d }\n"
637                   "dark[PRELIGHT] = { %d, %d, %d }\n"
638                   "light[NORMAL] = { %d, %d, %d }\n"
639                   "light[SELECTED] = { %d, %d, %d }\n"
640                   "light[INSENSITIVE] = { %d, %d, %d }\n"
641                   "light[ACTIVE] = { %d, %d, %d }\n"
642                   "light[PRELIGHT] = { %d, %d, %d }\n"
643                   "text_aa[NORMAL] = { %d, %d, %d }\n"
644                   "text_aa[SELECTED] = { %d, %d, %d }\n"
645                   "text_aa[INSENSITIVE] = { %d, %d, %d }\n"
646                   "text_aa[ACTIVE] = { %d, %d, %d }\n"
647                   "text_aa[PRELIGHT] = { %d, %d, %d }\n"
648                   "}widget_class \"*\" style \"msw-base\"\n",
649
650                   /* bg */
651                   btnface.red, btnface.green, btnface.blue,
652                   highlight.red, highlight.green, highlight.blue,
653                   btnface.red, btnface.green, btnface.blue,
654                   btnface.red, btnface.green, btnface.blue,
655                   btnface.red, btnface.green, btnface.blue,
656
657                   /* base */
658                   window.red, window.green, window.blue,
659                   highlight.red, highlight.green, highlight.blue,
660                   btnface.red, btnface.green, btnface.blue,
661                   btnface.red, btnface.green, btnface.blue,
662                   window.red, window.green, window.blue,
663
664                   /* text */
665                   windowtext.red, windowtext.green, windowtext.blue,
666                   highlighttext.red, highlighttext.green, highlighttext.blue,
667                   graytext.red, graytext.green, graytext.blue,
668                   btntext.red, btntext.green, btntext.blue,
669                   windowtext.red, windowtext.green, windowtext.blue,
670
671                   /* fg */
672                   btntext.red, btntext.green, btntext.blue,
673                   highlighttext.red, highlighttext.green, highlighttext.blue,
674                   graytext.red, graytext.green, graytext.blue,
675                   btntext.red, btntext.green, btntext.blue,
676                   windowtext.red, windowtext.green, windowtext.blue,
677
678                   /* dark */
679                   dark.red, dark.green, dark.blue,
680                   dark.red, dark.green, dark.blue,
681                   dark.red, dark.green, dark.blue,
682                   dark.red, dark.green, dark.blue,
683                   dark.red, dark.green, dark.blue,
684
685                   /* light */
686                   light.red, light.green, light.blue,
687                   light.red, light.green, light.blue,
688                   light.red, light.green, light.blue,
689                   light.red, light.green, light.blue,
690                   light.red, light.green, light.blue,
691
692                   /* text_aa */
693                   text_aa[0].red, text_aa[0].green, text_aa[0].blue,
694                   text_aa[1].red, text_aa[1].green, text_aa[1].blue,
695                   text_aa[2].red, text_aa[2].green, text_aa[2].blue,
696                   text_aa[3].red, text_aa[3].green, text_aa[3].blue,
697                   text_aa[4].red, text_aa[4].green, text_aa[4].blue
698                   );
699                   gtk_rc_parse_string(buf);
700 }
701 #endif
702
703 static void
704 setup_msw_rc_style(void)
705 {
706   /* TODO: Owen says:
707          "If your setup_system_styles() function called gtk_rc_parse_string(), then you are just piling a new set of strings on top each time the theme changes .. the old ones won't be removed" */
708
709   char buf[1024], font_buf[256], *font_ptr;
710
711   GdkColor menu_color;
712   GdkColor menu_text_color;
713   GdkColor tooltip_back;
714   GdkColor tooltip_fore;
715   GdkColor btn_fore;
716   GdkColor btn_face;
717   GdkColor progress_back;
718
719   GdkColor fg_prelight;
720   GdkColor bg_prelight;
721   GdkColor base_prelight;
722   GdkColor text_prelight;
723
724   NONCLIENTMETRICS nc;
725
726   /* Prelight */
727   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT, &fg_prelight);
728   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT, &bg_prelight);
729   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHT, &base_prelight);
730   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT, COLOR_HIGHLIGHTTEXT, &text_prelight);
731
732   sys_color_to_gtk_color(XP_THEME_CLASS_MENU, COLOR_MENUTEXT, &menu_text_color);
733   sys_color_to_gtk_color(XP_THEME_CLASS_MENU, COLOR_MENU, &menu_color);
734
735   /* tooltips */
736   sys_color_to_gtk_color(XP_THEME_CLASS_TOOLTIP, COLOR_INFOTEXT, &tooltip_fore);
737   sys_color_to_gtk_color(XP_THEME_CLASS_TOOLTIP, COLOR_INFOBK, &tooltip_back);
738
739   /* text on push buttons. TODO: button shadows, backgrounds, and highlights */
740   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON, COLOR_BTNTEXT, &btn_fore);
741   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON, COLOR_BTNFACE, &btn_face);
742
743   /* progress bar background color */
744   sys_color_to_gtk_color(XP_THEME_CLASS_PROGRESS, COLOR_HIGHLIGHT, &progress_back);
745
746   /* Enable coloring for menus. */
747   font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_MENU, XP_THEME_FONT_MENU,font_buf, sizeof (font_buf));
748   g_snprintf(buf, sizeof (buf),
749              "style \"msw-menu\" = \"msw-default\"\n"
750              "{\n"
751              "fg[PRELIGHT] = { %d, %d, %d }\n"
752              "bg[PRELIGHT] = { %d, %d, %d }\n"
753              "text[PRELIGHT] = { %d, %d, %d }\n"
754              "base[PRELIGHT] = { %d, %d, %d }\n"
755              "fg[NORMAL] = { %d, %d, %d }\n"
756              "bg[NORMAL] = { %d, %d, %d }\n"
757              "%s = \"%s\"\n"
758              "}widget_class \"*MenuItem*\" style \"msw-menu\"\n"
759              "widget_class \"*GtkMenu\" style \"msw-menu\"\n"
760              "widget_class \"*GtkMenuShell*\" style \"msw-menu\"\n",
761              fg_prelight.red,
762              fg_prelight.green,
763              fg_prelight.blue,
764              bg_prelight.red,
765              bg_prelight.green,
766              bg_prelight.blue,
767              text_prelight.red,
768              text_prelight.green,
769              text_prelight.blue,
770              base_prelight.red,
771              base_prelight.green,
772              base_prelight.blue,
773              menu_text_color.red,
774              menu_text_color.green,
775              menu_text_color.blue,
776              menu_color.red,
777              menu_color.green,
778              menu_color.blue,
779              (font_ptr ? "font_name" : "#"),
780              (font_ptr ? font_ptr : " font name should go here"));
781   gtk_rc_parse_string(buf);
782
783   /* Enable coloring for menu bars. */
784   font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_MENU, XP_THEME_FONT_MENU,font_buf, sizeof (font_buf));
785   g_snprintf(buf, sizeof (buf),
786              "style \"msw-menu-bar\" = \"msw-menu\"\n"
787              "{\n"
788              "bg[NORMAL] = { %d, %d, %d }\n"
789              "}widget_class \"*MenuBar*\" style \"msw-menu-bar\"\n",
790              btn_face.red,
791              btn_face.green,
792              btn_face.blue);
793   gtk_rc_parse_string(buf);
794
795   /* enable tooltip fonts */
796   font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,font_buf, sizeof (font_buf));
797   g_snprintf(buf, sizeof (buf),
798              "style \"msw-tooltips-caption\" = \"msw-default\"\n"
799              "{fg[NORMAL] = { %d, %d, %d }\n"
800              "%s = \"%s\"\n"
801              "}widget \"gtk-tooltips.GtkLabel\" style \"msw-tooltips-caption\"\n",
802              tooltip_fore.red,
803              tooltip_fore.green,
804              tooltip_fore.blue,
805              (font_ptr ? "font_name" : "#"),
806              (font_ptr ? font_ptr : " font name should go here"));
807   gtk_rc_parse_string(buf);
808
809   g_snprintf(buf, sizeof (buf),
810              "style \"msw-tooltips\" = \"msw-default\"\n"
811              "{bg[NORMAL] = { %d, %d, %d }\n"
812              "}widget \"gtk-tooltips*\" style \"msw-tooltips\"\n",
813              tooltip_back.red,
814              tooltip_back.green,
815              tooltip_back.blue);
816   gtk_rc_parse_string(buf);
817
818   /* enable font theming for status bars */
819   font_ptr = sys_font_to_pango_font (XP_THEME_CLASS_STATUS, XP_THEME_FONT_STATUS,font_buf, sizeof (font_buf));
820   g_snprintf(buf, sizeof (buf),
821              "style \"msw-status\" = \"msw-default\"\n"
822              "{%s = \"%s\"\n"
823              "bg[NORMAL] = { %d, %d, %d }\n"
824              "}widget_class \"*Status*\" style \"msw-status\"\n",
825              (font_ptr ? "font_name" : "#"),
826              (font_ptr ? font_ptr : " font name should go here"),
827              btn_face.red, btn_face.green, btn_face.blue);
828   gtk_rc_parse_string(buf);
829
830   /* enable coloring for text on buttons
831      TODO: use GetThemeMetric for the border and outside border */
832   g_snprintf(buf, sizeof (buf),
833              "style \"msw-button\" = \"msw-default\"\n"
834              "{\n"
835              "bg[NORMAL] = { %d, %d, %d }\n"
836              "bg[PRELIGHT] = { %d, %d, %d }\n"
837              "bg[INSENSITIVE] = { %d, %d, %d }\n"
838              "fg[PRELIGHT] = { %d, %d, %d }\n"
839              "GtkButton::default-border = { 1, 1, 1, 1 }\n"
840              "GtkButton::default-outside-border = { 0, 0, 0, 0 }\n"
841              "GtkButton::child-displacement-x = 1\n"
842              "GtkButton::child-displacement-y = 1\n"
843              "}widget_class \"*Button*\" style \"msw-button\"\n",
844              btn_face.red, btn_face.green, btn_face.blue,
845              btn_face.red, btn_face.green, btn_face.blue,
846              btn_face.red, btn_face.green, btn_face.blue,
847              btn_fore.red, btn_fore.green, btn_fore.blue
848              );
849   gtk_rc_parse_string(buf);
850
851   /* enable coloring for progress bars */
852   g_snprintf(buf, sizeof (buf),
853              "style \"msw-progress\" = \"msw-default\"\n"
854              "{bg[PRELIGHT] = { %d, %d, %d }\n"
855              "bg[NORMAL] = { %d, %d, %d }\n"
856              "}widget_class \"*Progress*\" style \"msw-progress\"\n",
857              progress_back.red,
858              progress_back.green,
859              progress_back.blue,
860              btn_face.red, btn_face.green, btn_face.blue);
861   gtk_rc_parse_string(buf);
862
863   /* scrollbar thumb width and height */
864   g_snprintf(buf, sizeof (buf),
865              "style \"msw-vscrollbar\" = \"msw-default\"\n"
866              "{GtkRange::slider-width = %d\n"
867              "GtkRange::stepper-size = %d\n"
868              "GtkRange::stepper-spacing = 0\n"
869              "GtkRange::trough_border = 0\n"
870              "}widget_class \"*VScrollbar*\" style \"msw-vscrollbar\"\n",
871              GetSystemMetrics(SM_CYVTHUMB),
872              get_system_metric(XP_THEME_CLASS_SCROLLBAR, SM_CXVSCROLL));
873   gtk_rc_parse_string(buf);
874
875   g_snprintf(buf, sizeof (buf),
876              "style \"msw-hscrollbar\" = \"msw-default\"\n"
877              "{GtkRange::slider-width = %d\n"
878              "GtkRange::stepper-size = %d\n"
879              "GtkRange::stepper-spacing = 0\n"
880              "GtkRange::trough_border = 0\n"
881              "}widget_class \"*HScrollbar*\" style \"msw-hscrollbar\"\n",
882              GetSystemMetrics(SM_CXHTHUMB),
883              get_system_metric(XP_THEME_CLASS_SCROLLBAR, SM_CYHSCROLL));
884   gtk_rc_parse_string(buf);
885
886   /* radio/check button sizes */
887   g_snprintf(buf, sizeof (buf),
888              "style \"msw-checkbutton\" = \"msw-button\"\n"
889              "{GtkCheckButton::indicator-size = 13\n"
890              "}widget_class \"*CheckButton*\" style \"msw-checkbutton\"\n"
891              "widget_class \"*RadioButton*\" style \"msw-checkbutton\"\n");
892   gtk_rc_parse_string(buf);
893 }
894
895 static void
896 setup_system_styles(GtkStyle *style)
897 {
898   int i;
899
900   /* Default background */
901   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->bg[GTK_STATE_NORMAL]);
902   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHT, &style->bg[GTK_STATE_SELECTED]);
903   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->bg[GTK_STATE_INSENSITIVE]);
904   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->bg[GTK_STATE_ACTIVE]);
905   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->bg[GTK_STATE_PRELIGHT]);
906
907   /* Default base */
908   sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOW,    &style->base[GTK_STATE_NORMAL]);
909   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHT, &style->base[GTK_STATE_SELECTED]);
910   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->base[GTK_STATE_INSENSITIVE]);
911   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNFACE,   &style->base[GTK_STATE_ACTIVE]);
912   sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOW,    &style->base[GTK_STATE_PRELIGHT]);
913
914   /* Default text */
915   sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOWTEXT,    &style->text[GTK_STATE_NORMAL]);
916   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHTTEXT, &style->text[GTK_STATE_SELECTED]);
917   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_GRAYTEXT,      &style->text[GTK_STATE_INSENSITIVE]);
918   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNTEXT,       &style->text[GTK_STATE_ACTIVE]);
919   sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOWTEXT,    &style->text[GTK_STATE_PRELIGHT]);
920
921   /* Default forgeground */
922   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNTEXT,       &style->fg[GTK_STATE_NORMAL]);
923   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_HIGHLIGHTTEXT, &style->fg[GTK_STATE_SELECTED]);
924   sys_color_to_gtk_color(XP_THEME_CLASS_TEXT,    COLOR_GRAYTEXT,      &style->fg[GTK_STATE_INSENSITIVE]);
925   sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON,  COLOR_BTNTEXT,       &style->fg[GTK_STATE_ACTIVE]);
926   sys_color_to_gtk_color(XP_THEME_CLASS_WINDOW,  COLOR_WINDOWTEXT,    &style->fg[GTK_STATE_PRELIGHT]);
927
928   for (i = 0; i < 5; i++)
929     {
930       sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON, COLOR_3DSHADOW, &style->dark[i]);
931       sys_color_to_gtk_color(XP_THEME_CLASS_BUTTON, COLOR_3DHILIGHT, &style->light[i]);
932
933       style->mid[i].red = (style->light[i].red + style->dark[i].red) / 2;
934       style->mid[i].green = (style->light[i].green + style->dark[i].green) / 2;
935       style->mid[i].blue = (style->light[i].blue + style->dark[i].blue) / 2;
936
937       style->text_aa[i].red = (style->text[i].red + style->base[i].red) / 2;
938       style->text_aa[i].green = (style->text[i].green + style->base[i].green) / 2;
939       style->text_aa[i].blue = (style->text[i].blue + style->base[i].blue) / 2;
940     }
941 }
942
943 static gboolean
944 sanitize_size (GdkWindow *window,
945                gint      *width,
946                gint      *height)
947 {
948   gboolean set_bg = FALSE;
949
950   if ((*width == -1) && (*height == -1))
951     {
952       set_bg = GDK_IS_WINDOW (window);
953       gdk_window_get_size (window, width, height);
954     }
955   else if (*width == -1)
956     gdk_window_get_size (window, width, NULL);
957   else if (*height == -1)
958     gdk_window_get_size (window, NULL, height);
959
960   return set_bg;
961 }
962
963 static XpThemeElement
964 map_gtk_progress_bar_to_xp(GtkProgressBar *progress_bar, gboolean trough)
965 {
966   XpThemeElement ret;
967   switch (progress_bar->orientation)
968     {
969     case GTK_PROGRESS_LEFT_TO_RIGHT:
970     case GTK_PROGRESS_RIGHT_TO_LEFT:
971       ret = trough
972         ? XP_THEME_ELEMENT_PROGRESS_TROUGH_H
973         : XP_THEME_ELEMENT_PROGRESS_BAR_H;
974       break;
975     default:
976       ret = trough
977         ? XP_THEME_ELEMENT_PROGRESS_TROUGH_V
978         : XP_THEME_ELEMENT_PROGRESS_BAR_V;
979       break;
980     }
981   return ret;
982 }
983
984 static void
985 draw_part (GdkDrawable  *drawable,
986            GdkGC        *gc,
987            GdkRectangle *area,
988            gint          x,
989            gint          y,
990            Part          part)
991 {
992   if (area)
993     gdk_gc_set_clip_rectangle (gc, area);
994
995   if (!parts[part].bmap)
996       parts[part].bmap = gdk_bitmap_create_from_data (drawable,
997                                                       parts[part].bits,
998                                                       PART_SIZE, PART_SIZE);
999
1000   gdk_gc_set_ts_origin (gc, x, y);
1001   gdk_gc_set_stipple (gc, parts[part].bmap);
1002   gdk_gc_set_fill (gc, GDK_STIPPLED);
1003
1004   gdk_draw_rectangle (drawable, gc, TRUE, x, y, PART_SIZE, PART_SIZE);
1005
1006   gdk_gc_set_fill (gc, GDK_SOLID);
1007
1008   if (area)
1009     gdk_gc_set_clip_rectangle (gc, NULL);
1010 }
1011
1012 static void
1013 draw_check(GtkStyle      *style,
1014            GdkWindow     *window,
1015            GtkStateType   state,
1016            GtkShadowType  shadow,
1017            GdkRectangle  *area,
1018            GtkWidget     *widget,
1019            const gchar   *detail,
1020            gint           x,
1021            gint           y,
1022            gint           width,
1023            gint           height)
1024 {
1025   x -= (1 + PART_SIZE - width) / 2;
1026   y -= (1 + PART_SIZE - height) / 2;
1027
1028   if (detail && strcmp (detail, "check") == 0)  /* Menu item */
1029     {
1030       if (shadow == GTK_SHADOW_IN)
1031         {
1032           draw_part (window, style->black_gc, area, x, y, CHECK_TEXT);
1033           draw_part (window, style->dark_gc[state], area, x, y, CHECK_AA);
1034         }
1035     }
1036   else
1037     {
1038       if (xp_theme_draw(window, shadow == GTK_SHADOW_IN
1039                         ? XP_THEME_ELEMENT_PRESSED_CHECKBOX
1040                         : XP_THEME_ELEMENT_CHECKBOX,
1041                         style, x, y, width, height, state, area))
1042         {
1043         }
1044       else
1045         {
1046           draw_part (window, style->black_gc, area, x, y, CHECK_BLACK);
1047           draw_part (window, style->dark_gc[state], area, x, y, CHECK_DARK);
1048           draw_part (window, style->mid_gc[state], area, x, y, CHECK_MID);
1049           draw_part (window, style->light_gc[state], area, x, y, CHECK_LIGHT);
1050           draw_part (window, style->base_gc[state], area, x, y, CHECK_BASE);
1051
1052           if (shadow == GTK_SHADOW_IN)
1053             {
1054               draw_part (window, style->text_gc[state], area, x, y, CHECK_TEXT);
1055               draw_part (window, style->text_aa_gc[state], area, x, y, CHECK_AA);
1056             }
1057         }
1058     }
1059 }
1060
1061 static void
1062 draw_expander(GtkStyle      *style,
1063               GdkWindow     *window,
1064               GtkStateType   state,
1065               GdkRectangle  *area,
1066               GtkWidget     *widget,
1067               const gchar   *detail,
1068               gint           x,
1069               gint           y,
1070               GtkExpanderStyle expander_style)
1071 {
1072   gint expander_size;
1073   gint expander_semi_size;
1074   GdkColor color;
1075   GdkGCValues values;
1076   gboolean success;
1077   XpThemeElement xp_expander;
1078
1079   gtk_widget_style_get (widget, "expander_size", &expander_size, NULL);
1080
1081   switch (expander_style)
1082     {
1083     case GTK_EXPANDER_COLLAPSED:
1084     case GTK_EXPANDER_SEMI_COLLAPSED:
1085       xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_CLOSED;
1086       break;
1087     default:
1088       xp_expander = XP_THEME_ELEMENT_TREEVIEW_EXPANDER_OPENED;
1089       break;
1090     }
1091
1092   if ((expander_size % 2) == 0)
1093     expander_size--;
1094
1095   if (expander_size > 2)
1096     expander_size -= 2;
1097
1098   if (area)
1099     gdk_gc_set_clip_rectangle (style->fg_gc[state], area);
1100
1101   expander_semi_size = expander_size / 2;
1102   x -= expander_semi_size;
1103   y -= expander_semi_size;
1104
1105   gdk_gc_get_values (style->fg_gc[state], &values);
1106
1107   if (! xp_theme_draw(window, xp_expander, style,
1108                       x, y,
1109                       expander_size, expander_size, state, area))
1110     {
1111       /* RGB values to emulate Windows Classic style */
1112       color.red = color.green = color.blue = 128 << 8;
1113
1114       success = gdk_colormap_alloc_color
1115         (gtk_widget_get_default_colormap (), &color, FALSE, TRUE);
1116
1117       if (success)
1118         gdk_gc_set_foreground (style->fg_gc[state], &color);
1119
1120       gdk_draw_rectangle
1121         (window, style->fg_gc[state], FALSE, x, y,
1122          expander_size - 1, expander_size - 1);
1123
1124       gdk_draw_line
1125         (window, style->fg_gc[state], x + 2, y + expander_semi_size,
1126          x + expander_size - 2, y + expander_semi_size);
1127
1128       switch (expander_style)
1129         {
1130         case GTK_EXPANDER_COLLAPSED:
1131         case GTK_EXPANDER_SEMI_COLLAPSED:
1132           gdk_draw_line
1133             (window, style->fg_gc[state], x + expander_semi_size, y + 2,
1134              x + expander_semi_size, y + expander_size - 2);
1135           break;
1136         }
1137
1138       if (success)
1139         gdk_gc_set_foreground (style->fg_gc[state], &values.foreground);
1140     }
1141
1142   if (area)
1143     gdk_gc_set_clip_rectangle (style->fg_gc[state], NULL);
1144 }
1145
1146 static void
1147 draw_option(GtkStyle      *style,
1148             GdkWindow     *window,
1149             GtkStateType   state,
1150             GtkShadowType  shadow,
1151             GdkRectangle  *area,
1152             GtkWidget     *widget,
1153             const gchar   *detail,
1154             gint           x,
1155             gint           y,
1156             gint           width,
1157             gint           height)
1158 {
1159   x -= (1 + PART_SIZE - width) / 2;
1160   y -= (1 + PART_SIZE - height) / 2;
1161
1162   if (detail && strcmp (detail, "option") == 0) /* Menu item */
1163     {
1164       if (shadow == GTK_SHADOW_IN)
1165         draw_part (window, style->fg_gc[state], area, x, y, RADIO_TEXT);
1166     }
1167   else
1168     {
1169       if (xp_theme_draw (window, shadow == GTK_SHADOW_IN
1170                         ? XP_THEME_ELEMENT_PRESSED_RADIO_BUTTON
1171                         : XP_THEME_ELEMENT_RADIO_BUTTON,
1172                         style, x, y, width, height, state, area))
1173         {
1174         }
1175       else
1176         {
1177           draw_part (window, style->black_gc, area, x, y, RADIO_BLACK);
1178           draw_part (window, style->dark_gc[state], area, x, y, RADIO_DARK);
1179           draw_part (window, style->mid_gc[state], area, x, y, RADIO_MID);
1180           draw_part (window, style->light_gc[state], area, x, y, RADIO_LIGHT);
1181           draw_part (window, style->base_gc[state], area, x, y, RADIO_BASE);
1182
1183           if (shadow == GTK_SHADOW_IN)
1184             draw_part (window, style->text_gc[state], area, x, y, RADIO_TEXT);
1185         }
1186     }
1187 }
1188
1189 static void
1190 draw_varrow (GdkWindow     *window,
1191              GdkGC         *gc,
1192              GtkShadowType  shadow_type,
1193              GdkRectangle  *area,
1194              GtkArrowType   arrow_type,
1195              gint           x,
1196              gint           y,
1197              gint           width,
1198              gint           height)
1199 {
1200   gint steps, extra;
1201   gint y_start, y_increment;
1202   gint i;
1203
1204   if (area)
1205     gdk_gc_set_clip_rectangle (gc, area);
1206
1207   width = width + width % 2 - 1;        /* Force odd */
1208
1209   steps = 1 + width / 2;
1210
1211   extra = height - steps;
1212
1213   if (arrow_type == GTK_ARROW_DOWN)
1214     {
1215       y_start = y;
1216       y_increment = 1;
1217     }
1218   else
1219     {
1220       y_start = y + height - 1;
1221       y_increment = -1;
1222     }
1223
1224 #if 0
1225   for (i = 0; i < extra; i++)
1226     {
1227       gdk_draw_line (window, gc,
1228                      x,              y_start + i * y_increment,
1229                      x + width - 1,  y_start + i * y_increment);
1230     }
1231 #endif
1232   for (i = extra; i < height; i++)
1233     {
1234       gdk_draw_line (window, gc,
1235                      x + (i - extra),              y_start + i * y_increment,
1236                      x + width - (i - extra) - 1,  y_start + i * y_increment);
1237     }
1238
1239
1240   if (area)
1241     gdk_gc_set_clip_rectangle (gc, NULL);
1242 }
1243
1244 static void
1245 draw_harrow (GdkWindow     *window,
1246              GdkGC         *gc,
1247              GtkShadowType  shadow_type,
1248              GdkRectangle  *area,
1249              GtkArrowType   arrow_type,
1250              gint           x,
1251              gint           y,
1252              gint           width,
1253              gint           height)
1254 {
1255   gint steps, extra;
1256   gint x_start, x_increment;
1257   gint i;
1258
1259   if (area)
1260     gdk_gc_set_clip_rectangle (gc, area);
1261
1262   height = height + height % 2 - 1;     /* Force odd */
1263
1264   steps = 1 + height / 2;
1265
1266   extra = width - steps;
1267
1268   if (arrow_type == GTK_ARROW_RIGHT)
1269     {
1270       x_start = x;
1271       x_increment = 1;
1272     }
1273   else
1274     {
1275       x_start = x + width - 1;
1276       x_increment = -1;
1277     }
1278
1279 #if 0
1280   for (i = 0; i < extra; i++)
1281     {
1282       gdk_draw_line (window, gc,
1283                      x_start + i * x_increment, y,
1284                      x_start + i * x_increment, y + height - 1);
1285     }
1286 #endif
1287   for (i = extra; i < width; i++)
1288     {
1289       gdk_draw_line (window, gc,
1290                      x_start + i * x_increment, y + (i - extra),
1291                      x_start + i * x_increment, y + height - (i - extra) - 1);
1292     }
1293
1294
1295   if (area)
1296     gdk_gc_set_clip_rectangle (gc, NULL);
1297 }
1298
1299 /* This function makes up for some brokeness in gtkrange.c
1300  * where we never get the full arrow of the stepper button
1301  * and the type of button in a single drawing function.
1302  *
1303  * It doesn't work correctly when the scrollbar is squished
1304  * to the point we don't have room for full-sized steppers.
1305  */
1306 static void
1307 reverse_engineer_stepper_box (GtkWidget    *range,
1308                               GtkArrowType  arrow_type,
1309                               gint         *x,
1310                               gint         *y,
1311                               gint         *width,
1312                               gint         *height)
1313 {
1314   gint slider_width = 14, stepper_size = 14;
1315   gint box_width;
1316   gint box_height;
1317
1318   if (range)
1319     {
1320       gtk_widget_style_get (range,
1321                             "slider_width", &slider_width,
1322                             "stepper_size", &stepper_size,
1323                             NULL);
1324     }
1325
1326   if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
1327     {
1328       box_width = slider_width;
1329       box_height = stepper_size;
1330     }
1331   else
1332     {
1333       box_width = stepper_size;
1334       box_height = slider_width;
1335     }
1336
1337   *x = *x - (box_width - *width) / 2;
1338   *y = *y - (box_height - *height) / 2;
1339   *width = box_width;
1340   *height = box_height;
1341 }
1342
1343 static void
1344 draw_arrow (GtkStyle      *style,
1345             GdkWindow     *window,
1346             GtkStateType   state,
1347             GtkShadowType  shadow,
1348             GdkRectangle  *area,
1349             GtkWidget     *widget,
1350             const gchar   *detail,
1351             GtkArrowType   arrow_type,
1352             gboolean       fill,
1353             gint           x,
1354             gint           y,
1355             gint           width,
1356             gint           height)
1357 {
1358   const gchar * name;
1359
1360   name = gtk_widget_get_name (widget);
1361
1362   sanitize_size (window, &width, &height);
1363
1364   if (detail && strcmp (detail, "spinbutton") == 0)
1365     {
1366       if (xp_theme_is_drawable(XP_THEME_ELEMENT_SPIN_BUTTON_UP))
1367         {
1368           return;
1369         }
1370       else
1371         {
1372           x += (width - 7) / 2;
1373
1374           if (arrow_type == GTK_ARROW_UP)
1375             y += (height - 4) / 2;
1376           else
1377             y += (1 + height - 4) / 2;
1378           draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
1379                        x, y, 7, 4);
1380         }
1381     }
1382   else if (detail && (!strcmp (detail, "vscrollbar")
1383                       || !strcmp (detail, "hscrollbar")))
1384     {
1385       gint box_x = x;
1386       gint box_y = y;
1387       gint box_width = width;
1388       gint box_height = height;
1389       XpThemeElement xp_arrow;
1390       reverse_engineer_stepper_box (widget, arrow_type,
1391                                     &box_x, &box_y, &box_width, &box_height);
1392
1393       switch (arrow_type)
1394         {
1395         case GTK_ARROW_UP:
1396           xp_arrow = XP_THEME_ELEMENT_ARROW_UP;
1397           break;
1398         case GTK_ARROW_DOWN:
1399           xp_arrow = XP_THEME_ELEMENT_ARROW_DOWN;
1400           break;
1401         case GTK_ARROW_LEFT:
1402           xp_arrow = XP_THEME_ELEMENT_ARROW_LEFT;
1403           break;
1404         default:
1405           xp_arrow = XP_THEME_ELEMENT_ARROW_RIGHT;
1406           break;
1407         }
1408       if (xp_theme_draw(window, xp_arrow, style, box_x, box_y, box_width, box_height, state, area))
1409         {
1410         }
1411       else if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
1412         {
1413           x += (width - 7) / 2;
1414           y += (height - 5) / 2;
1415
1416           draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
1417                        x, y, 7, 5);
1418         }
1419       else
1420         {
1421           y += (height - 7) / 2;
1422           x += (width - 5) / 2;
1423
1424           draw_harrow (window, style->fg_gc[state], shadow, area, arrow_type,
1425                        x, y, 5, 7);
1426         }
1427     }
1428   else
1429     {
1430       /* draw the toolbar chevrons - waiting for GTK 2.4 */
1431           if (name && !strcmp (name, "gtk-toolbar-arrow"))
1432           {
1433                   if (xp_theme_draw(window, XP_THEME_ELEMENT_REBAR_CHEVRON, style, x, y, width, height, state, area))
1434                                 return;
1435           }
1436
1437       if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
1438         {
1439           x += (width - 7) / 2;
1440           y += (height - 5) / 2;
1441
1442           draw_varrow (window, style->fg_gc[state], shadow, area, arrow_type,
1443                        x, y, 7, 5);
1444         }
1445       else
1446         {
1447           x += (width - 5) / 2;
1448           y += (height - 7) / 2;
1449
1450           draw_harrow (window, style->fg_gc[state], shadow, area, arrow_type,
1451                        x, y, 5, 7);
1452         }
1453     }
1454 }
1455
1456 static void
1457 option_menu_get_props (GtkWidget      *widget,
1458                        GtkRequisition *indicator_size,
1459                        GtkBorder      *indicator_spacing)
1460 {
1461   GtkRequisition *tmp_size = NULL;
1462   GtkBorder *tmp_spacing = NULL;
1463
1464   if (widget)
1465     gtk_widget_style_get (widget,
1466                           "indicator_size", &tmp_size,
1467                           "indicator_spacing", &tmp_spacing,
1468                           NULL);
1469
1470   if (tmp_size)
1471     {
1472       *indicator_size = *tmp_size;
1473       g_free (tmp_size);
1474     }
1475   else
1476     *indicator_size = default_option_indicator_size;
1477
1478   if (tmp_spacing)
1479     {
1480       *indicator_spacing = *tmp_spacing;
1481       g_free (tmp_spacing);
1482     }
1483   else
1484     *indicator_spacing = default_option_indicator_spacing;
1485 }
1486
1487 static gboolean is_toolbar_child(GtkWidget * wid)
1488 {
1489         while(wid)
1490         {
1491                 if(GTK_IS_TOOLBAR(wid))
1492                         return TRUE;
1493                 else
1494                         wid = wid->parent;
1495         }
1496
1497         return FALSE;
1498 }
1499
1500 static void
1501 draw_box (GtkStyle      *style,
1502           GdkWindow     *window,
1503           GtkStateType   state_type,
1504           GtkShadowType  shadow_type,
1505           GdkRectangle  *area,
1506           GtkWidget     *widget,
1507           const gchar   *detail,
1508           gint           x,
1509           gint           y,
1510           gint           width,
1511           gint           height)
1512 {
1513   if (detail &&
1514       (!strcmp (detail, "button") ||
1515        !strcmp (detail, "buttondefault")))
1516     {
1517       if (GTK_IS_TREE_VIEW (widget->parent) || GTK_IS_CLIST (widget->parent))
1518         {
1519           if (xp_theme_draw(window, XP_THEME_ELEMENT_LIST_HEADER, style, x, y,
1520                             width, height, state_type, area))
1521             return;
1522         }
1523       else if (is_toolbar_child (widget->parent))
1524       {
1525                     if (xp_theme_draw(window, XP_THEME_ELEMENT_TOOLBAR_BUTTON, style, x, y,
1526                                               width, height, state_type, area))
1527                 return;
1528           }
1529       else
1530         {
1531           gboolean is_default = !strcmp (detail, "buttondefault");
1532           if (xp_theme_draw(window, is_default ? XP_THEME_ELEMENT_DEFAULT_BUTTON
1533                             : XP_THEME_ELEMENT_BUTTON, style, x, y,
1534                             width, height, state_type, area))
1535             return;
1536         }
1537     }
1538   else if (detail && !strcmp (detail, "spinbutton"))
1539     {
1540       if (xp_theme_is_drawable(XP_THEME_ELEMENT_SPIN_BUTTON_UP))
1541         {
1542           return;
1543         }
1544     }
1545   else if (detail && (!strcmp (detail, "spinbutton_up")
1546                       || !strcmp (detail, "spinbutton_down")))
1547     {
1548       if (xp_theme_draw(window,
1549                         (! strcmp (detail, "spinbutton_up"))
1550                         ? XP_THEME_ELEMENT_SPIN_BUTTON_UP
1551                         : XP_THEME_ELEMENT_SPIN_BUTTON_DOWN,
1552                         style, x, y, width, height, state_type, area))
1553         {
1554           return;
1555         }
1556     }
1557
1558   else if (detail && !strcmp (detail, "slider"))
1559     {
1560       if (GTK_IS_SCROLLBAR(widget))
1561         {
1562           GtkScrollbar * scrollbar = GTK_SCROLLBAR(widget);
1563           gboolean is_v = GTK_IS_VSCROLLBAR(widget);
1564           if (xp_theme_draw(window,
1565                             is_v
1566                             ? XP_THEME_ELEMENT_SCROLLBAR_V
1567                             : XP_THEME_ELEMENT_SCROLLBAR_H,
1568                             style, x, y, width, height, state_type, area))
1569             {
1570               XpThemeElement gripper = (is_v ? XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_V : XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_H);
1571
1572               /* Do not display grippers on tiny scroll bars, the limit imposed
1573                  is rather arbitrary, perhaps we can fetch the gripper geometry
1574                  from somewhere and use that... */
1575               if ((gripper == XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_H && width < 16)
1576                   || (gripper == XP_THEME_ELEMENT_SCROLLBAR_GRIPPER_V && height < 16))
1577                 {
1578                   return;
1579                 }
1580
1581               xp_theme_draw(window, gripper, style, x, y, width, height, state_type, area);
1582               return;
1583             }
1584         }
1585     }
1586   else if (detail && !strcmp (detail, "bar"))
1587     {
1588       if (widget && GTK_IS_PROGRESS_BAR (widget))
1589         {
1590           GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(widget);
1591           XpThemeElement xp_progress_bar = map_gtk_progress_bar_to_xp (progress_bar, FALSE);
1592           if (xp_theme_draw (window, xp_progress_bar,
1593                              style, x, y, width, height, state_type, area))
1594             {
1595               return;
1596             }
1597         }
1598     }
1599   else if (detail && !strcmp (detail, "handlebox_bin")) {
1600         if (xp_theme_draw (window, XP_THEME_ELEMENT_REBAR, style, x, y, width, height, state_type, area))
1601           {
1602                 return;
1603           }
1604   }
1605   else if (detail && strcmp (detail, "menuitem") == 0) {
1606     shadow_type = GTK_SHADOW_NONE;
1607       if (xp_theme_draw (window, XP_THEME_ELEMENT_MENU_ITEM, style, x, y, width, height, state_type, area))
1608         {
1609                 return;
1610         }
1611   }
1612   else if (detail && !strcmp (detail, "trough"))
1613     {
1614       if (widget && GTK_IS_PROGRESS_BAR (widget))
1615         {
1616           GtkProgressBar *progress_bar = GTK_PROGRESS_BAR(widget);
1617           XpThemeElement xp_progress_bar = map_gtk_progress_bar_to_xp (progress_bar, TRUE);
1618           if (xp_theme_draw (window, xp_progress_bar,
1619                              style, x, y, width, height, state_type, area))
1620             {
1621               return;
1622             }
1623           else
1624             {
1625               /* Blank in classic Windows */
1626             }
1627         }
1628       else if (widget && GTK_IS_SCROLLBAR(widget))
1629         {
1630           gboolean is_vertical = GTK_IS_VSCROLLBAR(widget);
1631
1632           if (GTK_IS_RANGE(widget)
1633               && xp_theme_draw(window,
1634                                is_vertical
1635                                ? XP_THEME_ELEMENT_TROUGH_V
1636                                : XP_THEME_ELEMENT_TROUGH_H,
1637                                style,
1638                                x, y, width, height, state_type, area))
1639             {
1640               return;
1641             }
1642           else
1643             {
1644               GdkGCValues gc_values;
1645               GdkGC *gc;
1646               GdkPixmap *pixmap;
1647
1648               sanitize_size (window, &width, &height);
1649
1650               pixmap = gdk_pixmap_new (window, 2, 2, -1);
1651
1652               gdk_draw_point (pixmap, style->bg_gc[GTK_STATE_NORMAL], 0, 0);
1653               gdk_draw_point (pixmap, style->bg_gc[GTK_STATE_NORMAL], 1, 1);
1654               gdk_draw_point (pixmap, style->light_gc[GTK_STATE_NORMAL], 1, 0);
1655               gdk_draw_point (pixmap, style->light_gc[GTK_STATE_NORMAL], 0, 1);
1656
1657               gc_values.fill = GDK_TILED;
1658               gc_values.tile = pixmap;
1659               gc_values.ts_x_origin = x;
1660               gc_values.ts_y_origin = y;
1661               gc = gdk_gc_new_with_values (window, &gc_values,
1662                                            GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN | GDK_GC_FILL | GDK_GC_TILE);
1663
1664               if (area)
1665                 gdk_gc_set_clip_rectangle (gc, area);
1666
1667               gdk_draw_rectangle (window, gc, TRUE, x, y, width, height);
1668
1669               gdk_gc_unref (gc);
1670               gdk_pixmap_unref (pixmap);
1671
1672               return;
1673             }
1674         }
1675       else if (widget && GTK_IS_SCALE(widget))
1676         {
1677           gboolean is_vertical = GTK_IS_VSCALE(widget);
1678
1679           parent_class->draw_box (style, window, state_type, GTK_SHADOW_NONE, area,
1680                                   widget, detail, x, y, width, height);
1681
1682           if(is_vertical)
1683             parent_class->draw_box(style, window, state_type, GTK_SHADOW_ETCHED_IN, area, NULL, NULL, (2 * x + width)/2, y, 1, height);
1684           else
1685             parent_class->draw_box(style, window, state_type, GTK_SHADOW_ETCHED_IN, area, NULL, NULL, x, (2 * y + height)/2, width, 1);
1686
1687           return;
1688         }
1689     }
1690   else if (detail && strcmp (detail, "optionmenu") == 0)
1691     {
1692       if (xp_theme_draw(window, XP_THEME_ELEMENT_EDIT_TEXT,
1693                         style, x, y, width, height, state_type, area))
1694         {
1695           return;
1696         }
1697     }
1698   else if (detail && (strcmp (detail, "vscrollbar") == 0 || strcmp (detail, "hscrollbar") == 0))
1699   {
1700           if (shadow_type == GTK_SHADOW_IN)
1701                 shadow_type = GTK_SHADOW_ETCHED_IN;
1702   }
1703   else
1704   {
1705          const gchar * name = gtk_widget_get_name (widget);
1706
1707           if (name && !strcmp (name, "gtk-tooltips")) {
1708          if (xp_theme_draw (window, XP_THEME_ELEMENT_TOOLTIP, style, x, y, width, height, state_type, area))
1709            {
1710                         return;
1711             }
1712                 }
1713   }
1714
1715   parent_class->draw_box (style, window, state_type, shadow_type, area,
1716                           widget, detail, x, y, width, height);
1717
1718   if (detail && strcmp (detail, "optionmenu") == 0)
1719     {
1720       GtkRequisition indicator_size;
1721       GtkBorder indicator_spacing;
1722       gint vline_x;
1723
1724       option_menu_get_props (widget, &indicator_size, &indicator_spacing);
1725
1726       sanitize_size (window, &width, &height);
1727
1728       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1729         vline_x = x + indicator_size.width + indicator_spacing.left + indicator_spacing.right;
1730       else
1731         vline_x = x + width - (indicator_size.width + indicator_spacing.left + indicator_spacing.right) - style->xthickness;
1732
1733       parent_class->draw_vline (style, window, state_type, area, widget,
1734                                 detail,
1735                                 y + style->ythickness + 1,
1736                                 y + height - style->ythickness - 3,
1737                                 vline_x);
1738     }
1739 }
1740
1741 static void
1742 draw_tab (GtkStyle      *style,
1743           GdkWindow     *window,
1744           GtkStateType   state,
1745           GtkShadowType  shadow,
1746           GdkRectangle  *area,
1747           GtkWidget     *widget,
1748           const gchar   *detail,
1749           gint           x,
1750           gint           y,
1751           gint           width,
1752           gint           height)
1753 {
1754   GtkRequisition indicator_size;
1755   GtkBorder indicator_spacing;
1756
1757   gint arrow_height;
1758
1759   g_return_if_fail (style != NULL);
1760   g_return_if_fail (window != NULL);
1761
1762   if (detail && ! strcmp (detail, "optionmenutab"))
1763     {
1764       if (xp_theme_draw(window, XP_THEME_ELEMENT_COMBOBUTTON,
1765                         style, x-5, widget->allocation.y+1,
1766                         width+10, widget->allocation.height-2, state, area))
1767         {
1768           return;
1769         }
1770     }
1771
1772   if (widget)
1773     gtk_widget_style_get (widget, "indicator_size", &indicator_size, NULL);
1774
1775   option_menu_get_props (widget, &indicator_size, &indicator_spacing);
1776
1777   x += (width - indicator_size.width) / 2;
1778   arrow_height = (indicator_size.width + 1) / 2;
1779
1780   y += (height - arrow_height) / 2;
1781
1782   draw_varrow (window, style->black_gc, shadow, area, GTK_ARROW_DOWN,
1783                x, y, indicator_size.width, arrow_height);
1784 }
1785
1786 /* this is an undefined magic value that, according to the mozilla folks,
1787    worked for all the various themes that they tried */
1788 #define XP_EDGE_SIZE 2
1789
1790 static void
1791 draw_extension(GtkStyle *style,
1792                GdkWindow *window,
1793                GtkStateType state_type,
1794                GtkShadowType shadow_type,
1795                GdkRectangle *area,
1796                GtkWidget *widget,
1797                const gchar *detail,
1798                gint x,
1799                gint y,
1800                gint width,
1801                gint height,
1802                GtkPositionType gap_side)
1803 {
1804   if (detail && !strcmp(detail, "tab"))
1805     {
1806       GtkNotebook *notebook = GTK_NOTEBOOK(widget);
1807       GtkPositionType pos_type = gtk_notebook_get_tab_pos(notebook);
1808
1809       if (pos_type == GTK_POS_TOP && state_type == GTK_STATE_NORMAL)
1810           height += XP_EDGE_SIZE;
1811
1812 #if 0
1813         /* FIXME: pos != TOP to be implemented */
1814       else if (pos_type == GTK_POS_BOTTOM)
1815         y -= XP_EDGE_SIZE;
1816       else if (pos_type == GTK_POS_RIGHT)
1817         width += XP_EDGE_SIZE;
1818       else if (pos_type == GTK_POS_LEFT)
1819         height -= XP_EDGE_SIZE;
1820 #endif
1821
1822       if (pos_type == GTK_POS_TOP
1823           && xp_theme_draw
1824           (window, gtk_notebook_get_current_page(notebook)==0
1825            ? XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE
1826            : XP_THEME_ELEMENT_TAB_ITEM,
1827            style, x, y, width, height, state_type, area))
1828         {
1829           return;
1830         }
1831     }
1832   parent_class->draw_extension
1833     (style, window, state_type, shadow_type, area, widget, detail,
1834          x, y, width, height, gap_side);
1835 }
1836
1837 static void
1838 draw_box_gap (GtkStyle *style, GdkWindow *window, GtkStateType state_type,
1839               GtkShadowType shadow_type, GdkRectangle *area,
1840               GtkWidget *widget, const gchar *detail, gint x,
1841               gint y, gint width, gint height, GtkPositionType gap_side,
1842               gint gap_x, gint gap_width)
1843 {
1844   if (detail && !strcmp(detail, "notebook"))
1845     {
1846       GtkNotebook *notebook = GTK_NOTEBOOK(widget);
1847
1848       /* FIXME: pos != TOP to be implemented */
1849       if (gtk_notebook_get_tab_pos(notebook) == GTK_POS_TOP && xp_theme_draw(window, XP_THEME_ELEMENT_TAB_PANE, style,  x, y, width, height,
1850                         state_type, area))
1851         {
1852           return;
1853         }
1854     }
1855   parent_class->draw_box_gap(style, window, state_type, shadow_type,
1856                              area, widget, detail, x, y, width, height,
1857                              gap_side, gap_x, gap_width);
1858 }
1859
1860 static void
1861 draw_flat_box (GtkStyle *style, GdkWindow *window,
1862                GtkStateType state_type, GtkShadowType shadow_type,
1863                GdkRectangle *area, GtkWidget *widget,
1864                const gchar *detail, gint x, gint y,
1865                gint width, gint height)
1866 {
1867   if (detail && ! strcmp (detail, "checkbutton"))
1868     {
1869       if (state_type == GTK_STATE_PRELIGHT)
1870         {
1871           return;
1872         }
1873     }
1874
1875   parent_class->draw_flat_box(style, window, state_type, shadow_type,
1876                               area, widget, detail, x, y, width, height);
1877 }
1878
1879 static void
1880 draw_shadow (GtkStyle      *style,
1881              GdkWindow     *window,
1882              GtkStateType   state_type,
1883              GtkShadowType  shadow_type,
1884              GdkRectangle  *area,
1885              GtkWidget     *widget,
1886              const gchar   *detail,
1887              gint           x,
1888              gint           y,
1889              gint           width,
1890              gint           height)
1891 {
1892   if(detail && ! strcmp(detail, "entry"))
1893     {
1894       if (xp_theme_draw(window, XP_THEME_ELEMENT_EDIT_TEXT, style,
1895                         x, y, width, height, state_type, area))
1896         {
1897           return;
1898         }
1899     }
1900   parent_class->draw_shadow (style, window, state_type, shadow_type, area, widget,
1901                              detail, x, y, width, height);
1902 }
1903
1904 static void
1905 draw_hline (GtkStyle            *style,
1906             GdkWindow           *window,
1907             GtkStateType         state_type,
1908             GdkRectangle        *area,
1909             GtkWidget           *widget,
1910             const gchar         *detail,
1911             gint                 x1,
1912             gint                 x2,
1913             gint                 y)
1914 {
1915
1916   if (detail && !strcmp(detail, "menuitem")) {
1917           if (xp_theme_draw(window, XP_THEME_ELEMENT_MENU_SEPARATOR, style,
1918                         x1, y, x2, style->ythickness, state_type, area)) {
1919                         return;
1920           }
1921   }
1922
1923   parent_class->draw_hline (style, window, state_type, area, widget,
1924                             detail, x1, x2, y);
1925 }
1926
1927 static void
1928 draw_vline (GtkStyle            *style,
1929             GdkWindow           *window,
1930             GtkStateType         state_type,
1931             GdkRectangle        *area,
1932             GtkWidget           *widget,
1933             const gchar         *detail,
1934             gint                 y1,
1935             gint                 y2,
1936             gint                 x)
1937 {
1938   parent_class->draw_vline (style, window, state_type, area, widget,
1939                             detail, y1, y2, x);
1940 }
1941
1942 static void
1943 draw_resize_grip (GtkStyle      *style,
1944                        GdkWindow     *window,
1945                        GtkStateType   state_type,
1946                        GdkRectangle  *area,
1947                        GtkWidget     *widget,
1948                        const gchar   *detail,
1949                        GdkWindowEdge  edge,
1950                        gint           x,
1951                        gint           y,
1952                        gint           width,
1953                        gint           height)
1954 {
1955         if (detail && !strcmp(detail, "statusbar")) {
1956                 if (!xp_theme_draw(window, XP_THEME_ELEMENT_STATUS_GRIPPER, style, x, y, width, height,
1957                            state_type, area))
1958                         return;
1959         }
1960
1961         parent_class->draw_resize_grip (style, window, state_type, area,
1962                                                                         widget, detail, edge, x, y, width, height);
1963 }
1964
1965 static void
1966 draw_handle (GtkStyle        *style,
1967              GdkWindow       *window,
1968              GtkStateType     state_type,
1969              GtkShadowType    shadow_type,
1970              GdkRectangle    *area,
1971              GtkWidget       *widget,
1972              const gchar     *detail,
1973              gint             x,
1974              gint             y,
1975              gint             width,
1976              gint             height,
1977              GtkOrientation   orientation)
1978 {
1979   if (! GTK_IS_HANDLE_BOX (widget))
1980     {
1981       XpThemeElement hndl;
1982
1983       sanitize_size (window, &width, &height);
1984
1985       if (orientation == GTK_ORIENTATION_VERTICAL)
1986         hndl = XP_THEME_ELEMENT_REBAR_GRIPPER_V;
1987       else
1988         hndl = XP_THEME_ELEMENT_REBAR_GRIPPER_H;
1989
1990       if (xp_theme_draw (window, hndl, style, x, y, width, height,
1991                          state_type, area))
1992         {
1993           return;
1994         }
1995       /* grippers are just flat boxes when they're not a toolbar */
1996       parent_class->draw_box (style, window, state_type, shadow_type,
1997                               area, widget, detail, x, y, width, height);
1998     }
1999   else
2000     {
2001       /* TODO: Draw handle boxes as double lines: || */
2002       parent_class->draw_handle (style, window, state_type, shadow_type,
2003                                  area, widget, detail, x, y, width, height,
2004                                  orientation);
2005     }
2006 }
2007
2008 static void
2009 msw_style_init_from_rc (GtkStyle * style, GtkRcStyle * rc_style)
2010 {
2011   setup_system_font (style);
2012   setup_menu_settings ();
2013   setup_system_styles (style);
2014   parent_class->init_from_rc(style, rc_style);
2015 }
2016
2017 static void
2018 msw_style_class_init (MswStyleClass *klass)
2019 {
2020   GtkStyleClass *style_class = GTK_STYLE_CLASS (klass);
2021
2022   parent_class = g_type_class_peek_parent (klass);
2023
2024   style_class->init_from_rc = msw_style_init_from_rc;
2025   style_class->draw_arrow = draw_arrow;
2026   style_class->draw_box = draw_box;
2027   style_class->draw_check = draw_check;
2028   style_class->draw_option = draw_option;
2029   style_class->draw_tab = draw_tab;
2030   style_class->draw_flat_box = draw_flat_box;
2031   style_class->draw_expander = draw_expander;
2032   style_class->draw_extension = draw_extension;
2033   style_class->draw_box_gap = draw_box_gap;
2034   style_class->draw_shadow = draw_shadow;
2035   style_class->draw_hline = draw_hline;
2036   style_class->draw_vline = draw_vline;
2037   style_class->draw_handle = draw_handle;
2038   style_class->draw_resize_grip = draw_resize_grip;
2039 }
2040
2041 GType msw_type_style = 0;
2042
2043 void
2044 msw_style_register_type (GTypeModule *module)
2045 {
2046   static const GTypeInfo object_info =
2047   {
2048     sizeof (MswStyleClass),
2049     (GBaseInitFunc) NULL,
2050     (GBaseFinalizeFunc) NULL,
2051     (GClassInitFunc) msw_style_class_init,
2052     NULL,           /* class_finalize */
2053     NULL,           /* class_data */
2054     sizeof (MswStyle),
2055     0,              /* n_preallocs */
2056     (GInstanceInitFunc) NULL,
2057   };
2058
2059   msw_type_style = g_type_module_register_type (module,
2060                                                    GTK_TYPE_STYLE,
2061                                                    "MswStyle",
2062                                                    &object_info, 0);
2063 }
2064
2065 void
2066 msw_style_init (void)
2067 {
2068   xp_theme_init ();
2069   msw_style_setup_system_settings ();
2070   setup_msw_rc_style ();
2071 }