]> icculus.org git repositories - mikachu/openbox.git/blob - engines/openbox/theme.c
free the themename properly
[mikachu/openbox.git] / engines / openbox / theme.c
1 #include "openbox.h"
2 #include "../../kernel/config.h"
3 #include "../../kernel/openbox.h"
4
5 #include <glib.h>
6 #include <X11/Xlib.h>
7 #include <X11/Xresource.h>
8 #ifdef HAVE_STDLIB_H
9 #  include <stdlib.h>
10 #endif
11 #ifdef HAVE_CTYPE_H
12 #  include <ctype.h>
13 #endif
14 #ifdef HAVE_STRING_H
15 #  include <string.h>
16 #endif
17
18 static XrmDatabase loaddb(char *theme)
19 {
20     XrmDatabase db;
21
22     db = XrmGetFileDatabase(theme);
23     if (db == NULL) {
24         char *s = g_build_filename(g_get_home_dir(), ".openbox", "themes",
25                                    "openbox", theme, NULL);
26         db = XrmGetFileDatabase(s);
27         g_free(s);
28     }
29     if (db == NULL) {
30         char *s = g_build_filename(THEMEDIR, theme, NULL);
31         db = XrmGetFileDatabase(s);
32         g_free(s);
33     }
34     return db;
35 }
36
37 static char *create_class_name(char *rname)
38 {
39     char *rclass = g_strdup(rname);
40     char *p = rclass;
41
42     while (TRUE) {
43         *p = toupper(*p);
44         p = strchr(p+1, '.');
45         if (p == NULL) break;
46         ++p;
47         if (*p == '\0') break;
48     }
49     return rclass;
50 }
51
52 gboolean read_bool(XrmDatabase db, char *rname, gboolean *value)
53 {
54     gboolean ret = FALSE;
55     char *rclass = create_class_name(rname);
56     char *rettype;
57     XrmValue retvalue;
58   
59     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
60         retvalue.addr != NULL) {
61         if (!g_ascii_strcasecmp(retvalue.addr, "true"))
62             *value = TRUE;
63         else
64             *value = FALSE;
65         ret = TRUE;
66     }
67
68     g_free(rclass);
69     return ret;
70 }
71
72 gboolean read_int(XrmDatabase db, char *rname, int *value)
73 {
74     gboolean ret = FALSE;
75     char *rclass = create_class_name(rname);
76     char *rettype, *end;
77     XrmValue retvalue;
78   
79     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
80         retvalue.addr != NULL) {
81         *value = (int)strtol(retvalue.addr, &end, 10);
82         if (end != retvalue.addr)
83             ret = TRUE;
84     }
85
86     g_free(rclass);
87     return ret;
88 }
89
90 gboolean read_string(XrmDatabase db, char *rname, char **value)
91 {
92     gboolean ret = FALSE;
93     char *rclass = create_class_name(rname);
94     char *rettype;
95     XrmValue retvalue;
96   
97     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
98         retvalue.addr != NULL) {
99         *value = g_strdup(retvalue.addr);
100         ret = TRUE;
101     }
102
103     g_free(rclass);
104     return ret;
105 }
106
107 gboolean read_color(XrmDatabase db, char *rname, color_rgb **value)
108 {
109     gboolean ret = FALSE;
110     char *rclass = create_class_name(rname);
111     char *rettype;
112     XrmValue retvalue;
113   
114     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
115         retvalue.addr != NULL) {
116         color_rgb *c = color_parse(retvalue.addr);
117         if (c != NULL) {
118             *value = c;
119             ret = TRUE;
120         }
121     }
122
123     g_free(rclass);
124     return ret;
125 }
126
127 gboolean read_mask(XrmDatabase db, char *rname, pixmap_mask **value)
128 {
129     gboolean ret = FALSE;
130     char *rclass = create_class_name(rname);
131     char *rettype;
132     char *s;
133     char *button_dir;
134     XrmValue retvalue;
135     int hx, hy; /* ignored */
136     unsigned int w, h;
137     unsigned char *b;
138     ConfigValue theme;
139   
140     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
141         retvalue.addr != NULL) {
142         if (!config_get("theme", Config_String, &theme))
143             g_assert_not_reached(); /* where's the default!? its not set? */
144
145         button_dir = g_strdup_printf("%s_buttons", theme.string);
146
147         s = g_build_filename(g_get_home_dir(), ".openbox", "themes",
148                              "openbox", button_dir, retvalue.addr, NULL);
149
150         if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess)
151             ret = TRUE;
152         else {
153             g_free(s);
154             s = g_build_filename(THEMEDIR, button_dir, retvalue.addr, NULL);
155         
156             if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == BitmapSuccess) 
157                 ret = TRUE;
158             else {
159                 char *themename;
160
161                 g_free(s);
162                 themename = g_path_get_basename(theme.string);
163                 s = g_strdup_printf("%s_buttons/%s", theme.string,
164                                     themename);
165                 g_free(themename);
166                 if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) ==
167                     BitmapSuccess) 
168                     ret = TRUE;
169                 else
170                     g_message("Unable to find bitmap '%s'", s);
171             }
172         }
173
174         if (ret) {
175             *value = pixmap_mask_new(w, h, (char*)b);
176             XFree(b);
177         }
178       
179         g_free(s);
180         g_free(button_dir);
181     }
182
183     g_free(rclass);
184     return ret;
185 }
186
187 static void parse_appearance(char *tex, SurfaceColorType *grad,
188                              ReliefType *relief, BevelType *bevel,
189                              gboolean *interlaced, gboolean *border)
190 {
191     char *t;
192
193     /* convert to all lowercase */
194     for (t = tex; *t != '\0'; ++t)
195         *t = g_ascii_tolower(*t);
196
197     if (strstr(tex, "parentrelative") != NULL) {
198         *grad = Background_ParentRelative;
199     } else {
200         if (strstr(tex, "gradient") != NULL) {
201             if (strstr(tex, "crossdiagonal") != NULL)
202                 *grad = Background_CrossDiagonal;
203             else if (strstr(tex, "rectangle") != NULL)
204                 *grad = Background_Rectangle;
205             else if (strstr(tex, "pyramid") != NULL)
206                 *grad = Background_Pyramid;
207             else if (strstr(tex, "pipecross") != NULL)
208                 *grad = Background_PipeCross;
209             else if (strstr(tex, "elliptic") != NULL)
210                 *grad = Background_Elliptic;
211             else if (strstr(tex, "horizontal") != NULL)
212                 *grad = Background_Horizontal;
213             else if (strstr(tex, "vertical") != NULL)
214                 *grad = Background_Vertical;
215             else
216                 *grad = Background_Diagonal;
217         } else {
218             *grad = Background_Solid;
219         }
220
221         if (strstr(tex, "sunken") != NULL)
222             *relief = Sunken;
223         else if (strstr(tex, "flat") != NULL)
224             *relief = Flat;
225         else
226             *relief = Raised;
227         
228         *border = FALSE;
229         if (*relief == Flat) {
230             if (strstr(tex, "border") != NULL)
231                 *border = TRUE;
232         } else {
233             if (strstr(tex, "bevel2") != NULL)
234                 *bevel = Bevel2;
235             else
236                 *bevel = Bevel1;
237         }
238
239         if (strstr(tex, "interlaced") != NULL)
240             *interlaced = TRUE;
241         else
242             *interlaced = FALSE;
243     }
244 }
245
246
247 gboolean read_appearance(XrmDatabase db, char *rname, Appearance *value)
248 {
249     gboolean ret = FALSE;
250     char *rclass = create_class_name(rname), *cname, *ctoname, *bcname;
251     char *rettype;
252     XrmValue retvalue;
253
254     cname = g_strconcat(rname, ".color", NULL);
255     ctoname = g_strconcat(rname, ".colorTo", NULL);
256     bcname = g_strconcat(rname, ".borderColor", NULL);
257
258     if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) &&
259         retvalue.addr != NULL) {
260         parse_appearance(retvalue.addr,
261                          &value->surface.data.planar.grad,
262                          &value->surface.data.planar.relief,
263                          &value->surface.data.planar.bevel,
264                          &value->surface.data.planar.interlaced,
265                          &value->surface.data.planar.border);
266         if (!read_color(db, cname, &value->surface.data.planar.primary))
267             value->surface.data.planar.primary = color_new(0, 0, 0);
268         if (!read_color(db, ctoname, &value->surface.data.planar.secondary))
269             value->surface.data.planar.secondary = color_new(0, 0, 0);
270         if (value->surface.data.planar.border)
271             if (!read_color(db, bcname,
272                             &value->surface.data.planar.border_color))
273                 value->surface.data.planar.border_color = color_new(0, 0, 0);
274         ret = TRUE;
275     }
276
277     g_free(bcname);
278     g_free(ctoname);
279     g_free(cname);
280     g_free(rclass);
281     return ret;
282 }
283
284 void set_default_appearance(Appearance *a)
285 {
286     a->surface.data.planar.grad = Background_Solid;
287     a->surface.data.planar.relief = Flat;
288     a->surface.data.planar.bevel = Bevel1;
289     a->surface.data.planar.interlaced = FALSE;
290     a->surface.data.planar.border = FALSE;
291     a->surface.data.planar.primary = color_new(0, 0, 0);
292     a->surface.data.planar.secondary = color_new(0, 0, 0);
293 }
294
295 gboolean load()
296 {
297     XrmDatabase db = NULL;
298     Justify winjust;
299     char *winjuststr;
300     ConfigValue theme, shadow, offset, font;
301
302     if (config_get("theme", Config_String, &theme)) {
303         db = loaddb(theme.string);
304         if (db == NULL) {
305             g_warning("Failed to load the theme '%s'", theme.string);
306             g_message("Falling back to the default: '%s'", DEFAULT_THEME);
307         }
308     }
309     if (db == NULL) {
310         db = loaddb(DEFAULT_THEME);
311         if (db == NULL) {
312             g_warning("Failed to load the theme '%s'.", DEFAULT_THEME);
313             return FALSE;
314         }
315         /* change to reflect what was actually loaded */
316         theme.string = DEFAULT_THEME;
317         config_set("theme", Config_String, theme);
318     }
319
320     /* load the font, not from the theme file tho, its in the config */
321
322     if (!config_get("font.shadow", Config_Bool, &shadow)) {
323         shadow.bool = TRUE; /* default */
324         config_set("font.shadow", Config_Bool, shadow);
325     }
326     s_winfont_shadow = shadow.bool;
327     if (!config_get("font.shadow.offset", Config_Integer, &offset) ||
328         offset.integer < 0 || offset.integer >= 10) {
329         offset.integer = 1; /* default */
330         config_set("font.shadow.offset", Config_Integer, offset);
331     }
332     s_winfont_shadow_offset = offset.integer;
333     if (!config_get("font", Config_String, &font)) {
334         font.string = DEFAULT_FONT;
335         config_set("font", Config_String, font);
336     }
337     s_winfont = font_open(font.string);
338     s_winfont_height = font_height(s_winfont, s_winfont_shadow,
339                                    s_winfont_shadow_offset);
340
341     winjust = Justify_Left;
342     if (read_string(db, "window.justify", &winjuststr)) {
343         if (!g_ascii_strcasecmp(winjuststr, "right"))
344             winjust = Justify_Right;
345         else if (!g_ascii_strcasecmp(winjuststr, "center"))
346             winjust = Justify_Center;
347         g_free(winjuststr);
348     }
349
350     if (!read_int(db, "handleWidth", &s_handle_height) ||
351         s_handle_height < 0 || s_handle_height > 100) s_handle_height = 6;
352     if (!read_int(db, "bevelWidth", &s_bevel) ||
353         s_bevel <= 0 || s_bevel > 100) s_bevel = 3;
354     if (!read_int(db, "borderWidth", &s_bwidth) ||
355         s_bwidth < 0 || s_bwidth > 100) s_bwidth = 1;
356     if (!read_int(db, "frameWidth", &s_cbwidth) ||
357         s_cbwidth < 0 || s_cbwidth > 100) s_cbwidth = s_bevel;
358
359     if (!read_color(db, "borderColor", &s_b_color))
360         s_b_color = color_new(0, 0, 0);
361     if (!read_color(db, "window.frame.focusColor", &s_cb_focused_color))
362         s_cb_focused_color = color_new(0xff, 0xff, 0xff);
363     if (!read_color(db, "window.frame.unfocusColor", &s_cb_unfocused_color))
364         s_cb_unfocused_color = color_new(0xff, 0xff, 0xff);
365     if (!read_color(db, "window.label.focus.textColor",
366                     &s_title_focused_color))
367         s_title_focused_color = color_new(0xff, 0xff, 0xff);
368     if (!read_color(db, "window.label.unfocus.textColor",
369                     &s_title_unfocused_color))
370         s_title_unfocused_color = color_new(0xff, 0xff, 0xff);
371     if (!read_color(db, "window.button.focus.picColor",
372                     &s_titlebut_focused_color))
373         s_titlebut_focused_color = color_new(0, 0, 0);
374     if (!read_color(db, "window.button.unfocus.picColor",
375                     &s_titlebut_unfocused_color))
376         s_titlebut_unfocused_color = color_new(0xff, 0xff, 0xff);
377
378     if (!read_mask(db, "window.button.max.mask", &s_max_mask)) {
379         char data[] = { 0x7c, 0x44, 0x47, 0x47, 0x7f, 0x1f, 0x1f  };
380         s_max_mask = pixmap_mask_new(7, 7, data);
381     }
382     if (!read_mask(db, "window.button.icon.mask", &s_icon_mask)) {
383         char data[] = { 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e };
384         s_icon_mask = pixmap_mask_new(7, 7, data);
385     }
386     if (!read_mask(db, "window.button.stick.mask", &s_desk_mask)) {
387         char data[] = { 0x00, 0x36, 0x36, 0x00, 0x36, 0x36, 0x00 };
388         s_desk_mask = pixmap_mask_new(7, 7, data);
389     }
390     if (!read_mask(db, "window.button.close.mask", &s_close_mask)) {
391         char data[] = { 0x22, 0x77, 0x3e, 0x1c, 0x3e, 0x77, 0x22 };
392         s_close_mask = pixmap_mask_new(7, 7, data);
393     }        
394
395     if (!read_appearance(db, "window.title.focus", a_focused_title))
396         set_default_appearance(a_focused_title);
397     if (!read_appearance(db, "window.title.unfocus", a_unfocused_title))
398         set_default_appearance(a_unfocused_title);
399     if (!read_appearance(db, "window.label.focus", a_focused_label))
400         set_default_appearance(a_focused_label);
401     if (!read_appearance(db, "window.label.unfocus", a_unfocused_label))
402         set_default_appearance(a_unfocused_label);
403     if (!read_appearance(db, "window.handle.focus", a_focused_handle))
404         set_default_appearance(a_focused_handle);
405     if (!read_appearance(db, "window.handle.unfocus", a_unfocused_handle))
406         set_default_appearance(a_unfocused_handle);
407     if (!read_appearance(db, "window.grip.focus", a_focused_grip))
408         set_default_appearance(a_focused_grip);
409     if (!read_appearance(db, "window.grip.unfocus", a_unfocused_grip))
410         set_default_appearance(a_unfocused_grip);
411
412     if (!read_appearance(db, "window.button.pressed.focus",
413                          a_focused_pressed_max))
414         if (!read_appearance(db, "window.button.pressed",
415                              a_focused_pressed_max))
416             set_default_appearance(a_focused_pressed_max);
417     if (!read_appearance(db, "window.button.pressed.unfocus",
418                          a_unfocused_pressed_max))
419         if (!read_appearance(db, "window.button.pressed",
420                              a_unfocused_pressed_max))
421             set_default_appearance(a_unfocused_pressed_max);
422     if (!read_appearance(db, "window.button.focus",
423                          a_focused_unpressed_max))
424         set_default_appearance(a_focused_unpressed_max);
425     if (!read_appearance(db, "window.button.unfocus",
426                          a_unfocused_unpressed_max))
427         set_default_appearance(a_unfocused_unpressed_max);
428
429     a_unfocused_unpressed_close = appearance_copy(a_unfocused_unpressed_max);
430     a_unfocused_pressed_close = appearance_copy(a_unfocused_pressed_max);
431     a_focused_unpressed_close = appearance_copy(a_focused_unpressed_max);
432     a_focused_pressed_close = appearance_copy(a_focused_pressed_max);
433     a_unfocused_unpressed_desk = appearance_copy(a_unfocused_unpressed_max);
434     a_unfocused_pressed_desk = appearance_copy(a_unfocused_pressed_max);
435     a_focused_unpressed_desk = appearance_copy(a_focused_unpressed_max);
436     a_focused_pressed_desk = appearance_copy(a_focused_pressed_max);
437     a_unfocused_unpressed_iconify = appearance_copy(a_unfocused_unpressed_max);
438     a_unfocused_pressed_iconify = appearance_copy(a_unfocused_pressed_max);
439     a_focused_unpressed_iconify = appearance_copy(a_focused_unpressed_max);
440     a_focused_pressed_iconify = appearance_copy(a_focused_pressed_max);
441
442     a_icon->surface.data.planar.grad = Background_ParentRelative;
443
444     /* set up the textures */
445     a_focused_label->texture[0].type = Text;
446     a_focused_label->texture[0].data.text.justify = winjust;
447     a_focused_label->texture[0].data.text.font = s_winfont;
448     a_focused_label->texture[0].data.text.shadow = s_winfont_shadow;
449     a_focused_label->texture[0].data.text.offset = s_winfont_shadow_offset;
450     a_focused_label->texture[0].data.text.color = s_title_focused_color;
451
452     a_unfocused_label->texture[0].type = Text;
453     a_unfocused_label->texture[0].data.text.justify = winjust;
454     a_unfocused_label->texture[0].data.text.font = s_winfont;
455     a_unfocused_label->texture[0].data.text.shadow = s_winfont_shadow;
456     a_unfocused_label->texture[0].data.text.offset = s_winfont_shadow_offset;
457     a_unfocused_label->texture[0].data.text.color = s_title_unfocused_color;
458
459     a_focused_unpressed_max->texture[0].type = 
460         a_focused_pressed_max->texture[0].type = 
461         a_unfocused_unpressed_max->texture[0].type = 
462         a_unfocused_pressed_max->texture[0].type = 
463         a_focused_unpressed_close->texture[0].type = 
464         a_focused_pressed_close->texture[0].type = 
465         a_unfocused_unpressed_close->texture[0].type = 
466         a_unfocused_pressed_close->texture[0].type = 
467         a_focused_unpressed_desk->texture[0].type = 
468         a_focused_pressed_desk->texture[0].type = 
469         a_unfocused_unpressed_desk->texture[0].type = 
470         a_unfocused_pressed_desk->texture[0].type = 
471         a_focused_unpressed_iconify->texture[0].type = 
472         a_focused_pressed_iconify->texture[0].type = 
473         a_unfocused_unpressed_iconify->texture[0].type = 
474         a_unfocused_pressed_iconify->texture[0].type = Bitmask;
475     a_focused_unpressed_max->texture[0].data.mask.mask = 
476         a_focused_pressed_max->texture[0].data.mask.mask = 
477         a_unfocused_unpressed_max->texture[0].data.mask.mask = 
478         a_unfocused_pressed_max->texture[0].data.mask.mask = s_max_mask;
479     a_focused_unpressed_close->texture[0].data.mask.mask = 
480         a_focused_pressed_close->texture[0].data.mask.mask = 
481         a_unfocused_unpressed_close->texture[0].data.mask.mask = 
482         a_unfocused_pressed_close->texture[0].data.mask.mask = s_close_mask;
483     a_focused_unpressed_desk->texture[0].data.mask.mask = 
484         a_focused_pressed_desk->texture[0].data.mask.mask = 
485         a_unfocused_unpressed_desk->texture[0].data.mask.mask = 
486         a_unfocused_pressed_desk->texture[0].data.mask.mask = s_desk_mask;
487     a_focused_unpressed_iconify->texture[0].data.mask.mask = 
488         a_focused_pressed_iconify->texture[0].data.mask.mask = 
489         a_unfocused_unpressed_iconify->texture[0].data.mask.mask = 
490         a_unfocused_pressed_iconify->texture[0].data.mask.mask = s_icon_mask;
491     a_focused_unpressed_max->texture[0].data.mask.color = 
492         a_focused_pressed_max->texture[0].data.mask.color = 
493         a_focused_unpressed_close->texture[0].data.mask.color = 
494         a_focused_pressed_close->texture[0].data.mask.color = 
495         a_focused_unpressed_desk->texture[0].data.mask.color = 
496         a_focused_pressed_desk->texture[0].data.mask.color = 
497         a_focused_unpressed_iconify->texture[0].data.mask.color = 
498         a_focused_pressed_iconify->texture[0].data.mask.color =
499         s_titlebut_focused_color;
500     a_unfocused_unpressed_max->texture[0].data.mask.color = 
501         a_unfocused_pressed_max->texture[0].data.mask.color = 
502         a_unfocused_unpressed_close->texture[0].data.mask.color = 
503         a_unfocused_pressed_close->texture[0].data.mask.color = 
504         a_unfocused_unpressed_desk->texture[0].data.mask.color = 
505         a_unfocused_pressed_desk->texture[0].data.mask.color = 
506         a_unfocused_unpressed_iconify->texture[0].data.mask.color = 
507         a_unfocused_pressed_iconify->texture[0].data.mask.color =
508         s_titlebut_unfocused_color;
509
510     XrmDestroyDatabase(db);
511     return TRUE;
512 }
513
514