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