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