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