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