]> icculus.org git repositories - dana/openbox.git/blob - openbox/config.c
Rename ObActions* to ObAction* and remove more 3.4-compat action stuff that was missed.
[dana/openbox.git] / openbox / config.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    config.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "config.h"
21 #include "keyboard.h"
22 #include "mouse.h"
23 #include "action.h"
24 #include "action_list.h"
25 #include "action_parser.h"
26 #include "translate.h"
27 #include "client.h"
28 #include "screen.h"
29 #include "openbox.h"
30 #include "gettext.h"
31 #include "obt/paths.h"
32
33 gboolean config_focus_new;
34 gboolean config_focus_follow;
35 guint    config_focus_delay;
36 gboolean config_focus_raise;
37 gboolean config_focus_last;
38 gboolean config_focus_under_mouse;
39 gboolean config_unfocus_leave;
40
41 ObPlacePolicy  config_place_policy;
42 gboolean       config_place_center;
43 ObPlaceMonitor config_place_monitor;
44
45 guint          config_primary_monitor_index;
46 ObPlaceMonitor config_primary_monitor;
47
48 StrutPartial config_margins;
49
50 gchar   *config_theme;
51 gboolean config_theme_keepborder;
52 guint    config_theme_window_list_icon_size;
53
54 gchar   *config_title_layout;
55
56 gboolean config_animate_iconify;
57
58 RrFont *config_font_activewindow;
59 RrFont *config_font_inactivewindow;
60 RrFont *config_font_menuitem;
61 RrFont *config_font_menutitle;
62 RrFont *config_font_activeosd;
63 RrFont *config_font_inactiveosd;
64
65 guint   config_desktops_num;
66 GSList *config_desktops_names;
67 guint   config_screen_firstdesk;
68 guint   config_desktop_popup_time;
69
70 gboolean         config_resize_redraw;
71 gint             config_resize_popup_show;
72 ObResizePopupPos config_resize_popup_pos;
73 GravityPoint     config_resize_popup_fixed;
74
75 ObStackingLayer config_dock_layer;
76 gboolean        config_dock_floating;
77 gboolean        config_dock_nostrut;
78 ObDirection     config_dock_pos;
79 gint            config_dock_x;
80 gint            config_dock_y;
81 ObOrientation   config_dock_orient;
82 gboolean        config_dock_hide;
83 guint           config_dock_hide_delay;
84 guint           config_dock_show_delay;
85 guint           config_dock_app_move_button;
86 guint           config_dock_app_move_modifiers;
87
88 guint config_keyboard_reset_keycode;
89 guint config_keyboard_reset_state;
90
91 gint     config_mouse_threshold;
92 gint     config_mouse_dclicktime;
93 gint     config_mouse_screenedgetime;
94 gboolean config_mouse_screenedgewarp;
95
96 guint    config_menu_hide_delay;
97 gboolean config_menu_middle;
98 guint    config_submenu_show_delay;
99 guint    config_submenu_hide_delay;
100 gboolean config_menu_manage_desktops;
101 gboolean config_menu_show_icons;
102
103 GSList *config_menu_files;
104
105 gint     config_resist_win;
106 gint     config_resist_edge;
107
108 GSList *config_per_app_settings;
109
110 ObAppSettings* config_create_app_settings(void)
111 {
112     ObAppSettings *settings = g_slice_new0(ObAppSettings);
113     settings->type = -1;
114     settings->decor = -1;
115     settings->shade = -1;
116     settings->monitor_type = OB_PLACE_MONITOR_ANY;
117     settings->monitor = -1;
118     settings->focus = -1;
119     settings->desktop = 0;
120     settings->layer = -2;
121     settings->iconic = -1;
122     settings->skip_pager = -1;
123     settings->skip_taskbar = -1;
124     settings->fullscreen = -1;
125     settings->max_horz = -1;
126     settings->max_vert = -1;
127     return settings;
128 }
129
130 #define copy_if(setting, default) \
131   if (src->setting != default) dst->setting = src->setting
132 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
133                                            ObAppSettings *dst)
134 {
135     g_assert(src != NULL);
136     g_assert(dst != NULL);
137
138     copy_if(type, (ObClientType)-1);
139     copy_if(decor, -1);
140     copy_if(shade, -1);
141     copy_if(monitor_type, OB_PLACE_MONITOR_ANY);
142     copy_if(monitor, -1);
143     copy_if(focus, -1);
144     copy_if(desktop, 0);
145     copy_if(layer, -2);
146     copy_if(iconic, -1);
147     copy_if(skip_pager, -1);
148     copy_if(skip_taskbar, -1);
149     copy_if(fullscreen, -1);
150     copy_if(max_horz, -1);
151     copy_if(max_vert, -1);
152
153     if (src->pos_given) {
154         dst->pos_given = TRUE;
155         dst->pos_force = src->pos_force;
156         dst->position = src->position;
157         /* monitor is copied above */
158     }
159 }
160
161 void config_parse_relative_number(gchar *s, gint *num, gint *denom)
162 {
163     *num = strtol(s, &s, 10);
164
165     if (*s == '%') {
166         *denom = 100;
167     } else if (*s == '/') {
168         *denom = atoi(s+1);
169     }
170 }
171
172 void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
173 {
174     gchar *s = obt_xml_node_string(node);
175     if (!g_ascii_strcasecmp(s, "center"))
176         c->center = TRUE;
177     else {
178         gchar *ps = s;
179         if (s[0] == '-')
180             c->opposite = TRUE;
181         if (s[0] == '-' || s[0] == '+')
182             ps++;
183         config_parse_relative_number(ps, &c->pos, &c->denom);
184     }
185     g_free(s);
186 }
187
188 /*
189   <applications>
190     <application name="aterm">
191       <decor>false</decor>
192     </application>
193     <application name="Rhythmbox">
194       <layer>above</layer>
195       <position>
196         <x>700</x>
197         <y>0</y>
198         <monitor>1</monitor>
199       </position>
200       .. there is a lot more settings available
201     </application>
202   </applications>
203 */
204
205 /* Manages settings for individual applications.
206    Some notes: monitor is the screen number in a multi monitor
207    (Xinerama) setup (starting from 0), or mouse: the monitor the pointer
208    is on, active: the active monitor, primary: the primary monitor.
209    Layer can be three values, above (Always on top), below
210    (Always on bottom) and everything else (normal behaviour).
211    Positions can be an integer value or center, which will
212    center the window in the specified axis. Position is within
213    the monitor, so <position><x>center</x></position><monitor>2</monitor>
214    will center the window on the second monitor.
215 */
216 static void parse_per_app_settings(xmlNodePtr node, gpointer d)
217 {
218     xmlNodePtr app = obt_xml_find_node(node->children, "application");
219     gchar *name = NULL, *class = NULL, *role = NULL, *title = NULL,
220         *type_str = NULL;
221     gboolean name_set, class_set, type_set, role_set, title_set;
222     ObClientType type;
223     gboolean x_pos_given;
224
225     while (app) {
226         x_pos_given = FALSE;
227
228         class_set = obt_xml_attr_string(app, "class", &class);
229         name_set = obt_xml_attr_string(app, "name", &name);
230         type_set = obt_xml_attr_string(app, "type", &type_str);
231         role_set = obt_xml_attr_string(app, "role", &role);
232         title_set = obt_xml_attr_string(app, "title", &title);
233
234         /* validate the type tho */
235         if (type_set) {
236             if (!g_ascii_strcasecmp(type_str, "normal"))
237                 type = OB_CLIENT_TYPE_NORMAL;
238             else if (!g_ascii_strcasecmp(type_str, "dialog"))
239                 type = OB_CLIENT_TYPE_DIALOG;
240             else if (!g_ascii_strcasecmp(type_str, "splash"))
241                 type = OB_CLIENT_TYPE_SPLASH;
242             else if (!g_ascii_strcasecmp(type_str, "utility"))
243                 type = OB_CLIENT_TYPE_UTILITY;
244             else if (!g_ascii_strcasecmp(type_str, "menu"))
245                 type = OB_CLIENT_TYPE_MENU;
246             else if (!g_ascii_strcasecmp(type_str, "toolbar"))
247                 type = OB_CLIENT_TYPE_TOOLBAR;
248             else if (!g_ascii_strcasecmp(type_str, "dock"))
249                 type = OB_CLIENT_TYPE_DOCK;
250             else if (!g_ascii_strcasecmp(type_str, "desktop"))
251                 type = OB_CLIENT_TYPE_DESKTOP;
252             else
253                 type_set = FALSE; /* not valid! */
254         }
255
256         if (class_set || name_set || role_set || title_set || type_set) {
257             xmlNodePtr n, c;
258             ObAppSettings *settings = config_create_app_settings();
259
260             if (name_set)
261                 settings->name = g_pattern_spec_new(name);
262
263             if (class_set)
264                 settings->class = g_pattern_spec_new(class);
265
266             if (role_set)
267                 settings->role = g_pattern_spec_new(role);
268
269             if (title_set)
270                 settings->title = g_pattern_spec_new(title);
271
272             if (type_set)
273                 settings->type = type;
274
275             if ((n = obt_xml_find_node(app->children, "decor")))
276                 if (!obt_xml_node_contains(n, "default"))
277                     settings->decor = obt_xml_node_bool(n);
278
279             if ((n = obt_xml_find_node(app->children, "shade")))
280                 if (!obt_xml_node_contains(n, "default"))
281                     settings->shade = obt_xml_node_bool(n);
282
283             if ((n = obt_xml_find_node(app->children, "position"))) {
284                 if ((c = obt_xml_find_node(n->children, "x")))
285                     if (!obt_xml_node_contains(c, "default")) {
286                         config_parse_gravity_coord(c, &settings->position.x);
287                         x_pos_given = TRUE;
288                     }
289
290                 if (x_pos_given && (c = obt_xml_find_node(n->children, "y")))
291                     if (!obt_xml_node_contains(c, "default")) {
292                         config_parse_gravity_coord(c, &settings->position.y);
293                         settings->pos_given = TRUE;
294                     }
295
296                 /* monitor can be set without setting x or y */
297                 if ((c = obt_xml_find_node(n->children, "monitor")))
298                     if (!obt_xml_node_contains(c, "default")) {
299                         gchar *s = obt_xml_node_string(c);
300                         if (!g_ascii_strcasecmp(s, "mouse"))
301                             settings->monitor_type =
302                                     OB_PLACE_MONITOR_MOUSE;
303                         else if (!g_ascii_strcasecmp(s, "active"))
304                             settings->monitor_type =
305                                     OB_PLACE_MONITOR_ACTIVE;
306                         else if (!g_ascii_strcasecmp(s, "primary"))
307                             settings->monitor_type =
308                                     OB_PLACE_MONITOR_PRIMARY;
309                         else
310                             settings->monitor = obt_xml_node_int(c);
311                         g_free(s);
312                     }
313
314                 obt_xml_attr_bool(n, "force", &settings->pos_force);
315             }
316
317             if ((n = obt_xml_find_node(app->children, "focus")))
318                 if (!obt_xml_node_contains(n, "default"))
319                     settings->focus = obt_xml_node_bool(n);
320
321             if ((n = obt_xml_find_node(app->children, "desktop"))) {
322                 if (!obt_xml_node_contains(n, "default")) {
323                     gchar *s = obt_xml_node_string(n);
324                     if (!g_ascii_strcasecmp(s, "all"))
325                         settings->desktop = DESKTOP_ALL;
326                     else {
327                         gint i = obt_xml_node_int(n);
328                         if (i > 0)
329                             settings->desktop = i;
330                     }
331                     g_free(s);
332                 }
333             }
334
335             if ((n = obt_xml_find_node(app->children, "layer")))
336                 if (!obt_xml_node_contains(n, "default")) {
337                     gchar *s = obt_xml_node_string(n);
338                     if (!g_ascii_strcasecmp(s, "above"))
339                         settings->layer = 1;
340                     else if (!g_ascii_strcasecmp(s, "below"))
341                         settings->layer = -1;
342                     else
343                         settings->layer = 0;
344                     g_free(s);
345                 }
346
347             if ((n = obt_xml_find_node(app->children, "iconic")))
348                 if (!obt_xml_node_contains(n, "default"))
349                     settings->iconic = obt_xml_node_bool(n);
350
351             if ((n = obt_xml_find_node(app->children, "skip_pager")))
352                 if (!obt_xml_node_contains(n, "default"))
353                     settings->skip_pager = obt_xml_node_bool(n);
354
355             if ((n = obt_xml_find_node(app->children, "skip_taskbar")))
356                 if (!obt_xml_node_contains(n, "default"))
357                     settings->skip_taskbar = obt_xml_node_bool(n);
358
359             if ((n = obt_xml_find_node(app->children, "fullscreen")))
360                 if (!obt_xml_node_contains(n, "default"))
361                     settings->fullscreen = obt_xml_node_bool(n);
362
363             if ((n = obt_xml_find_node(app->children, "maximized")))
364                 if (!obt_xml_node_contains(n, "default")) {
365                     gchar *s = obt_xml_node_string(n);
366                     if (!g_ascii_strcasecmp(s, "horizontal")) {
367                         settings->max_horz = TRUE;
368                         settings->max_vert = FALSE;
369                     } else if (!g_ascii_strcasecmp(s, "vertical")) {
370                         settings->max_horz = FALSE;
371                         settings->max_vert = TRUE;
372                     } else
373                         settings->max_horz = settings->max_vert =
374                             obt_xml_node_bool(n);
375                     g_free(s);
376                 }
377
378             config_per_app_settings = g_slist_append(config_per_app_settings,
379                                                      (gpointer) settings);
380             g_free(name);
381             g_free(class);
382             g_free(role);
383             g_free(title);
384             g_free(type_str);
385             name = class = role = title = type_str = NULL;
386         }
387
388         app = obt_xml_find_node(app->next, "application");
389     }
390 }
391
392 /*
393
394 <keybind key="C-x">
395   <action name="ChangeDesktop">
396     <desktop>3</desktop>
397   </action>
398 </keybind>
399
400 */
401
402 static void parse_key(xmlNodePtr node, GList *keylist)
403 {
404     gchar *keystring, **keys, **key;
405     xmlNodePtr n;
406     gboolean is_chroot = FALSE;
407
408     if (!obt_xml_attr_string(node, "key", &keystring))
409         return;
410
411     obt_xml_attr_bool(node, "chroot", &is_chroot);
412
413     keys = g_strsplit(keystring, " ", 0);
414     for (key = keys; *key; ++key) {
415         keylist = g_list_append(keylist, *key);
416
417         if ((n = obt_xml_find_node(node->children, "keybind"))) {
418             while (n) {
419                 parse_key(n, keylist);
420                 n = obt_xml_find_node(n->next, "keybind");
421             }
422         }
423         else if ((n = obt_xml_find_node(node->children, "action"))) {
424             while (n) {
425                 ObActionParser *p;
426                 ObActionList *actions;
427                 xmlChar *c;
428
429                 c = xmlNodeGetContent(node);
430                 p = action_parser_new();
431                 actions = action_parser_read_string(p, (gchar*)c);
432                 xmlFree(c);
433                 action_parser_unref(p);
434
435                 if (actions)
436                     keyboard_bind(keylist, actions);
437
438                 action_list_unref(actions);
439                 n = obt_xml_find_node(n->next, "action");
440             }
441         }
442
443
444         if (is_chroot)
445             keyboard_chroot(keylist);
446         keylist = g_list_delete_link(keylist, g_list_last(keylist));
447     }
448
449     g_strfreev(keys);
450     g_free(keystring);
451 }
452
453 static void parse_keyboard(xmlNodePtr node, gpointer d)
454 {
455     xmlNodePtr n;
456     gchar *key;
457
458     keyboard_unbind_all();
459
460     if ((n = obt_xml_find_node(node->children, "chainQuitKey"))) {
461         key = obt_xml_node_string(n);
462         translate_key(key, &config_keyboard_reset_state,
463                       &config_keyboard_reset_keycode);
464         g_free(key);
465     }
466
467     if ((n = obt_xml_find_node(node->children, "keybind")))
468         while (n) {
469             parse_key(n, NULL);
470             n = obt_xml_find_node(n->next, "keybind");
471         }
472 }
473
474 /*
475
476 <context name="Titlebar">
477   <mousebind button="Left" action="Press">
478     <action name="Raise"></action>
479   </mousebind>
480 </context>
481
482 */
483
484 static void parse_mouse(xmlNodePtr node, gpointer d)
485 {
486     xmlNodePtr n, nbut, nact;
487     gchar *buttonstr;
488     gchar *cxstr;
489     ObMouseAction mact;
490
491     mouse_unbind_all();
492
493     node = node->children;
494
495     if ((n = obt_xml_find_node(node, "dragThreshold")))
496         config_mouse_threshold = obt_xml_node_int(n);
497     if ((n = obt_xml_find_node(node, "doubleClickTime")))
498         config_mouse_dclicktime = obt_xml_node_int(n);
499     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
500         config_mouse_screenedgetime = obt_xml_node_int(n);
501         /* minimum value of 25 for this property, when it is 1 and you hit the
502            edge it basically never stops */
503         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
504             config_mouse_screenedgetime = 25;
505     }
506     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
507         config_mouse_screenedgewarp = obt_xml_node_bool(n);
508
509     n = obt_xml_find_node(node, "context");
510     while (n) {
511         gchar *modcxstr;
512         ObFrameContext cx;
513
514         if (!obt_xml_attr_string(n, "name", &cxstr))
515             goto next_n;
516
517         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
518         while (frame_next_context_from_string(modcxstr, &cx)) {
519             if (!cx) {
520                 gchar *s = strchr(modcxstr, ' ');
521                 if (s) {
522                     *s = '\0';
523                     g_message(_("Invalid context \"%s\" in mouse binding"),
524                               modcxstr);
525                     *s = ' ';
526                 }
527                 continue;
528             }
529
530             nbut = obt_xml_find_node(n->children, "mousebind");
531             while (nbut) {
532                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
533                     goto next_nbut;
534                 if (obt_xml_attr_contains(nbut, "action", "press"))
535                     mact = OB_MOUSE_ACTION_PRESS;
536                 else if (obt_xml_attr_contains(nbut, "action", "release"))
537                     mact = OB_MOUSE_ACTION_RELEASE;
538                 else if (obt_xml_attr_contains(nbut, "action", "click"))
539                     mact = OB_MOUSE_ACTION_CLICK;
540                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
541                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
542                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
543                     mact = OB_MOUSE_ACTION_MOTION;
544                 else
545                     goto next_nbut;
546
547                 nact = obt_xml_find_node(nbut->children, "action");
548                 while (nact) {
549                     ObActionList *actions;
550                     ObActionParser *p;
551                     xmlChar *c;
552
553                     c = xmlNodeGetContent(nact);
554                     p = action_parser_new();
555                     if ((actions = action_parser_read_string(p, (gchar*)c)))
556                         mouse_bind(buttonstr, cx, mact, actions);
557                     nact = obt_xml_find_node(nact->next, "action");
558                     action_list_unref(actions);
559                     xmlFree(c);
560                     action_parser_unref(p);
561                 }
562             g_free(buttonstr);
563             next_nbut:
564             nbut = obt_xml_find_node(nbut->next, "mousebind");
565             }
566         }
567         g_free(modcxstr);
568         g_free(cxstr);
569     next_n:
570         n = obt_xml_find_node(n->next, "context");
571     }
572 }
573
574 static void parse_focus(xmlNodePtr node, gpointer d)
575 {
576     xmlNodePtr n;
577
578     node = node->children;
579
580     if ((n = obt_xml_find_node(node, "focusNew")))
581         config_focus_new = obt_xml_node_bool(n);
582     if ((n = obt_xml_find_node(node, "followMouse")))
583         config_focus_follow = obt_xml_node_bool(n);
584     if ((n = obt_xml_find_node(node, "focusDelay")))
585         config_focus_delay = obt_xml_node_int(n);
586     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
587         config_focus_raise = obt_xml_node_bool(n);
588     if ((n = obt_xml_find_node(node, "focusLast")))
589         config_focus_last = obt_xml_node_bool(n);
590     if ((n = obt_xml_find_node(node, "underMouse")))
591         config_focus_under_mouse = obt_xml_node_bool(n);
592     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
593         config_unfocus_leave = obt_xml_node_bool(n);
594 }
595
596 static void parse_placement(xmlNodePtr node, gpointer d)
597 {
598     xmlNodePtr n;
599
600     node = node->children;
601
602     if ((n = obt_xml_find_node(node, "policy")))
603         if (obt_xml_node_contains(n, "UnderMouse"))
604             config_place_policy = OB_PLACE_POLICY_MOUSE;
605     if ((n = obt_xml_find_node(node, "center")))
606         config_place_center = obt_xml_node_bool(n);
607     if ((n = obt_xml_find_node(node, "monitor"))) {
608         if (obt_xml_node_contains(n, "active"))
609             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
610         else if (obt_xml_node_contains(n, "mouse"))
611             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
612         else if (obt_xml_node_contains(n, "any"))
613             config_place_monitor = OB_PLACE_MONITOR_ANY;
614     }
615     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
616         config_primary_monitor_index = obt_xml_node_int(n);
617         if (!config_primary_monitor_index) {
618             if (obt_xml_node_contains(n, "mouse"))
619                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
620         }
621     }
622 }
623
624 static void parse_margins(xmlNodePtr node, gpointer d)
625 {
626     xmlNodePtr n;
627
628     node = node->children;
629
630     if ((n = obt_xml_find_node(node, "top")))
631         config_margins.top = MAX(0, obt_xml_node_int(n));
632     if ((n = obt_xml_find_node(node, "left")))
633         config_margins.left = MAX(0, obt_xml_node_int(n));
634     if ((n = obt_xml_find_node(node, "right")))
635         config_margins.right = MAX(0, obt_xml_node_int(n));
636     if ((n = obt_xml_find_node(node, "bottom")))
637         config_margins.bottom = MAX(0, obt_xml_node_int(n));
638 }
639
640 static void parse_theme(xmlNodePtr node, gpointer d)
641 {
642     xmlNodePtr n;
643
644     node = node->children;
645
646     if ((n = obt_xml_find_node(node, "name"))) {
647         gchar *c;
648
649         g_free(config_theme);
650         c = obt_xml_node_string(n);
651         config_theme = obt_paths_expand_tilde(c);
652         g_free(c);
653     }
654     if ((n = obt_xml_find_node(node, "titleLayout"))) {
655         gchar *c, *d;
656
657         g_free(config_title_layout);
658         config_title_layout = obt_xml_node_string(n);
659
660         /* replace duplicates with spaces */
661         for (c = config_title_layout; *c != '\0'; ++c)
662             for (d = c+1; *d != '\0'; ++d)
663                 if (*c == *d) *d = ' ';
664     }
665     if ((n = obt_xml_find_node(node, "keepBorder")))
666         config_theme_keepborder = obt_xml_node_bool(n);
667     if ((n = obt_xml_find_node(node, "animateIconify")))
668         config_animate_iconify = obt_xml_node_bool(n);
669     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
670         config_theme_window_list_icon_size = obt_xml_node_int(n);
671         if (config_theme_window_list_icon_size < 16)
672             config_theme_window_list_icon_size = 16;
673         else if (config_theme_window_list_icon_size > 96)
674             config_theme_window_list_icon_size = 96;
675     }
676
677     n = obt_xml_find_node(node, "font");
678     while (n) {
679         xmlNodePtr   fnode;
680         RrFont     **font;
681         gchar       *name = g_strdup(RrDefaultFontFamily);
682         gint         size = RrDefaultFontSize;
683         RrFontWeight weight = RrDefaultFontWeight;
684         RrFontSlant  slant = RrDefaultFontSlant;
685
686         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
687             font = &config_font_activewindow;
688         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
689             font = &config_font_inactivewindow;
690         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
691             font = &config_font_menutitle;
692         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
693             font = &config_font_menuitem;
694         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
695             font = &config_font_activeosd;
696         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
697             font = &config_font_activeosd;
698         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
699             font = &config_font_inactiveosd;
700         else
701             goto next_font;
702
703         if ((fnode = obt_xml_find_node(n->children, "name"))) {
704             g_free(name);
705             name = obt_xml_node_string(fnode);
706         }
707         if ((fnode = obt_xml_find_node(n->children, "size"))) {
708             int s = obt_xml_node_int(fnode);
709             if (s > 0) size = s;
710         }
711         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
712             gchar *w = obt_xml_node_string(fnode);
713             if (!g_ascii_strcasecmp(w, "Bold"))
714                 weight = RR_FONTWEIGHT_BOLD;
715             g_free(w);
716         }
717         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
718             gchar *s = obt_xml_node_string(fnode);
719             if (!g_ascii_strcasecmp(s, "Italic"))
720                 slant = RR_FONTSLANT_ITALIC;
721             if (!g_ascii_strcasecmp(s, "Oblique"))
722                 slant = RR_FONTSLANT_OBLIQUE;
723             g_free(s);
724         }
725
726         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
727         g_free(name);
728     next_font:
729         n = obt_xml_find_node(n->next, "font");
730     }
731 }
732
733 static void parse_desktops(xmlNodePtr node, gpointer d)
734 {
735     xmlNodePtr n;
736
737     node = node->children;
738
739     if ((n = obt_xml_find_node(node, "number"))) {
740         gint d = obt_xml_node_int(n);
741         if (d > 0)
742             config_desktops_num = (unsigned) d;
743     }
744     if ((n = obt_xml_find_node(node, "firstdesk"))) {
745         gint d = obt_xml_node_int(n);
746         if (d > 0)
747             config_screen_firstdesk = (unsigned) d;
748     }
749     if ((n = obt_xml_find_node(node, "names"))) {
750         GSList *it;
751         xmlNodePtr nname;
752
753         for (it = config_desktops_names; it; it = it->next)
754             g_free(it->data);
755         g_slist_free(config_desktops_names);
756         config_desktops_names = NULL;
757
758         nname = obt_xml_find_node(n->children, "name");
759         while (nname) {
760             config_desktops_names =
761                 g_slist_append(config_desktops_names,
762                                obt_xml_node_string(nname));
763             nname = obt_xml_find_node(nname->next, "name");
764         }
765     }
766     if ((n = obt_xml_find_node(node, "popupTime")))
767         config_desktop_popup_time = obt_xml_node_int(n);
768 }
769
770 static void parse_resize(xmlNodePtr node, gpointer d)
771 {
772     xmlNodePtr n;
773
774     node = node->children;
775
776     if ((n = obt_xml_find_node(node, "drawContents")))
777         config_resize_redraw = obt_xml_node_bool(n);
778     if ((n = obt_xml_find_node(node, "popupShow"))) {
779         config_resize_popup_show = obt_xml_node_int(n);
780         if (obt_xml_node_contains(n, "Always"))
781             config_resize_popup_show = 2;
782         else if (obt_xml_node_contains(n, "Never"))
783             config_resize_popup_show = 0;
784         else if (obt_xml_node_contains(n, "Nonpixel"))
785             config_resize_popup_show = 1;
786     }
787     if ((n = obt_xml_find_node(node, "popupPosition"))) {
788         if (obt_xml_node_contains(n, "Top"))
789             config_resize_popup_pos = OB_RESIZE_POS_TOP;
790         else if (obt_xml_node_contains(n, "Center"))
791             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
792         else if (obt_xml_node_contains(n, "Fixed")) {
793             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
794
795             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
796                 xmlNodePtr n2;
797
798                 if ((n2 = obt_xml_find_node(n->children, "x")))
799                     config_parse_gravity_coord(n2,
800                                                &config_resize_popup_fixed.x);
801                 if ((n2 = obt_xml_find_node(n->children, "y")))
802                     config_parse_gravity_coord(n2,
803                                                &config_resize_popup_fixed.y);
804
805                 config_resize_popup_fixed.x.pos =
806                     MAX(config_resize_popup_fixed.x.pos, 0);
807                 config_resize_popup_fixed.y.pos =
808                     MAX(config_resize_popup_fixed.y.pos, 0);
809             }
810         }
811     }
812 }
813
814 static void parse_dock(xmlNodePtr node, gpointer d)
815 {
816     xmlNodePtr n;
817
818     node = node->children;
819
820     if ((n = obt_xml_find_node(node, "position"))) {
821         if (obt_xml_node_contains(n, "TopLeft"))
822             config_dock_floating = FALSE,
823             config_dock_pos = OB_DIRECTION_NORTHWEST;
824         else if (obt_xml_node_contains(n, "Top"))
825             config_dock_floating = FALSE,
826             config_dock_pos = OB_DIRECTION_NORTH;
827         else if (obt_xml_node_contains(n, "TopRight"))
828             config_dock_floating = FALSE,
829             config_dock_pos = OB_DIRECTION_NORTHEAST;
830         else if (obt_xml_node_contains(n, "Right"))
831             config_dock_floating = FALSE,
832             config_dock_pos = OB_DIRECTION_EAST;
833         else if (obt_xml_node_contains(n, "BottomRight"))
834             config_dock_floating = FALSE,
835             config_dock_pos = OB_DIRECTION_SOUTHEAST;
836         else if (obt_xml_node_contains(n, "Bottom"))
837             config_dock_floating = FALSE,
838             config_dock_pos = OB_DIRECTION_SOUTH;
839         else if (obt_xml_node_contains(n, "BottomLeft"))
840             config_dock_floating = FALSE,
841             config_dock_pos = OB_DIRECTION_SOUTHWEST;
842         else if (obt_xml_node_contains(n, "Left"))
843             config_dock_floating = FALSE,
844             config_dock_pos = OB_DIRECTION_WEST;
845         else if (obt_xml_node_contains(n, "Floating"))
846             config_dock_floating = TRUE;
847     }
848     if (config_dock_floating) {
849         if ((n = obt_xml_find_node(node, "floatingX")))
850             config_dock_x = obt_xml_node_int(n);
851         if ((n = obt_xml_find_node(node, "floatingY")))
852             config_dock_y = obt_xml_node_int(n);
853     } else {
854         if ((n = obt_xml_find_node(node, "noStrut")))
855             config_dock_nostrut = obt_xml_node_bool(n);
856     }
857     if ((n = obt_xml_find_node(node, "stacking"))) {
858         if (obt_xml_node_contains(n, "normal"))
859             config_dock_layer = OB_STACKING_LAYER_NORMAL;
860         else if (obt_xml_node_contains(n, "below"))
861             config_dock_layer = OB_STACKING_LAYER_BELOW;
862         else if (obt_xml_node_contains(n, "above"))
863             config_dock_layer = OB_STACKING_LAYER_ABOVE;
864     }
865     if ((n = obt_xml_find_node(node, "direction"))) {
866         if (obt_xml_node_contains(n, "horizontal"))
867             config_dock_orient = OB_ORIENTATION_HORZ;
868         else if (obt_xml_node_contains(n, "vertical"))
869             config_dock_orient = OB_ORIENTATION_VERT;
870     }
871     if ((n = obt_xml_find_node(node, "autoHide")))
872         config_dock_hide = obt_xml_node_bool(n);
873     if ((n = obt_xml_find_node(node, "hideDelay")))
874         config_dock_hide_delay = obt_xml_node_int(n);
875     if ((n = obt_xml_find_node(node, "showDelay")))
876         config_dock_show_delay = obt_xml_node_int(n);
877     if ((n = obt_xml_find_node(node, "moveButton"))) {
878         gchar *str = obt_xml_node_string(n);
879         guint b, s;
880         if (translate_button(str, &s, &b)) {
881             config_dock_app_move_button = b;
882             config_dock_app_move_modifiers = s;
883         } else {
884             g_message(_("Invalid button \"%s\" specified in config file"), str);
885         }
886         g_free(str);
887     }
888 }
889
890 static void parse_menu(xmlNodePtr node, gpointer d)
891 {
892     xmlNodePtr n;
893     node = node->children;
894
895     if ((n = obt_xml_find_node(node, "hideDelay")))
896         config_menu_hide_delay = obt_xml_node_int(n);
897     if ((n = obt_xml_find_node(node, "middle")))
898         config_menu_middle = obt_xml_node_bool(n);
899     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
900         config_submenu_show_delay = obt_xml_node_int(n);
901     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
902         config_submenu_hide_delay = obt_xml_node_int(n);
903     if ((n = obt_xml_find_node(node, "manageDesktops")))
904         config_menu_manage_desktops = obt_xml_node_bool(n);
905     if ((n = obt_xml_find_node(node, "showIcons"))) {
906         config_menu_show_icons = obt_xml_node_bool(n);
907 #ifndef USE_IMLIB2
908         if (config_menu_show_icons)
909             g_message(_("Openbox was compiled without Imlib2 image loading support. Icons in menus will not be loaded."));
910 #endif
911     }
912
913     while ((node = obt_xml_find_node(node, "file"))) {
914             gchar *c = obt_xml_node_string(node);
915             config_menu_files = g_slist_append(config_menu_files,
916                                                obt_paths_expand_tilde(c));
917             g_free(c);
918             node = node->next;
919     }
920 }
921
922 static void parse_resistance(xmlNodePtr node, gpointer d)
923 {
924     xmlNodePtr n;
925
926     node = node->children;
927     if ((n = obt_xml_find_node(node, "strength")))
928         config_resist_win = obt_xml_node_int(n);
929     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
930         config_resist_edge = obt_xml_node_int(n);
931 }
932
933 typedef struct
934 {
935     const gchar *key;
936     const gchar *actiontext;
937 } ObDefKeyBind;
938
939 static void bind_default_keyboard(void)
940 {
941     ObDefKeyBind *it;
942     ObDefKeyBind binds[] = {
943         { "Left",
944           "Execute startupnotify:yes name:Konqueror              \\\n"
945           "  command:(kfmclient openProfile filemanagement)" },
946         { "A-Tab", "NextWindow" },
947         { "S-A-Tab", "PreviousWindow" },
948         { "A-F4", "Close" },
949         { NULL, NULL }
950     };
951     ObActionParser *p;
952
953     p = action_parser_new();
954     for (it = binds; it->key; ++it) {
955         GList *l = g_list_append(NULL, g_strdup(it->key));
956         ObActionList *actions = action_parser_read_string(p, it->actiontext);
957         keyboard_bind(l, actions);
958         action_list_unref(actions);
959     }
960     action_parser_unref(p);
961 }
962
963 typedef struct
964 {
965     const gchar *button;
966     const gchar *context;
967     const ObMouseAction mact;
968     const gchar *actname;
969 } ObDefMouseBind;
970
971 static void bind_default_mouse(void)
972 {
973     ObDefMouseBind *it;
974     ObDefMouseBind binds[] = {
975         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
976         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
977         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
978         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
979         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
980         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
981         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
982         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
983         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
984         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
985         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
986         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
987         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
988         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
989         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
990         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
991         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
992         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
993         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
994         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
995         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
996         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
997         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
998         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
999         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1000         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
1001         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
1002         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
1003         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
1004         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
1005         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
1006         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
1007         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
1008         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
1009         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
1010         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
1011         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1012         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1013         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1014         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1015         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
1016         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
1017         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
1018         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
1019         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
1020         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
1021         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
1022         { NULL, NULL, 0, NULL }
1023     };
1024     ObActionParser *p;
1025     ObActionList *actions;
1026
1027     p = action_parser_new();
1028     for (it = binds; it->button; ++it) {
1029         actions = action_parser_read_string(p, it->actname);
1030         mouse_bind(it->button, frame_context_from_string(it->context),
1031                    it->mact, actions);
1032         action_list_unref(actions);
1033     }
1034     action_parser_unref(p);
1035 }
1036
1037 void config_startup(ObtXmlInst *i)
1038 {
1039     config_focus_new = TRUE;
1040     config_focus_follow = FALSE;
1041     config_focus_delay = 0;
1042     config_focus_raise = FALSE;
1043     config_focus_last = TRUE;
1044     config_focus_under_mouse = FALSE;
1045     config_unfocus_leave = FALSE;
1046
1047     obt_xml_register(i, "focus", parse_focus, NULL);
1048
1049     config_place_policy = OB_PLACE_POLICY_SMART;
1050     config_place_center = TRUE;
1051     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
1052
1053     config_primary_monitor_index = 1;
1054     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1055
1056     obt_xml_register(i, "placement", parse_placement, NULL);
1057
1058     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1059
1060     obt_xml_register(i, "margins", parse_margins, NULL);
1061
1062     config_theme = NULL;
1063
1064     config_animate_iconify = TRUE;
1065     config_title_layout = g_strdup("NLIMC");
1066     config_theme_keepborder = TRUE;
1067     config_theme_window_list_icon_size = 36;
1068
1069     config_font_activewindow = NULL;
1070     config_font_inactivewindow = NULL;
1071     config_font_menuitem = NULL;
1072     config_font_menutitle = NULL;
1073     config_font_activeosd = NULL;
1074     config_font_inactiveosd = NULL;
1075
1076     obt_xml_register(i, "theme", parse_theme, NULL);
1077
1078     config_desktops_num = 4;
1079     config_screen_firstdesk = 1;
1080     config_desktops_names = NULL;
1081     config_desktop_popup_time = 875;
1082
1083     obt_xml_register(i, "desktops", parse_desktops, NULL);
1084
1085     config_resize_redraw = TRUE;
1086     config_resize_popup_show = 1; /* nonpixel increments */
1087     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1088     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1089     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1090
1091     obt_xml_register(i, "resize", parse_resize, NULL);
1092
1093     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1094     config_dock_pos = OB_DIRECTION_NORTHEAST;
1095     config_dock_floating = FALSE;
1096     config_dock_nostrut = FALSE;
1097     config_dock_x = 0;
1098     config_dock_y = 0;
1099     config_dock_orient = OB_ORIENTATION_VERT;
1100     config_dock_hide = FALSE;
1101     config_dock_hide_delay = 300;
1102     config_dock_show_delay = 300;
1103     config_dock_app_move_button = 2; /* middle */
1104     config_dock_app_move_modifiers = 0;
1105
1106     obt_xml_register(i, "dock", parse_dock, NULL);
1107
1108     translate_key("C-g", &config_keyboard_reset_state,
1109                   &config_keyboard_reset_keycode);
1110
1111     bind_default_keyboard();
1112
1113     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1114
1115     config_mouse_threshold = 8;
1116     config_mouse_dclicktime = 500;
1117     config_mouse_screenedgetime = 400;
1118     config_mouse_screenedgewarp = FALSE;
1119
1120     bind_default_mouse();
1121
1122     obt_xml_register(i, "mouse", parse_mouse, NULL);
1123
1124     config_resist_win = 10;
1125     config_resist_edge = 20;
1126
1127     obt_xml_register(i, "resistance", parse_resistance, NULL);
1128
1129     config_menu_hide_delay = 250;
1130     config_menu_middle = FALSE;
1131     config_submenu_show_delay = 100;
1132     config_submenu_hide_delay = 400;
1133     config_menu_manage_desktops = TRUE;
1134     config_menu_files = NULL;
1135     config_menu_show_icons = TRUE;
1136
1137     obt_xml_register(i, "menu", parse_menu, NULL);
1138
1139     config_per_app_settings = NULL;
1140
1141     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1142 }
1143
1144 void config_shutdown(void)
1145 {
1146     GSList *it;
1147
1148     g_free(config_theme);
1149
1150     g_free(config_title_layout);
1151
1152     RrFontClose(config_font_activewindow);
1153     RrFontClose(config_font_inactivewindow);
1154     RrFontClose(config_font_menuitem);
1155     RrFontClose(config_font_menutitle);
1156     RrFontClose(config_font_activeosd);
1157     RrFontClose(config_font_inactiveosd);
1158
1159     for (it = config_desktops_names; it; it = g_slist_next(it))
1160         g_free(it->data);
1161     g_slist_free(config_desktops_names);
1162
1163     for (it = config_menu_files; it; it = g_slist_next(it))
1164         g_free(it->data);
1165     g_slist_free(config_menu_files);
1166
1167     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1168         ObAppSettings *itd = (ObAppSettings *)it->data;
1169         if (itd->name)  g_pattern_spec_free(itd->name);
1170         if (itd->role)  g_pattern_spec_free(itd->role);
1171         if (itd->title) g_pattern_spec_free(itd->title);
1172         if (itd->class) g_pattern_spec_free(itd->class);
1173         g_slice_free(ObAppSettings, it->data);
1174     }
1175     g_slist_free(config_per_app_settings);
1176 }