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