prepare for alpha1 release.
[mikachu/openbox.git] / render / theme.c
1 #include "render.h"
2 #include "color.h"
3 #include "font.h"
4 #include "mask.h"
5 #include "theme.h"
6
7 #include <X11/Xlib.h>
8 #include <X11/Xresource.h>
9
10 static XrmDatabase loaddb(RrTheme *theme, char *name);
11 static gboolean read_int(XrmDatabase db, char *rname, int *value);
12 static gboolean read_string(XrmDatabase db, char *rname, char **value);
13 static gboolean read_color(XrmDatabase db, const RrInstance *inst,
14                            gchar *rname, RrColor **value);
15 static gboolean read_mask(const RrInstance *inst,
16                           gchar *maskname, RrTheme *theme,
17                           RrPixmapMask **value);
18 static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
19                                 gchar *rname, RrAppearance *value);
20 static void set_default_appearance(RrAppearance *a);
21
22 RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
23 {
24     XrmDatabase db = NULL;
25     RrJustify winjust, mtitlejust, mjust;
26     gchar *str;
27     gchar *font_str;
28     RrTheme *theme;
29
30     theme = g_new0(RrTheme, 1);
31
32     theme->inst = inst;
33
34     theme->b_color = theme->cb_unfocused_color = theme->cb_focused_color = 
35         theme->title_unfocused_color = theme->title_focused_color = 
36         theme->titlebut_unfocused_color = theme->titlebut_focused_color = 
37         theme->menu_color = theme->menu_title_color =
38         theme->menu_disabled_color = theme->menu_hilite_color = NULL;
39     theme->winfont = theme->mtitlefont = theme->mfont = NULL;
40     theme->title_layout = NULL;
41     theme->max_set_mask = theme->max_unset_mask = NULL;
42     theme->desk_set_mask = theme->desk_unset_mask = NULL;
43     theme->shade_set_mask = theme->shade_unset_mask = NULL;
44     theme->iconify_mask = theme->close_mask = NULL;
45
46     theme->a_focused_unpressed_max = RrAppearanceNew(inst, 1);
47     theme->a_focused_pressed_max = RrAppearanceNew(inst, 1);
48     theme->a_focused_pressed_set_max = RrAppearanceNew(inst, 1);
49     theme->a_unfocused_unpressed_max = RrAppearanceNew(inst, 1);
50     theme->a_unfocused_pressed_max = RrAppearanceNew(inst, 1);
51     theme->a_unfocused_pressed_set_max = RrAppearanceNew(inst, 1);
52     theme->a_focused_unpressed_close = NULL;
53     theme->a_focused_pressed_close = NULL;
54     theme->a_unfocused_unpressed_close = NULL;
55     theme->a_unfocused_pressed_close = NULL;
56     theme->a_focused_unpressed_desk = NULL;
57     theme->a_focused_pressed_desk = NULL;
58     theme->a_focused_pressed_set_desk = NULL;
59     theme->a_unfocused_unpressed_desk = NULL;
60     theme->a_unfocused_pressed_desk = NULL;
61     theme->a_unfocused_pressed_set_desk = NULL;
62     theme->a_focused_unpressed_shade = NULL;
63     theme->a_focused_pressed_shade = NULL;
64     theme->a_focused_pressed_set_shade = NULL;
65     theme->a_unfocused_unpressed_shade = NULL;
66     theme->a_unfocused_pressed_shade = NULL;
67     theme->a_unfocused_pressed_set_shade = NULL;
68     theme->a_focused_unpressed_iconify = NULL;
69     theme->a_focused_pressed_iconify = NULL;
70     theme->a_unfocused_unpressed_iconify = NULL;
71     theme->a_unfocused_pressed_iconify = NULL;
72     theme->a_focused_grip = RrAppearanceNew(inst, 0);
73     theme->a_unfocused_grip = RrAppearanceNew(inst, 0);
74     theme->a_focused_title = RrAppearanceNew(inst, 0);
75     theme->a_unfocused_title = RrAppearanceNew(inst, 0);
76     theme->a_focused_label = RrAppearanceNew(inst, 1);
77     theme->a_unfocused_label = RrAppearanceNew(inst, 1);
78     theme->a_icon = RrAppearanceNew(inst, 1);
79     theme->a_focused_handle = RrAppearanceNew(inst, 0);
80     theme->a_unfocused_handle = RrAppearanceNew(inst, 0);
81     theme->a_menu = RrAppearanceNew(inst, 0);
82     theme->a_menu_title = RrAppearanceNew(inst, 1);
83     theme->a_menu_item = RrAppearanceNew(inst, 1);
84     theme->a_menu_disabled = RrAppearanceNew(inst, 1);
85     theme->a_menu_hilite = RrAppearanceNew(inst, 1);
86
87     theme->app_hilite_bg = RrAppearanceNew(inst, 0);
88     theme->app_unhilite_bg = RrAppearanceNew(inst, 0);
89     theme->app_hilite_label = RrAppearanceNew(inst, 1);
90     theme->app_unhilite_label = RrAppearanceNew(inst, 1);
91     theme->app_icon = RrAppearanceNew(inst, 1);
92
93     if (name) {
94         db = loaddb(theme, name);
95         if (db == NULL) {
96             g_warning("Failed to load the theme '%s'", name);
97             g_message("Falling back to the default: '%s'", DEFAULT_THEME);
98         } else
99             theme->name = g_path_get_basename(name);
100     }
101     if (db == NULL) {
102         db = loaddb(theme, DEFAULT_THEME);
103         if (db == NULL) {
104             g_warning("Failed to load the theme '%s'.", DEFAULT_THEME);
105             return NULL;
106         } else
107             theme->name = g_path_get_basename(DEFAULT_THEME);
108     }
109
110     /* load the font stuff */
111     if (!read_string(db, "window.title.xftfont", &font_str))
112         font_str = "arial,sans:bold:pixelsize=10:shadow=y:shadowtint=50";
113
114     if (!(theme->winfont = RrFontOpen(inst, font_str))) {
115         RrThemeFree(theme);
116         return NULL;
117     }
118     theme->winfont_height = RrFontHeight(theme->winfont);
119
120     winjust = RR_JUSTIFY_LEFT;
121     if (read_string(db, "window.justify", &str)) {
122         if (!g_ascii_strcasecmp(str, "right"))
123             winjust = RR_JUSTIFY_RIGHT;
124         else if (!g_ascii_strcasecmp(str, "center"))
125             winjust = RR_JUSTIFY_CENTER;
126     }
127
128     if (!read_string(db, "menu.title.xftfont", &font_str))
129         font_str = "arial,sans:bold:pixelsize=12:shadow=y";
130
131     if (!(theme->mtitlefont = RrFontOpen(inst, font_str))) {
132         RrThemeFree(theme);
133         return NULL;
134     }
135     theme->mtitlefont_height = RrFontHeight(theme->mtitlefont);
136
137     mtitlejust = RR_JUSTIFY_LEFT;
138     if (read_string(db, "menu.title.justify", &str)) {
139         if (!g_ascii_strcasecmp(str, "right"))
140             mtitlejust = RR_JUSTIFY_RIGHT;
141         else if (!g_ascii_strcasecmp(str, "center"))
142             mtitlejust = RR_JUSTIFY_CENTER;
143     }
144
145     if (!read_string(db, "menu.frame.xftfont", &font_str))
146         font_str = "arial,sans:bold:pixelsize=11:shadow=y";
147
148     if (!(theme->mfont = RrFontOpen(inst, font_str))) {
149         RrThemeFree(theme);
150         return NULL;
151     }
152     theme->mfont_height = RrFontHeight(theme->mfont);
153
154     mjust = RR_JUSTIFY_LEFT;
155     if (read_string(db, "menu.frame.justify", &str)) {
156         if (!g_ascii_strcasecmp(str, "right"))
157             mjust = RR_JUSTIFY_RIGHT;
158         else if (!g_ascii_strcasecmp(str, "center"))
159             mjust = RR_JUSTIFY_CENTER;
160     }
161
162     /* load the title layout */
163     if (!read_string(db, "window.title.layout", &font_str))
164         font_str = "NLIMC";
165     theme->title_layout = g_strdup(font_str);
166
167     /* load direct dimensions */
168     if (!read_int(db, "handleWidth", &theme->handle_height) ||
169         theme->handle_height < 0 || theme->handle_height > 100)
170         theme->handle_height = 6;
171     if (!read_int(db, "bevelWidth", &theme->bevel) ||
172         theme->bevel <= 0 || theme->bevel > 100) theme->bevel = 3;
173     if (!read_int(db, "borderWidth", &theme->bwidth) ||
174         theme->bwidth < 0 || theme->bwidth > 100) theme->bwidth = 1;
175     if (!read_int(db, "frameWidth", &theme->cbwidth) ||
176         theme->cbwidth < 0 || theme->cbwidth > 100)
177         theme->cbwidth = theme->bevel;
178
179     /* load colors */
180     if (!read_color(db, inst,
181                     "borderColor", &theme->b_color))
182         theme->b_color = RrColorNew(inst, 0, 0, 0);
183     if (!read_color(db, inst,
184                     "window.frame.focusColor", &theme->cb_focused_color))
185         theme->cb_focused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
186     if (!read_color(db, inst,
187                     "window.frame.unfocusColor",&theme->cb_unfocused_color))
188         theme->cb_unfocused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
189     if (!read_color(db, inst,
190                     "window.label.focus.textColor",
191                     &theme->title_focused_color))
192         theme->title_focused_color = RrColorNew(inst, 0x0, 0x0, 0x0);
193     if (!read_color(db, inst,
194                     "window.label.unfocus.textColor",
195                     &theme->title_unfocused_color))
196         theme->title_unfocused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
197     if (!read_color(db, inst,
198                     "window.button.focus.picColor",
199                     &theme->titlebut_focused_color))
200         theme->titlebut_focused_color = RrColorNew(inst, 0, 0, 0);
201     if (!read_color(db, inst,
202                     "window.button.unfocus.picColor",
203                     &theme->titlebut_unfocused_color))
204         theme->titlebut_unfocused_color = RrColorNew(inst, 0xff, 0xff, 0xff);
205     if (!read_color(db, inst,
206                     "menu.title.textColor", &theme->menu_title_color))
207         theme->menu_title_color = RrColorNew(inst, 0, 0, 0);
208     if (!read_color(db, inst,
209                     "menu.frame.textColor", &theme->menu_color))
210         theme->menu_color = RrColorNew(inst, 0xff, 0xff, 0xff);
211     if (!read_color(db, inst,
212                     "menu.frame.disableColor", &theme->menu_disabled_color))
213         theme->menu_disabled_color = RrColorNew(inst, 0, 0, 0);
214     if (!read_color(db, inst,
215                     "menu.hilite.textColor", &theme->menu_hilite_color))
216         theme->menu_hilite_color = RrColorNew(inst, 0, 0, 0);
217
218     if (read_mask(inst, "max.xbm", theme, &theme->max_unset_mask)){
219         if (!read_mask(inst, "max_t.xbm", theme, &theme->max_set_mask)) {
220             theme->max_set_mask = RrPixmapMaskCopy(theme->max_unset_mask);
221         }
222     } else {
223         {
224             char data[] = { 0x7f, 0x7f, 0x7f, 0x41, 0x41, 0x41, 0x7f };
225             theme->max_unset_mask = RrPixmapMaskNew(inst, 7, 7, data);
226         }
227         {
228             char data[] = { 0x7c, 0x44, 0x47, 0x47, 0x7f, 0x1f, 0x1f };
229             theme->max_set_mask = RrPixmapMaskNew(inst, 7, 7, data);
230         }
231     }
232
233     if (!read_mask(inst, "iconify.xbm", theme, &theme->iconify_mask)) {
234         char data[] = { 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f };
235         theme->iconify_mask = RrPixmapMaskNew(inst, 7, 7, data);
236     }
237
238     if (read_mask(inst, "stick.xbm", theme, &theme->desk_unset_mask)) {
239         if (!read_mask(inst, "stick_t.xbm", theme, &theme->desk_set_mask)) {
240             theme->desk_set_mask =
241                 RrPixmapMaskCopy(theme->desk_unset_mask);
242         }
243     } else {
244         {
245             char data[] = { 0x63, 0x63, 0x00, 0x00, 0x00, 0x63, 0x63 };
246             theme->desk_unset_mask = RrPixmapMaskNew(inst, 7, 7, data);
247         }
248         {
249             char data[] = { 0x00, 0x36, 0x36, 0x08, 0x36, 0x36, 0x00 };
250             theme->desk_set_mask = RrPixmapMaskNew(inst, 7, 7, data);
251         }
252     }
253
254     if (read_mask(inst, "shade.xbm", theme, &theme->shade_unset_mask)) {
255         if (!read_mask(inst, "shade_t.xbm", theme, &theme->shade_set_mask)) {
256             theme->shade_set_mask =
257                 RrPixmapMaskCopy(theme->shade_unset_mask);
258         }
259     } else {
260         {
261             char data[] = { 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00 };
262             theme->shade_unset_mask = RrPixmapMaskNew(inst, 7, 7, data);
263         }
264         {
265             char data[] = { 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x7f };
266             theme->shade_set_mask = RrPixmapMaskNew(inst, 7, 7, data);
267         }
268     }
269
270     if (!read_mask(inst, "close.xbm", theme, &theme->close_mask)) {
271         char data[] = { 0x63, 0x77, 0x3e, 0x1c, 0x3e, 0x77, 0x63 };
272         theme->close_mask = RrPixmapMaskNew(inst, 7, 7, data);
273     }        
274
275     /* read the decoration textures */
276     if (!read_appearance(db, inst,
277                          "window.title.focus", theme->a_focused_title))
278         set_default_appearance(theme->a_focused_title);
279     if (!read_appearance(db, inst,
280                          "window.title.unfocus", theme->a_unfocused_title))
281         set_default_appearance(theme->a_unfocused_title);
282     if (!read_appearance(db, inst,
283                          "window.label.focus", theme->a_focused_label))
284         set_default_appearance(theme->a_focused_label);
285     if (!read_appearance(db, inst,
286                          "window.label.unfocus", theme->a_unfocused_label))
287         set_default_appearance(theme->a_unfocused_label);
288     if (!read_appearance(db, inst,
289                          "window.handle.focus", theme->a_focused_handle))
290         set_default_appearance(theme->a_focused_handle);
291     if (!read_appearance(db, inst,
292                          "window.handle.unfocus",theme->a_unfocused_handle))
293         set_default_appearance(theme->a_unfocused_handle);
294     if (!read_appearance(db, inst,
295                          "window.grip.focus", theme->a_focused_grip))
296         set_default_appearance(theme->a_focused_grip);
297     if (!read_appearance(db, inst,
298                          "window.grip.unfocus", theme->a_unfocused_grip))
299         set_default_appearance(theme->a_unfocused_grip);
300     if (!read_appearance(db, inst,
301                          "menu.frame", theme->a_menu))
302         set_default_appearance(theme->a_menu);
303     if (!read_appearance(db, inst,
304                          "menu.title", theme->a_menu_title))
305         set_default_appearance(theme->a_menu_title);
306     if (!read_appearance(db, inst,
307                          "menu.hilite", theme->a_menu_hilite))
308         set_default_appearance(theme->a_menu_hilite);
309
310     /* read the appearances for rendering non-decorations */
311     if (!read_appearance(db, inst,
312                          "window.title.focus", theme->app_hilite_bg))
313         set_default_appearance(theme->app_hilite_bg);
314     if (!read_appearance(db, inst,
315                          "window.label.focus", theme->app_hilite_label))
316         set_default_appearance(theme->app_hilite_label);
317     if (!read_appearance(db, inst,
318                          "window.title.unfocus", theme->app_unhilite_bg))
319         set_default_appearance(theme->app_unhilite_bg);
320     if (!read_appearance(db, inst,
321                          "window.label.unfocus", theme->app_unhilite_label))
322         set_default_appearance(theme->app_unhilite_label);
323
324     /* read buttons textures */
325     if (!read_appearance(db, inst,
326                          "window.button.pressed.focus",
327                          theme->a_focused_pressed_max))
328         if (!read_appearance(db, inst,
329                              "window.button.pressed",
330                              theme->a_focused_pressed_max))
331             set_default_appearance(theme->a_focused_pressed_max);
332     if (!read_appearance(db, inst,
333                          "window.button.pressed.unfocus",
334                          theme->a_unfocused_pressed_max))
335         if (!read_appearance(db, inst,
336                              "window.button.pressed",
337                              theme->a_unfocused_pressed_max))
338             set_default_appearance(theme->a_unfocused_pressed_max);
339     if (!read_appearance(db, inst,
340                          "window.button.focus",
341                          theme->a_focused_unpressed_max))
342         set_default_appearance(theme->a_focused_unpressed_max);
343     if (!read_appearance(db, inst,
344                          "window.button.unfocus",
345                          theme->a_unfocused_unpressed_max))
346         set_default_appearance(theme->a_unfocused_unpressed_max);
347
348     theme->a_unfocused_unpressed_close =
349         RrAppearanceCopy(theme->a_unfocused_unpressed_max);
350     theme->a_unfocused_pressed_close =
351         RrAppearanceCopy(theme->a_unfocused_pressed_max);
352     theme->a_focused_unpressed_close =
353         RrAppearanceCopy(theme->a_focused_unpressed_max);
354     theme->a_focused_pressed_close =
355         RrAppearanceCopy(theme->a_focused_pressed_max);
356     theme->a_unfocused_unpressed_desk =
357         RrAppearanceCopy(theme->a_unfocused_unpressed_max);
358     theme->a_unfocused_pressed_desk =
359         RrAppearanceCopy(theme->a_unfocused_pressed_max);
360     theme->a_unfocused_pressed_set_desk =
361         RrAppearanceCopy(theme->a_unfocused_pressed_max);
362     theme->a_focused_unpressed_desk =
363         RrAppearanceCopy(theme->a_focused_unpressed_max);
364     theme->a_focused_pressed_desk =
365         RrAppearanceCopy(theme->a_focused_pressed_max);
366     theme->a_focused_pressed_set_desk =
367         RrAppearanceCopy(theme->a_focused_pressed_max);
368     theme->a_unfocused_unpressed_shade =
369         RrAppearanceCopy(theme->a_unfocused_unpressed_max);
370     theme->a_unfocused_pressed_shade =
371         RrAppearanceCopy(theme->a_unfocused_pressed_max);
372     theme->a_unfocused_pressed_set_shade =
373         RrAppearanceCopy(theme->a_unfocused_pressed_max);
374     theme->a_focused_unpressed_shade =
375         RrAppearanceCopy(theme->a_focused_unpressed_max);
376     theme->a_focused_pressed_shade =
377         RrAppearanceCopy(theme->a_focused_pressed_max);
378     theme->a_focused_pressed_set_shade =
379         RrAppearanceCopy(theme->a_focused_pressed_max);
380     theme->a_unfocused_unpressed_iconify =
381         RrAppearanceCopy(theme->a_unfocused_unpressed_max);
382     theme->a_unfocused_pressed_iconify =
383         RrAppearanceCopy(theme->a_unfocused_pressed_max);
384     theme->a_focused_unpressed_iconify =
385         RrAppearanceCopy(theme->a_focused_unpressed_max);
386     theme->a_focused_pressed_iconify =
387         RrAppearanceCopy(theme->a_focused_pressed_max);
388     theme->a_unfocused_pressed_set_max =
389         RrAppearanceCopy(theme->a_unfocused_pressed_max);
390     theme->a_focused_pressed_set_max =
391         RrAppearanceCopy(theme->a_focused_pressed_max);
392
393     theme->a_icon->surface.grad = RR_SURFACE_PARENTREL;
394
395     /* set up the textures */
396     theme->a_focused_label->texture[0].type = 
397         theme->app_hilite_label->texture[0].type = RR_TEXTURE_TEXT;
398     theme->a_focused_label->texture[0].data.text.justify = winjust;
399     theme->app_hilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
400     theme->a_focused_label->texture[0].data.text.font =
401         theme->app_hilite_label->texture[0].data.text.font = theme->winfont;
402     theme->a_focused_label->texture[0].data.text.color =
403         theme->app_hilite_label->texture[0].data.text.color =
404         theme->title_focused_color;
405
406     theme->a_unfocused_label->texture[0].type =
407         theme->app_unhilite_label->texture[0].type = RR_TEXTURE_TEXT;
408     theme->a_unfocused_label->texture[0].data.text.justify = winjust;
409     theme->app_unhilite_label->texture[0].data.text.justify = RR_JUSTIFY_LEFT;
410     theme->a_unfocused_label->texture[0].data.text.font =
411         theme->app_unhilite_label->texture[0].data.text.font = theme->winfont;
412     theme->a_unfocused_label->texture[0].data.text.color =
413         theme->app_unhilite_label->texture[0].data.text.color =
414         theme->title_unfocused_color;
415
416     theme->a_menu_title->texture[0].type = RR_TEXTURE_TEXT;
417     theme->a_menu_title->texture[0].data.text.justify = mtitlejust;
418     theme->a_menu_title->texture[0].data.text.font = theme->mtitlefont;
419     theme->a_menu_title->texture[0].data.text.color = theme->menu_title_color;
420
421     theme->a_menu_item->surface.grad = 
422         theme->a_menu_disabled->surface.grad =
423         theme->app_icon->surface.grad = RR_SURFACE_PARENTREL;
424
425     theme->a_menu_item->texture[0].type =
426         theme->a_menu_disabled->texture[0].type = 
427         theme->a_menu_hilite->texture[0].type = RR_TEXTURE_TEXT;
428     theme->a_menu_item->texture[0].data.text.justify = 
429         theme->a_menu_disabled->texture[0].data.text.justify = 
430         theme->a_menu_hilite->texture[0].data.text.justify = mjust;
431     theme->a_menu_item->texture[0].data.text.font =
432         theme->a_menu_disabled->texture[0].data.text.font =
433         theme->a_menu_hilite->texture[0].data.text.font = theme->mfont;
434     theme->a_menu_item->texture[0].data.text.color = theme->menu_color;
435     theme->a_menu_disabled->texture[0].data.text.color =
436         theme->menu_disabled_color;
437     theme->a_menu_hilite->texture[0].data.text.color =
438         theme->menu_hilite_color;
439
440     theme->a_focused_unpressed_max->texture[0].type = 
441         theme->a_focused_pressed_max->texture[0].type = 
442         theme->a_focused_pressed_set_max->texture[0].type =  
443         theme->a_unfocused_unpressed_max->texture[0].type = 
444         theme->a_unfocused_pressed_max->texture[0].type = 
445         theme->a_unfocused_pressed_set_max->texture[0].type = 
446         theme->a_focused_unpressed_close->texture[0].type = 
447         theme->a_focused_pressed_close->texture[0].type = 
448         theme->a_unfocused_unpressed_close->texture[0].type = 
449         theme->a_unfocused_pressed_close->texture[0].type = 
450         theme->a_focused_unpressed_desk->texture[0].type = 
451         theme->a_focused_pressed_desk->texture[0].type = 
452         theme->a_focused_pressed_set_desk->texture[0].type = 
453         theme->a_unfocused_unpressed_desk->texture[0].type = 
454         theme->a_unfocused_pressed_desk->texture[0].type = 
455         theme->a_unfocused_pressed_set_desk->texture[0].type = 
456         theme->a_focused_unpressed_shade->texture[0].type = 
457         theme->a_focused_pressed_shade->texture[0].type = 
458         theme->a_focused_pressed_set_shade->texture[0].type = 
459         theme->a_unfocused_unpressed_shade->texture[0].type = 
460         theme->a_unfocused_pressed_shade->texture[0].type = 
461         theme->a_unfocused_pressed_set_shade->texture[0].type = 
462         theme->a_focused_unpressed_iconify->texture[0].type = 
463         theme->a_focused_pressed_iconify->texture[0].type = 
464         theme->a_unfocused_unpressed_iconify->texture[0].type = 
465         theme->a_unfocused_pressed_iconify->texture[0].type = RR_TEXTURE_MASK;
466     theme->a_focused_unpressed_max->texture[0].data.mask.mask = 
467         theme->a_unfocused_unpressed_max->texture[0].data.mask.mask = 
468         theme->a_focused_pressed_max->texture[0].data.mask.mask = 
469         theme->a_unfocused_pressed_max->texture[0].data.mask.mask =
470         theme->max_unset_mask;
471     theme->a_focused_pressed_set_max->texture[0].data.mask.mask = 
472         theme->a_unfocused_pressed_set_max->texture[0].data.mask.mask =
473         theme->max_set_mask;
474     theme->a_focused_pressed_close->texture[0].data.mask.mask = 
475         theme->a_unfocused_pressed_close->texture[0].data.mask.mask =
476         theme->a_focused_unpressed_close->texture[0].data.mask.mask = 
477         theme->a_unfocused_unpressed_close->texture[0].data.mask.mask =
478         theme->close_mask;
479     theme->a_focused_unpressed_desk->texture[0].data.mask.mask = 
480         theme->a_unfocused_unpressed_desk->texture[0].data.mask.mask = 
481         theme->a_focused_pressed_desk->texture[0].data.mask.mask = 
482         theme->a_unfocused_pressed_desk->texture[0].data.mask.mask =
483         theme->desk_unset_mask;
484     theme->a_focused_pressed_set_desk->texture[0].data.mask.mask = 
485         theme->a_unfocused_pressed_set_desk->texture[0].data.mask.mask =
486         theme->desk_set_mask;
487     theme->a_focused_unpressed_shade->texture[0].data.mask.mask = 
488         theme->a_unfocused_unpressed_shade->texture[0].data.mask.mask = 
489         theme->a_focused_pressed_shade->texture[0].data.mask.mask = 
490         theme->a_unfocused_pressed_shade->texture[0].data.mask.mask =
491         theme->shade_unset_mask;
492     theme->a_focused_pressed_set_shade->texture[0].data.mask.mask = 
493         theme->a_unfocused_pressed_set_shade->texture[0].data.mask.mask =
494         theme->shade_set_mask;
495     theme->a_focused_unpressed_iconify->texture[0].data.mask.mask = 
496         theme->a_unfocused_unpressed_iconify->texture[0].data.mask.mask = 
497         theme->a_focused_pressed_iconify->texture[0].data.mask.mask = 
498         theme->a_unfocused_pressed_iconify->texture[0].data.mask.mask =
499         theme->iconify_mask;
500     theme->a_focused_unpressed_max->texture[0].data.mask.color = 
501         theme->a_focused_pressed_max->texture[0].data.mask.color = 
502         theme->a_focused_pressed_set_max->texture[0].data.mask.color = 
503         theme->a_focused_unpressed_close->texture[0].data.mask.color = 
504         theme->a_focused_pressed_close->texture[0].data.mask.color = 
505         theme->a_focused_unpressed_desk->texture[0].data.mask.color = 
506         theme->a_focused_pressed_desk->texture[0].data.mask.color = 
507         theme->a_focused_pressed_set_desk->texture[0].data.mask.color = 
508         theme->a_focused_unpressed_shade->texture[0].data.mask.color = 
509         theme->a_focused_pressed_shade->texture[0].data.mask.color = 
510         theme->a_focused_pressed_set_shade->texture[0].data.mask.color = 
511         theme->a_focused_unpressed_iconify->texture[0].data.mask.color = 
512         theme->a_focused_pressed_iconify->texture[0].data.mask.color =
513         theme->titlebut_focused_color;
514     theme->a_unfocused_unpressed_max->texture[0].data.mask.color = 
515         theme->a_unfocused_pressed_max->texture[0].data.mask.color = 
516         theme->a_unfocused_pressed_set_max->texture[0].data.mask.color = 
517         theme->a_unfocused_unpressed_close->texture[0].data.mask.color = 
518         theme->a_unfocused_pressed_close->texture[0].data.mask.color = 
519         theme->a_unfocused_unpressed_desk->texture[0].data.mask.color = 
520         theme->a_unfocused_pressed_desk->texture[0].data.mask.color = 
521         theme->a_unfocused_pressed_set_desk->texture[0].data.mask.color = 
522         theme->a_unfocused_unpressed_shade->texture[0].data.mask.color = 
523         theme->a_unfocused_pressed_shade->texture[0].data.mask.color = 
524         theme->a_unfocused_pressed_set_shade->texture[0].data.mask.color = 
525         theme->a_unfocused_unpressed_iconify->texture[0].data.mask.color = 
526         theme->a_unfocused_pressed_iconify->texture[0].data.mask.color =
527         theme->titlebut_unfocused_color;
528
529     XrmDestroyDatabase(db);
530
531     theme->label_height = theme->winfont_height;
532     theme->title_height = theme->label_height + theme->bevel * 2;
533     theme->button_size = theme->label_height - 2;
534     theme->grip_width = theme->button_size * 2;
535
536     return theme;
537 }
538
539 void RrThemeFree(RrTheme *theme)
540 {
541     if (theme) {
542         g_free(theme->name);
543
544         RrColorFree(theme->b_color);
545         RrColorFree(theme->cb_unfocused_color);
546         RrColorFree(theme->cb_focused_color);
547         RrColorFree(theme->title_unfocused_color);
548         RrColorFree(theme->title_focused_color);
549         RrColorFree(theme->titlebut_unfocused_color);
550         RrColorFree(theme->titlebut_focused_color);
551         RrColorFree(theme->menu_color);
552         RrColorFree(theme->menu_title_color);
553         RrColorFree(theme->menu_disabled_color);
554         RrColorFree(theme->menu_hilite_color);
555
556         RrPixmapMaskFree(theme->max_set_mask);
557         RrPixmapMaskFree(theme->max_unset_mask);
558         RrPixmapMaskFree(theme->desk_set_mask);
559         RrPixmapMaskFree(theme->desk_unset_mask);
560         RrPixmapMaskFree(theme->shade_set_mask);
561         RrPixmapMaskFree(theme->shade_unset_mask);
562         RrPixmapMaskFree(theme->iconify_mask);
563         RrPixmapMaskFree(theme->close_mask);
564
565         RrFontClose(theme->winfont);
566         RrFontClose(theme->mtitlefont);
567         RrFontClose(theme->mfont);
568
569         g_free(theme->title_layout);
570
571         RrAppearanceFree(theme->a_focused_unpressed_max);
572         RrAppearanceFree(theme->a_focused_pressed_max);
573         RrAppearanceFree(theme->a_focused_pressed_set_max);
574         RrAppearanceFree(theme->a_unfocused_unpressed_max);
575         RrAppearanceFree(theme->a_unfocused_pressed_max);
576         RrAppearanceFree(theme->a_unfocused_pressed_set_max);
577         RrAppearanceFree(theme->a_focused_unpressed_close);
578         RrAppearanceFree(theme->a_focused_pressed_close);
579         RrAppearanceFree(theme->a_unfocused_unpressed_close);
580         RrAppearanceFree(theme->a_unfocused_pressed_close);
581         RrAppearanceFree(theme->a_focused_unpressed_desk);
582         RrAppearanceFree(theme->a_focused_pressed_desk);
583         RrAppearanceFree(theme->a_unfocused_unpressed_desk);
584         RrAppearanceFree(theme->a_unfocused_pressed_desk);
585         RrAppearanceFree(theme->a_focused_unpressed_shade);
586         RrAppearanceFree(theme->a_focused_pressed_shade);
587         RrAppearanceFree(theme->a_unfocused_unpressed_shade);
588         RrAppearanceFree(theme->a_unfocused_pressed_shade);
589         RrAppearanceFree(theme->a_focused_unpressed_iconify);
590         RrAppearanceFree(theme->a_focused_pressed_iconify);
591         RrAppearanceFree(theme->a_unfocused_unpressed_iconify);
592         RrAppearanceFree(theme->a_unfocused_pressed_iconify);
593         RrAppearanceFree(theme->a_focused_grip);
594         RrAppearanceFree(theme->a_unfocused_grip);
595         RrAppearanceFree(theme->a_focused_title);
596         RrAppearanceFree(theme->a_unfocused_title);
597         RrAppearanceFree(theme->a_focused_label);
598         RrAppearanceFree(theme->a_unfocused_label);
599         RrAppearanceFree(theme->a_icon);
600         RrAppearanceFree(theme->a_focused_handle);
601         RrAppearanceFree(theme->a_unfocused_handle);
602         RrAppearanceFree(theme->a_menu);
603         RrAppearanceFree(theme->a_menu_title);
604         RrAppearanceFree(theme->a_menu_item);
605         RrAppearanceFree(theme->a_menu_disabled);
606         RrAppearanceFree(theme->a_menu_hilite);
607         RrAppearanceFree(theme->app_hilite_bg);
608         RrAppearanceFree(theme->app_unhilite_bg);
609         RrAppearanceFree(theme->app_hilite_label);
610         RrAppearanceFree(theme->app_unhilite_label);
611         RrAppearanceFree(theme->app_icon);
612     }
613 }
614
615 static XrmDatabase loaddb(RrTheme *theme, char *name)
616 {
617     XrmDatabase db;
618
619     if ((db = XrmGetFileDatabase(name)))
620         theme->path = g_path_get_dirname(name);
621     if (db == NULL) {
622         char *s = g_build_filename(g_get_home_dir(), ".openbox", "themes",
623                                    name, NULL);
624         if ((db = XrmGetFileDatabase(s)))
625             theme->path = g_path_get_dirname(s);
626         g_free(s);
627     }
628     if (db == NULL) {
629         char *s = g_build_filename(THEMEDIR, name, NULL);
630         if ((db = XrmGetFileDatabase(s)))
631             theme->path = g_path_get_dirname(s);
632         g_free(s);
633     }
634
635     return db;
636 }
637
638 static char *create_class_name(char *rname)
639 {
640     char *rclass = g_strdup(rname);
641     char *p = rclass;
642
643     while (TRUE) {
644         *p = toupper(*p);
645         p = strchr(p+1, '.');
646         if (p == NULL) break;
647         ++p;
648         if (*p == '\0') break;
649     }
650     return rclass;
651 }
652
653 static gboolean read_int(XrmDatabase db, char *rname, int *value)
654 {
655     gboolean ret = FALSE;
656     char *rclass = create_class_name(rname);
657     char *rettype, *end;
658     XrmValue retvalue;
659   
660     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
661         retvalue.addr != NULL) {
662         *value = (int)strtol(retvalue.addr, &end, 10);
663         if (end != retvalue.addr)
664             ret = TRUE;
665     }
666
667     g_free(rclass);
668     return ret;
669 }
670
671 static gboolean read_string(XrmDatabase db, char *rname, char **value)
672 {
673     gboolean ret = FALSE;
674     char *rclass = create_class_name(rname);
675     char *rettype;
676     XrmValue retvalue;
677   
678     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
679         retvalue.addr != NULL) {
680         *value = retvalue.addr;
681         ret = TRUE;
682     }
683
684     g_free(rclass);
685     return ret;
686 }
687
688 static gboolean read_color(XrmDatabase db, const RrInstance *inst,
689                            gchar *rname, RrColor **value)
690 {
691     gboolean ret = FALSE;
692     char *rclass = create_class_name(rname);
693     char *rettype;
694     XrmValue retvalue;
695   
696     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
697         retvalue.addr != NULL) {
698         RrColor *c = RrColorParse(inst, retvalue.addr);
699         if (c != NULL) {
700             *value = c;
701             ret = TRUE;
702         }
703     }
704
705     g_free(rclass);
706     return ret;
707 }
708
709 static gboolean read_mask(const RrInstance *inst,
710                           gchar *maskname, RrTheme *theme,
711                           RrPixmapMask **value)
712 {
713     gboolean ret = FALSE;
714     char *s;
715     char *data_dir;
716     int hx, hy; /* ignored */
717     unsigned int w, h;
718     unsigned char *b;
719
720     data_dir = g_strdup_printf("%s_data", theme->name);
721
722     s = g_build_filename(g_get_home_dir(), ".openbox", "themes",
723                          data_dir, maskname, NULL);
724     if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess)
725         ret = TRUE;
726     else {
727         g_free(s);
728         s = g_build_filename(THEMEDIR, data_dir, maskname, NULL);
729         if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) 
730             ret = TRUE;
731         else {
732             g_free(s);
733             s = g_build_filename(theme->path, data_dir, maskname, NULL);
734             if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) 
735                 ret = TRUE;
736         }
737     }
738
739     if (ret) {
740         *value = RrPixmapMaskNew(inst, w, h, (char*)b);
741         XFree(b);
742     }
743       
744     g_free(s);
745     g_free(data_dir);
746
747     return ret;
748 }
749
750 static void parse_appearance(gchar *tex, RrSurfaceColorType *grad,
751                              RrReliefType *relief, RrBevelType *bevel,
752                              gboolean *interlaced, gboolean *border)
753 {
754     char *t;
755
756     /* convert to all lowercase */
757     for (t = tex; *t != '\0'; ++t)
758         *t = g_ascii_tolower(*t);
759
760     if (strstr(tex, "parentrelative") != NULL) {
761         *grad = RR_SURFACE_PARENTREL;
762     } else {
763         if (strstr(tex, "gradient") != NULL) {
764             if (strstr(tex, "crossdiagonal") != NULL)
765                 *grad = RR_SURFACE_CROSS_DIAGONAL;
766             else if (strstr(tex, "pyramid") != NULL)
767                 *grad = RR_SURFACE_PYRAMID;
768             else if (strstr(tex, "horizontal") != NULL)
769                 *grad = RR_SURFACE_HORIZONTAL;
770             else if (strstr(tex, "vertical") != NULL)
771                 *grad = RR_SURFACE_VERTICAL;
772             else
773                 *grad = RR_SURFACE_DIAGONAL;
774         } else {
775             *grad = RR_SURFACE_SOLID;
776         }
777
778         if (strstr(tex, "sunken") != NULL)
779             *relief = RR_RELIEF_SUNKEN;
780         else if (strstr(tex, "flat") != NULL)
781             *relief = RR_RELIEF_FLAT;
782         else
783             *relief = RR_RELIEF_RAISED;
784         
785         *border = FALSE;
786         if (*relief == RR_RELIEF_FLAT) {
787             if (strstr(tex, "border") != NULL)
788                 *border = TRUE;
789         } else {
790             if (strstr(tex, "bevel2") != NULL)
791                 *bevel = RR_BEVEL_2;
792             else
793                 *bevel = RR_BEVEL_1;
794         }
795
796         if (strstr(tex, "interlaced") != NULL)
797             *interlaced = TRUE;
798         else
799             *interlaced = FALSE;
800     }
801 }
802
803
804 static gboolean read_appearance(XrmDatabase db, const RrInstance *inst,
805                            gchar *rname, RrAppearance *value)
806 {
807     gboolean ret = FALSE;
808     char *rclass = create_class_name(rname), *cname, *ctoname, *bcname;
809     char *rettype;
810     XrmValue retvalue;
811
812     cname = g_strconcat(rname, ".color", NULL);
813     ctoname = g_strconcat(rname, ".colorTo", NULL);
814     bcname = g_strconcat(rname, ".borderColor", NULL);
815
816     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
817         retvalue.addr != NULL) {
818         parse_appearance(retvalue.addr,
819                          &value->surface.grad,
820                          &value->surface.relief,
821                          &value->surface.bevel,
822                          &value->surface.interlaced,
823                          &value->surface.border);
824         if (!read_color(db, inst, cname, &value->surface.primary))
825             value->surface.primary = RrColorNew(inst, 0, 0, 0);
826         if (!read_color(db, inst, ctoname, &value->surface.secondary))
827             value->surface.secondary = RrColorNew(inst, 0, 0, 0);
828         if (value->surface.border)
829             if (!read_color(db, inst, bcname,
830                             &value->surface.border_color))
831                 value->surface.border_color = RrColorNew(inst, 0, 0, 0);
832         ret = TRUE;
833     }
834
835     g_free(bcname);
836     g_free(ctoname);
837     g_free(cname);
838     g_free(rclass);
839     return ret;
840 }
841
842 static void set_default_appearance(RrAppearance *a)
843 {
844     a->surface.grad = RR_SURFACE_SOLID;
845     a->surface.relief = RR_RELIEF_FLAT;
846     a->surface.bevel = RR_BEVEL_1;
847     a->surface.interlaced = FALSE;
848     a->surface.border = FALSE;
849     a->surface.primary = RrColorNew(a->inst, 0, 0, 0);
850     a->surface.secondary = RrColorNew(a->inst, 0, 0, 0);
851 }