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