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