Merge branch 'backport' into work
[mikachu/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 "actions.h"
24 #include "translate.h"
25 #include "client.h"
26 #include "screen.h"
27 #include "openbox.h"
28 #include "gettext.h"
29 #include "obt/paths.h"
30
31 gboolean config_focus_new;
32 gboolean config_focus_follow;
33 guint    config_focus_delay;
34 gboolean config_focus_raise;
35 gboolean config_focus_last;
36 gboolean config_focus_under_mouse;
37
38 ObPlacePolicy  config_place_policy;
39 gboolean       config_place_center;
40 ObPlaceMonitor config_place_monitor;
41
42 StrutPartial config_margins;
43
44 gchar   *config_theme;
45 gboolean config_theme_keepborder;
46
47 gchar   *config_title_layout;
48
49 gboolean config_animate_iconify;
50
51 RrFont *config_font_activewindow;
52 RrFont *config_font_inactivewindow;
53 RrFont *config_font_menuitem;
54 RrFont *config_font_menutitle;
55 RrFont *config_font_osd;
56
57 guint   config_desktops_num;
58 GSList *config_desktops_names;
59 guint   config_screen_firstdesk;
60 guint   config_desktop_popup_time;
61
62 gboolean         config_resize_redraw;
63 gint             config_resize_popup_show;
64 ObResizePopupPos config_resize_popup_pos;
65 GravityPoint     config_resize_popup_fixed;
66
67 ObStackingLayer config_dock_layer;
68 gboolean        config_dock_floating;
69 gboolean        config_dock_nostrut;
70 ObDirection     config_dock_pos;
71 gint            config_dock_x;
72 gint            config_dock_y;
73 ObOrientation   config_dock_orient;
74 gboolean        config_dock_hide;
75 guint           config_dock_hide_delay;
76 guint           config_dock_show_delay;
77 guint           config_dock_app_move_button;
78 guint           config_dock_app_move_modifiers;
79
80 guint config_keyboard_reset_keycode;
81 guint config_keyboard_reset_state;
82
83 gint config_mouse_threshold;
84 gint config_mouse_dclicktime;
85 gint config_mouse_screenedgetime;
86
87 guint    config_menu_hide_delay;
88 gboolean config_menu_middle;
89 guint    config_submenu_show_delay;
90 gboolean config_menu_client_list_icons;
91 gboolean config_menu_manage_desktops;
92
93 GSList *config_menu_files;
94
95 gint     config_resist_win;
96 gint     config_resist_edge;
97
98 GSList *config_per_app_settings;
99
100 ObAppSettings* config_create_app_settings(void)
101 {
102     ObAppSettings *settings = g_new0(ObAppSettings, 1);
103     settings->decor = -1;
104     settings->shade = -1;
105     settings->monitor = -1;
106     settings->focus = -1;
107     settings->desktop = 0;
108     settings->layer = -2;
109     settings->iconic = -1;
110     settings->skip_pager = -1;
111     settings->skip_taskbar = -1;
112     settings->fullscreen = -1;
113     settings->max_horz = -1;
114     settings->max_vert = -1;
115     return settings;
116 }
117
118 #define copy_if(setting, default) \
119   if (src->setting != default) dst->setting = src->setting
120 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
121                                            ObAppSettings *dst)
122 {
123     g_assert(src != NULL);
124     g_assert(dst != NULL);
125
126     copy_if(decor, -1);
127     copy_if(shade, -1);
128     copy_if(focus, -1);
129     copy_if(desktop, 0);
130     copy_if(layer, -2);
131     copy_if(iconic, -1);
132     copy_if(skip_pager, -1);
133     copy_if(skip_taskbar, -1);
134     copy_if(fullscreen, -1);
135     copy_if(max_horz, -1);
136     copy_if(max_vert, -1);
137
138     if (src->pos_given) {
139         dst->pos_given = TRUE;
140         dst->pos_force = src->pos_force;
141         dst->position = src->position;
142         dst->monitor = src->monitor;
143     }
144 }
145
146 static void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
147 {
148     gchar *s = obt_parse_node_string(node);
149     if (!g_ascii_strcasecmp(s, "center"))
150         c->center = TRUE;
151     else {
152         if (s[0] == '-')
153             c->opposite = TRUE;
154         if (s[0] == '-' || s[0] == '+')
155             c->pos = atoi(s+1);
156         else
157             c->pos = atoi(s);
158     }
159     g_free(s);
160 }
161
162 /*
163   <applications>
164     <application name="aterm">
165       <decor>false</decor>
166     </application>
167     <application name="Rhythmbox">
168       <layer>above</layer>
169       <position>
170         <x>700</x>
171         <y>0</y>
172         <monitor>1</monitor>
173       </position>
174       .. there is a lot more settings available
175     </application>
176   </applications>
177 */
178
179 /* Manages settings for individual applications.
180    Some notes: monitor is the screen number in a multi monitor
181    (Xinerama) setup (starting from 0) or mouse, meaning the
182    monitor the pointer is on. Default: mouse.
183    Layer can be three values, above (Always on top), below
184    (Always on bottom) and everything else (normal behaviour).
185    Positions can be an integer value or center, which will
186    center the window in the specified axis. Position is within
187    the monitor, so <position><x>center</x></position><monitor>2</monitor>
188    will center the window on the second monitor.
189 */
190 static void parse_per_app_settings(xmlNodePtr node, gpointer d)
191 {
192     xmlNodePtr app = obt_parse_find_node(node->children, "application");
193     gchar *name = NULL, *class = NULL, *role = NULL;
194     gboolean name_set, class_set;
195     gboolean x_pos_given;
196
197     while (app) {
198         name_set = class_set = x_pos_given = FALSE;
199
200         class_set = obt_parse_attr_string(app, "class", &class);
201         name_set = obt_parse_attr_string(app, "name", &name);
202         if (class_set || name_set) {
203             xmlNodePtr n, c;
204             ObAppSettings *settings = config_create_app_settings();;
205
206             if (name_set)
207                 settings->name = g_pattern_spec_new(name);
208
209             if (class_set)
210                 settings->class = g_pattern_spec_new(class);
211
212             if (obt_parse_attr_string(app, "role", &role))
213                 settings->role = g_pattern_spec_new(role);
214
215             if ((n = obt_parse_find_node(app->children, "decor")))
216                 if (!obt_parse_node_contains(n, "default"))
217                     settings->decor = obt_parse_node_bool(n);
218
219             if ((n = obt_parse_find_node(app->children, "shade")))
220                 if (!obt_parse_node_contains(n, "default"))
221                     settings->shade = obt_parse_node_bool(n);
222
223             if ((n = obt_parse_find_node(app->children, "position"))) {
224                 if ((c = obt_parse_find_node(n->children, "x")))
225                     if (!obt_parse_node_contains(c, "default")) {
226                         config_parse_gravity_coord(c, &settings->position.x);
227                         x_pos_given = TRUE;
228                     }
229
230                 if (x_pos_given && (c = obt_parse_find_node(n->children, "y")))
231                     if (!obt_parse_node_contains(c, "default")) {
232                         config_parse_gravity_coord(c, &settings->position.y);
233                         settings->pos_given = TRUE;
234                     }
235
236                 if (settings->pos_given &&
237                     (c = obt_parse_find_node(n->children, "monitor")))
238                     if (!obt_parse_node_contains(c, "default")) {
239                         gchar *s = obt_parse_node_string(c);
240                         if (!g_ascii_strcasecmp(s, "mouse"))
241                             settings->monitor = 0;
242                         else
243                             settings->monitor = obt_parse_node_int(c) + 1;
244                         g_free(s);
245                     }
246
247                 obt_parse_attr_bool(n, "force", &settings->pos_force);
248             }
249
250             if ((n = obt_parse_find_node(app->children, "focus")))
251                 if (!obt_parse_node_contains(n, "default"))
252                     settings->focus = obt_parse_node_bool(n);
253
254             if ((n = obt_parse_find_node(app->children, "desktop"))) {
255                 if (!obt_parse_node_contains(n, "default")) {
256                     gchar *s = obt_parse_node_string(n);
257                     if (!g_ascii_strcasecmp(s, "all"))
258                         settings->desktop = DESKTOP_ALL;
259                     else {
260                         gint i = obt_parse_node_int(n);
261                         if (i > 0)
262                             settings->desktop = i;
263                     }
264                     g_free(s);
265                 }
266             }
267
268             if ((n = obt_parse_find_node(app->children, "layer")))
269                 if (!obt_parse_node_contains(n, "default")) {
270                     gchar *s = obt_parse_node_string(n);
271                     if (!g_ascii_strcasecmp(s, "above"))
272                         settings->layer = 1;
273                     else if (!g_ascii_strcasecmp(s, "below"))
274                         settings->layer = -1;
275                     else
276                         settings->layer = 0;
277                     g_free(s);
278                 }
279
280             if ((n = obt_parse_find_node(app->children, "iconic")))
281                 if (!obt_parse_node_contains(n, "default"))
282                     settings->iconic = obt_parse_node_bool(n);
283
284             if ((n = obt_parse_find_node(app->children, "skip_pager")))
285                 if (!obt_parse_node_contains(n, "default"))
286                     settings->skip_pager = obt_parse_node_bool(n);
287
288             if ((n = obt_parse_find_node(app->children, "skip_taskbar")))
289                 if (!obt_parse_node_contains(n, "default"))
290                     settings->skip_taskbar = obt_parse_node_bool(n);
291
292             if ((n = obt_parse_find_node(app->children, "fullscreen")))
293                 if (!obt_parse_node_contains(n, "default"))
294                     settings->fullscreen = obt_parse_node_bool(n);
295
296             if ((n = obt_parse_find_node(app->children, "maximized")))
297                 if (!obt_parse_node_contains(n, "default")) {
298                     gchar *s = obt_parse_node_string(n);
299                     if (!g_ascii_strcasecmp(s, "horizontal")) {
300                         settings->max_horz = TRUE;
301                         settings->max_vert = FALSE;
302                     } else if (!g_ascii_strcasecmp(s, "vertical")) {
303                         settings->max_horz = FALSE;
304                         settings->max_vert = TRUE;
305                     } else
306                         settings->max_horz = settings->max_vert =
307                             obt_parse_node_bool(n);
308                     g_free(s);
309                 }
310
311             config_per_app_settings = g_slist_append(config_per_app_settings,
312                                               (gpointer) settings);
313             g_free(name);
314             g_free(class);
315             g_free(role);
316             name = class = role = NULL;
317         }
318
319         app = obt_parse_find_node(app->next, "application");
320     }
321 }
322
323 /*
324
325 <keybind key="C-x">
326   <action name="ChangeDesktop">
327     <desktop>3</desktop>
328   </action>
329 </keybind>
330
331 */
332
333 static void parse_key(xmlNodePtr node, GList *keylist)
334 {
335     gchar *key;
336     xmlNodePtr n;
337     gboolean is_chroot = FALSE;
338
339     if (!obt_parse_attr_string(node, "key", &key))
340         return;
341
342     obt_parse_attr_bool(node, "chroot", &is_chroot);
343
344     keylist = g_list_append(keylist, key);
345
346     if ((n = obt_parse_find_node(node->children, "keybind"))) {
347         while (n) {
348             parse_key(n, keylist);
349             n = obt_parse_find_node(n->next, "keybind");
350         }
351     }
352     else if ((n = obt_parse_find_node(node->children, "action"))) {
353         while (n) {
354             ObActionsAct *action;
355
356             action = actions_parse(n);
357             if (action)
358                 keyboard_bind(keylist, action);
359             n = obt_parse_find_node(n->next, "action");
360         }
361     }
362
363     if (is_chroot)
364         keyboard_chroot(keylist);
365
366     g_free(key);
367     keylist = g_list_delete_link(keylist, g_list_last(keylist));
368 }
369
370 static void parse_keyboard(xmlNodePtr node, gpointer d)
371 {
372     xmlNodePtr n;
373     gchar *key;
374
375     keyboard_unbind_all();
376
377     if ((n = obt_parse_find_node(node->children, "chainQuitKey"))) {
378         key = obt_parse_node_string(n);
379         translate_key(key, &config_keyboard_reset_state,
380                       &config_keyboard_reset_keycode);
381         g_free(key);
382     }
383
384     if ((n = obt_parse_find_node(node->children, "keybind")))
385         while (n) {
386             parse_key(n, NULL);
387             n = obt_parse_find_node(n->next, "keybind");
388         }
389 }
390
391 /*
392
393 <context name="Titlebar">
394   <mousebind button="Left" action="Press">
395     <action name="Raise"></action>
396   </mousebind>
397 </context>
398
399 */
400
401 static void parse_mouse(xmlNodePtr node, gpointer d)
402 {
403     xmlNodePtr n, nbut, nact;
404     gchar *buttonstr;
405     gchar *contextstr;
406     ObMouseAction mact;
407
408     mouse_unbind_all();
409
410     node = node->children;
411
412     if ((n = obt_parse_find_node(node, "dragThreshold")))
413         config_mouse_threshold = obt_parse_node_int(n);
414     if ((n = obt_parse_find_node(node, "doubleClickTime")))
415         config_mouse_dclicktime = obt_parse_node_int(n);
416     if ((n = obt_parse_find_node(node, "screenEdgeWarpTime")))
417         config_mouse_screenedgetime = obt_parse_node_int(n);
418
419     n = obt_parse_find_node(node, "context");
420     while (n) {
421         if (!obt_parse_attr_string(n, "name", &contextstr))
422             goto next_n;
423         nbut = obt_parse_find_node(n->children, "mousebind");
424         while (nbut) {
425             if (!obt_parse_attr_string(nbut, "button", &buttonstr))
426                 goto next_nbut;
427             if (obt_parse_attr_contains(nbut, "action", "press")) {
428                 mact = OB_MOUSE_ACTION_PRESS;
429             } else if (obt_parse_attr_contains(nbut, "action", "release")) {
430                 mact = OB_MOUSE_ACTION_RELEASE;
431             } else if (obt_parse_attr_contains(nbut, "action", "click")) {
432                 mact = OB_MOUSE_ACTION_CLICK;
433             } else if (obt_parse_attr_contains(nbut, "action","doubleclick")) {
434                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
435             } else if (obt_parse_attr_contains(nbut, "action", "drag")) {
436                 mact = OB_MOUSE_ACTION_MOTION;
437             } else
438                 goto next_nbut;
439             nact = obt_parse_find_node(nbut->children, "action");
440             while (nact) {
441                 ObActionsAct *action;
442
443                 if ((action = actions_parse(nact)))
444                     mouse_bind(buttonstr, contextstr, mact, action);
445                 nact = obt_parse_find_node(nact->next, "action");
446             }
447             g_free(buttonstr);
448         next_nbut:
449             nbut = obt_parse_find_node(nbut->next, "mousebind");
450         }
451         g_free(contextstr);
452     next_n:
453         n = obt_parse_find_node(n->next, "context");
454     }
455 }
456
457 static void parse_focus(xmlNodePtr node, gpointer d)
458 {
459     xmlNodePtr n;
460
461     node = node->children;
462
463     if ((n = obt_parse_find_node(node, "focusNew")))
464         config_focus_new = obt_parse_node_bool(n);
465     if ((n = obt_parse_find_node(node, "followMouse")))
466         config_focus_follow = obt_parse_node_bool(n);
467     if ((n = obt_parse_find_node(node, "focusDelay")))
468         config_focus_delay = obt_parse_node_int(n);
469     if ((n = obt_parse_find_node(node, "raiseOnFocus")))
470         config_focus_raise = obt_parse_node_bool(n);
471     if ((n = obt_parse_find_node(node, "focusLast")))
472         config_focus_last = obt_parse_node_bool(n);
473     if ((n = obt_parse_find_node(node, "underMouse")))
474         config_focus_under_mouse = obt_parse_node_bool(n);
475 }
476
477 static void parse_placement(xmlNodePtr node, gpointer d)
478 {
479     xmlNodePtr n;
480
481     node = node->children;
482
483     if ((n = obt_parse_find_node(node, "policy")))
484         if (obt_parse_node_contains(n, "UnderMouse"))
485             config_place_policy = OB_PLACE_POLICY_MOUSE;
486     if ((n = obt_parse_find_node(node, "center")))
487         config_place_center = obt_parse_node_bool(n);
488     if ((n = obt_parse_find_node(node, "monitor"))) {
489         if (obt_parse_node_contains(n, "active"))
490             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
491         else if (obt_parse_node_contains(n, "mouse"))
492             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
493     }
494 }
495
496 static void parse_margins(xmlNodePtr node, gpointer d)
497 {
498     xmlNodePtr n;
499
500     node = node->children;
501
502     if ((n = obt_parse_find_node(node, "top")))
503         config_margins.top = MAX(0, obt_parse_node_int(n));
504     if ((n = obt_parse_find_node(node, "left")))
505         config_margins.left = MAX(0, obt_parse_node_int(n));
506     if ((n = obt_parse_find_node(node, "right")))
507         config_margins.right = MAX(0, obt_parse_node_int(n));
508     if ((n = obt_parse_find_node(node, "bottom")))
509         config_margins.bottom = MAX(0, obt_parse_node_int(n));
510 }
511
512 static void parse_theme(xmlNodePtr node, gpointer d)
513 {
514     xmlNodePtr n;
515
516     node = node->children;
517
518     if ((n = obt_parse_find_node(node, "name"))) {
519         gchar *c;
520
521         g_free(config_theme);
522         c = obt_parse_node_string(n);
523         config_theme = obt_paths_expand_tilde(c);
524         g_free(c);
525     }
526     if ((n = obt_parse_find_node(node, "titleLayout"))) {
527         gchar *c, *d;
528
529         g_free(config_title_layout);
530         config_title_layout = obt_parse_node_string(n);
531
532         /* replace duplicates with spaces */
533         for (c = config_title_layout; *c != '\0'; ++c)
534             for (d = c+1; *d != '\0'; ++d)
535                 if (*c == *d) *d = ' ';
536     }
537     if ((n = obt_parse_find_node(node, "keepBorder")))
538         config_theme_keepborder = obt_parse_node_bool(n);
539     if ((n = obt_parse_find_node(node, "animateIconify")))
540         config_animate_iconify = obt_parse_node_bool(n);
541
542     n = obt_parse_find_node(node, "font");
543     while (n) {
544         xmlNodePtr   fnode;
545         RrFont     **font;
546         gchar       *name = g_strdup(RrDefaultFontFamily);
547         gint         size = RrDefaultFontSize;
548         RrFontWeight weight = RrDefaultFontWeight;
549         RrFontSlant  slant = RrDefaultFontSlant;
550
551         if (obt_parse_attr_contains(n, "place", "ActiveWindow"))
552             font = &config_font_activewindow;
553         else if (obt_parse_attr_contains(n, "place", "InactiveWindow"))
554             font = &config_font_inactivewindow;
555         else if (obt_parse_attr_contains(n, "place", "MenuHeader"))
556             font = &config_font_menutitle;
557         else if (obt_parse_attr_contains(n, "place", "MenuItem"))
558             font = &config_font_menuitem;
559         else if (obt_parse_attr_contains(n, "place", "OnScreenDisplay"))
560             font = &config_font_osd;
561         else
562             goto next_font;
563
564         if ((fnode = obt_parse_find_node(n->children, "name"))) {
565             g_free(name);
566             name = obt_parse_node_string(fnode);
567         }
568         if ((fnode = obt_parse_find_node(n->children, "size"))) {
569             int s = obt_parse_node_int(fnode);
570             if (s > 0) size = s;
571         }
572         if ((fnode = obt_parse_find_node(n->children, "weight"))) {
573             gchar *w = obt_parse_node_string(fnode);
574             if (!g_ascii_strcasecmp(w, "Bold"))
575                 weight = RR_FONTWEIGHT_BOLD;
576             g_free(w);
577         }
578         if ((fnode = obt_parse_find_node(n->children, "slant"))) {
579             gchar *s = obt_parse_node_string(fnode);
580             if (!g_ascii_strcasecmp(s, "Italic"))
581                 slant = RR_FONTSLANT_ITALIC;
582             if (!g_ascii_strcasecmp(s, "Oblique"))
583                 slant = RR_FONTSLANT_OBLIQUE;
584             g_free(s);
585         }
586
587         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
588         g_free(name);
589     next_font:
590         n = obt_parse_find_node(n->next, "font");
591     }
592 }
593
594 static void parse_desktops(xmlNodePtr node, gpointer d)
595 {
596     xmlNodePtr n;
597
598     node = node->children;
599
600     if ((n = obt_parse_find_node(node, "number"))) {
601         gint d = obt_parse_node_int(n);
602         if (d > 0)
603             config_desktops_num = (unsigned) d;
604     }
605     if ((n = obt_parse_find_node(node, "firstdesk"))) {
606         gint d = obt_parse_node_int(n);
607         if (d > 0)
608             config_screen_firstdesk = (unsigned) d;
609     }
610     if ((n = obt_parse_find_node(node, "names"))) {
611         GSList *it;
612         xmlNodePtr nname;
613
614         for (it = config_desktops_names; it; it = it->next)
615             g_free(it->data);
616         g_slist_free(config_desktops_names);
617         config_desktops_names = NULL;
618
619         nname = obt_parse_find_node(n->children, "name");
620         while (nname) {
621             config_desktops_names =
622                 g_slist_append(config_desktops_names,
623                                obt_parse_node_string(nname));
624             nname = obt_parse_find_node(nname->next, "name");
625         }
626     }
627     if ((n = obt_parse_find_node(node, "popupTime")))
628         config_desktop_popup_time = obt_parse_node_int(n);
629 }
630
631 static void parse_resize(xmlNodePtr node, gpointer d)
632 {
633     xmlNodePtr n;
634
635     node = node->children;
636
637     if ((n = obt_parse_find_node(node, "drawContents")))
638         config_resize_redraw = obt_parse_node_bool(n);
639     if ((n = obt_parse_find_node(node, "popupShow"))) {
640         config_resize_popup_show = obt_parse_node_int(n);
641         if (obt_parse_node_contains(n, "Always"))
642             config_resize_popup_show = 2;
643         else if (obt_parse_node_contains(n, "Never"))
644             config_resize_popup_show = 0;
645         else if (obt_parse_node_contains(n, "Nonpixel"))
646             config_resize_popup_show = 1;
647     }
648     if ((n = obt_parse_find_node(node, "popupPosition"))) {
649         if (obt_parse_node_contains(n, "Top"))
650             config_resize_popup_pos = OB_RESIZE_POS_TOP;
651         else if (obt_parse_node_contains(n, "Center"))
652             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
653         else if (obt_parse_node_contains(n, "Fixed")) {
654             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
655
656             if ((n = obt_parse_find_node(node, "popupFixedPosition"))) {
657                 xmlNodePtr n2;
658
659                 if ((n2 = obt_parse_find_node(n->children, "x")))
660                     config_parse_gravity_coord(n2,
661                                                &config_resize_popup_fixed.x);
662                 if ((n2 = obt_parse_find_node(n->children, "y")))
663                     config_parse_gravity_coord(n2,
664                                                &config_resize_popup_fixed.y);
665             }
666         }
667     }
668 }
669
670 static void parse_dock(xmlNodePtr node, gpointer d)
671 {
672     xmlNodePtr n;
673
674     node = node->children;
675
676     if ((n = obt_parse_find_node(node, "position"))) {
677         if (obt_parse_node_contains(n, "TopLeft"))
678             config_dock_floating = FALSE,
679             config_dock_pos = OB_DIRECTION_NORTHWEST;
680         else if (obt_parse_node_contains(n, "Top"))
681             config_dock_floating = FALSE,
682             config_dock_pos = OB_DIRECTION_NORTH;
683         else if (obt_parse_node_contains(n, "TopRight"))
684             config_dock_floating = FALSE,
685             config_dock_pos = OB_DIRECTION_NORTHEAST;
686         else if (obt_parse_node_contains(n, "Right"))
687             config_dock_floating = FALSE,
688             config_dock_pos = OB_DIRECTION_EAST;
689         else if (obt_parse_node_contains(n, "BottomRight"))
690             config_dock_floating = FALSE,
691             config_dock_pos = OB_DIRECTION_SOUTHEAST;
692         else if (obt_parse_node_contains(n, "Bottom"))
693             config_dock_floating = FALSE,
694             config_dock_pos = OB_DIRECTION_SOUTH;
695         else if (obt_parse_node_contains(n, "BottomLeft"))
696             config_dock_floating = FALSE,
697             config_dock_pos = OB_DIRECTION_SOUTHWEST;
698         else if (obt_parse_node_contains(n, "Left"))
699             config_dock_floating = FALSE,
700             config_dock_pos = OB_DIRECTION_WEST;
701         else if (obt_parse_node_contains(n, "Floating"))
702             config_dock_floating = TRUE;
703     }
704     if (config_dock_floating) {
705         if ((n = obt_parse_find_node(node, "floatingX")))
706             config_dock_x = obt_parse_node_int(n);
707         if ((n = obt_parse_find_node(node, "floatingY")))
708             config_dock_y = obt_parse_node_int(n);
709     } else {
710         if ((n = obt_parse_find_node(node, "noStrut")))
711             config_dock_nostrut = obt_parse_node_bool(n);
712     }
713     if ((n = obt_parse_find_node(node, "stacking"))) {
714         if (obt_parse_node_contains(n, "normal"))
715             config_dock_layer = OB_STACKING_LAYER_NORMAL;
716         else if (obt_parse_node_contains(n, "below"))
717             config_dock_layer = OB_STACKING_LAYER_BELOW;
718         else if (obt_parse_node_contains(n, "above"))
719             config_dock_layer = OB_STACKING_LAYER_ABOVE;
720     }
721     if ((n = obt_parse_find_node(node, "direction"))) {
722         if (obt_parse_node_contains(n, "horizontal"))
723             config_dock_orient = OB_ORIENTATION_HORZ;
724         else if (obt_parse_node_contains(n, "vertical"))
725             config_dock_orient = OB_ORIENTATION_VERT;
726     }
727     if ((n = obt_parse_find_node(node, "autoHide")))
728         config_dock_hide = obt_parse_node_bool(n);
729     if ((n = obt_parse_find_node(node, "hideDelay")))
730         config_dock_hide_delay = obt_parse_node_int(n);
731     if ((n = obt_parse_find_node(node, "showDelay")))
732         config_dock_show_delay = obt_parse_node_int(n);
733     if ((n = obt_parse_find_node(node, "moveButton"))) {
734         gchar *str = obt_parse_node_string(n);
735         guint b, s;
736         if (translate_button(str, &s, &b)) {
737             config_dock_app_move_button = b;
738             config_dock_app_move_modifiers = s;
739         } else {
740             g_message(_("Invalid button '%s' specified in config file"), str);
741         }
742         g_free(str);
743     }
744 }
745
746 static void parse_menu(xmlNodePtr node, gpointer d)
747 {
748     xmlNodePtr n;
749     for (node = node->children; node; node = node->next) {
750         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
751             gchar *c;
752
753             c = obt_parse_node_string(node);
754             config_menu_files = g_slist_append(config_menu_files,
755                                                obt_paths_expand_tilde(c));
756             g_free(c);
757         }
758         if ((n = obt_parse_find_node(node, "hideDelay")))
759             config_menu_hide_delay = obt_parse_node_int(n);
760         if ((n = obt_parse_find_node(node, "middle")))
761             config_menu_middle = obt_parse_node_bool(n);
762         if ((n = obt_parse_find_node(node, "submenuShowDelay")))
763             config_submenu_show_delay = obt_parse_node_int(n);
764         if ((n = obt_parse_find_node(node, "applicationIcons")))
765             config_menu_client_list_icons = obt_parse_node_bool(n);
766         if ((n = obt_parse_find_node(node, "manageDesktops")))
767             config_menu_manage_desktops = obt_parse_node_bool(n);
768     }
769 }
770
771 static void parse_resistance(xmlNodePtr node, gpointer d)
772 {
773     xmlNodePtr n;
774
775     node = node->children;
776     if ((n = obt_parse_find_node(node, "strength")))
777         config_resist_win = obt_parse_node_int(n);
778     if ((n = obt_parse_find_node(node, "screen_edge_strength")))
779         config_resist_edge = obt_parse_node_int(n);
780 }
781
782 typedef struct
783 {
784     const gchar *key;
785     const gchar *actname;
786 } ObDefKeyBind;
787
788 static void bind_default_keyboard(void)
789 {
790     ObDefKeyBind *it;
791     ObDefKeyBind binds[] = {
792         { "A-Tab", "NextWindow" },
793         { "S-A-Tab", "PreviousWindow" },
794         { "A-F4", "Close" },
795         { NULL, NULL }
796     };
797     for (it = binds; it->key; ++it) {
798         GList *l = g_list_append(NULL, g_strdup(it->key));
799         keyboard_bind(l, actions_parse_string(it->actname));
800     }
801 }
802
803 typedef struct
804 {
805     const gchar *button;
806     const gchar *context;
807     const ObMouseAction mact;
808     const gchar *actname;
809 } ObDefMouseBind;
810
811 static void bind_default_mouse(void)
812 {
813     ObDefMouseBind *it;
814     ObDefMouseBind binds[] = {
815         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
816         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
817         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
818         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
819         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
820         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
821         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
822         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
823         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
824         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
825         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
826         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
827         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
828         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
829         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
830         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
831         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
832         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
833         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
834         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
835         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
836         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
837         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
838         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
839         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
840         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
841         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
842         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
843         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
844         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
845         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
846         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
847         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
848         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
849         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
850         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
851         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
852         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
853         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
854         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
855         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
856         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
857         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
858         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
859         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
860         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
861         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
862         { NULL, NULL, 0, NULL }
863     };
864
865     for (it = binds; it->button; ++it)
866         mouse_bind(it->button, it->context, it->mact,
867                    actions_parse_string(it->actname));
868 }
869
870 void config_startup(ObtParseInst *i)
871 {
872     config_focus_new = TRUE;
873     config_focus_follow = FALSE;
874     config_focus_delay = 0;
875     config_focus_raise = FALSE;
876     config_focus_last = TRUE;
877     config_focus_under_mouse = FALSE;
878
879     obt_parse_register(i, "focus", parse_focus, NULL);
880
881     config_place_policy = OB_PLACE_POLICY_SMART;
882     config_place_center = TRUE;
883     config_place_monitor = OB_PLACE_MONITOR_ANY;
884
885     obt_parse_register(i, "placement", parse_placement, NULL);
886
887     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
888
889     obt_parse_register(i, "margins", parse_margins, NULL);
890
891     config_theme = NULL;
892
893     config_animate_iconify = TRUE;
894     config_title_layout = g_strdup("NLIMC");
895     config_theme_keepborder = TRUE;
896
897     config_font_activewindow = NULL;
898     config_font_inactivewindow = NULL;
899     config_font_menuitem = NULL;
900     config_font_menutitle = NULL;
901
902     obt_parse_register(i, "theme", parse_theme, NULL);
903
904     config_desktops_num = 4;
905     config_screen_firstdesk = 1;
906     config_desktops_names = NULL;
907     config_desktop_popup_time = 875;
908
909     obt_parse_register(i, "desktops", parse_desktops, NULL);
910
911     config_resize_redraw = TRUE;
912     config_resize_popup_show = 1; /* nonpixel increments */
913     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
914     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
915     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
916
917     obt_parse_register(i, "resize", parse_resize, NULL);
918
919     config_dock_layer = OB_STACKING_LAYER_ABOVE;
920     config_dock_pos = OB_DIRECTION_NORTHEAST;
921     config_dock_floating = FALSE;
922     config_dock_nostrut = FALSE;
923     config_dock_x = 0;
924     config_dock_y = 0;
925     config_dock_orient = OB_ORIENTATION_VERT;
926     config_dock_hide = FALSE;
927     config_dock_hide_delay = 300;
928     config_dock_show_delay = 300;
929     config_dock_app_move_button = 2; /* middle */
930     config_dock_app_move_modifiers = 0;
931
932     obt_parse_register(i, "dock", parse_dock, NULL);
933
934     translate_key("C-g", &config_keyboard_reset_state,
935                   &config_keyboard_reset_keycode);
936
937     bind_default_keyboard();
938
939     obt_parse_register(i, "keyboard", parse_keyboard, NULL);
940
941     config_mouse_threshold = 8;
942     config_mouse_dclicktime = 200;
943     config_mouse_screenedgetime = 400;
944
945     bind_default_mouse();
946
947     obt_parse_register(i, "mouse", parse_mouse, NULL);
948
949     config_resist_win = 10;
950     config_resist_edge = 20;
951
952     obt_parse_register(i, "resistance", parse_resistance, NULL);
953
954     config_menu_hide_delay = 250;
955     config_menu_middle = FALSE;
956     config_submenu_show_delay = 0;
957     config_menu_client_list_icons = TRUE;
958     config_menu_manage_desktops = TRUE;
959     config_menu_files = NULL;
960
961     obt_parse_register(i, "menu", parse_menu, NULL);
962
963     config_per_app_settings = NULL;
964
965     obt_parse_register(i, "applications", parse_per_app_settings, NULL);
966 }
967
968 void config_shutdown(void)
969 {
970     GSList *it;
971
972     g_free(config_theme);
973
974     g_free(config_title_layout);
975
976     RrFontClose(config_font_activewindow);
977     RrFontClose(config_font_inactivewindow);
978     RrFontClose(config_font_menuitem);
979     RrFontClose(config_font_menutitle);
980     RrFontClose(config_font_osd);
981
982     for (it = config_desktops_names; it; it = g_slist_next(it))
983         g_free(it->data);
984     g_slist_free(config_desktops_names);
985
986     for (it = config_menu_files; it; it = g_slist_next(it))
987         g_free(it->data);
988     g_slist_free(config_menu_files);
989
990     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
991         ObAppSettings *itd = (ObAppSettings *)it->data;
992         if (itd->name)  g_pattern_spec_free(itd->name);
993         if (itd->role)  g_pattern_spec_free(itd->role);
994         if (itd->class) g_pattern_spec_free(itd->class);
995         g_free(it->data);
996     }
997     g_slist_free(config_per_app_settings);
998 }