allow you to force the position of windows with rc.xml's per-app settings with the...
[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 ObPlaceMonitor config_place_monitor;
42
43 StrutPartial config_margins;
44
45 gchar   *config_theme;
46 gboolean config_theme_keepborder;
47
48 gchar   *config_title_layout;
49
50 gboolean config_animate_iconify;
51
52 RrFont *config_font_activewindow;
53 RrFont *config_font_inactivewindow;
54 RrFont *config_font_menuitem;
55 RrFont *config_font_menutitle;
56 RrFont *config_font_osd;
57
58 guint   config_desktops_num;
59 GSList *config_desktops_names;
60 guint   config_screen_firstdesk;
61 guint   config_desktop_popup_time;
62
63 gboolean         config_resize_redraw;
64 gint             config_resize_popup_show;
65 ObResizePopupPos config_resize_popup_pos;
66 GravityPoint     config_resize_popup_fixed;
67
68 ObStackingLayer config_dock_layer;
69 gboolean        config_dock_floating;
70 gboolean        config_dock_nostrut;
71 ObDirection     config_dock_pos;
72 gint            config_dock_x;
73 gint            config_dock_y;
74 ObOrientation   config_dock_orient;
75 gboolean        config_dock_hide;
76 guint           config_dock_hide_delay;
77 guint           config_dock_show_delay;
78 guint           config_dock_app_move_button;
79 guint           config_dock_app_move_modifiers;
80
81 guint config_keyboard_reset_keycode;
82 guint config_keyboard_reset_state;
83
84 gint config_mouse_threshold;
85 gint config_mouse_dclicktime;
86 gint config_mouse_screenedgetime;
87
88 guint    config_menu_hide_delay;
89 gboolean config_menu_middle;
90 guint    config_submenu_show_delay;
91 gboolean config_menu_client_list_icons;
92
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->pos_force = src->pos_force;
141         dst->position = src->position;
142         dst->monitor = src->monitor;
143     }
144 }
145
146 static void config_parse_gravity_coord(xmlDocPtr doc, xmlNodePtr node,
147                                        GravityCoord *c)
148 {
149     gchar *s = parse_string(doc, node);
150     if (!g_ascii_strcasecmp(s, "center"))
151         c->center = TRUE;
152     else {
153         if (s[0] == '-')
154             c->opposite = TRUE;
155         if (s[0] == '-' || s[0] == '+')
156             c->pos = atoi(s+1);
157         else
158             c->pos = atoi(s);
159     }
160     g_free(s);
161 }
162
163 /*
164   <applications>
165     <application name="aterm">
166       <decor>false</decor>
167     </application>
168     <application name="Rhythmbox">
169       <layer>above</layer>
170       <position>
171         <x>700</x>
172         <y>0</y>
173         <monitor>1</monitor>
174       </position>
175       .. there is a lot more settings available
176     </application>
177   </applications>
178 */
179
180 /* Manages settings for individual applications.
181    Some notes: monitor is the screen number in a multi monitor
182    (Xinerama) setup (starting from 0) or mouse, meaning the
183    monitor the pointer is on. Default: mouse.
184    Layer can be three values, above (Always on top), below
185    (Always on bottom) and everything else (normal behaviour).
186    Positions can be an integer value or center, which will
187    center the window in the specified axis. Position is within
188    the monitor, so <position><x>center</x></position><monitor>2</monitor>
189    will center the window on the second monitor.
190 */
191 static void parse_per_app_settings(ObParseInst *inst, xmlDocPtr doc,
192                                    xmlNodePtr node, gpointer data)
193 {
194     xmlNodePtr app = parse_find_node("application", node->children);
195     gchar *name = NULL, *class = NULL, *role = NULL;
196     gboolean name_set, class_set;
197     gboolean x_pos_given;
198
199     while (app) {
200         name_set = class_set = x_pos_given = FALSE;
201
202         class_set = parse_attr_string("class", app, &class);
203         name_set = parse_attr_string("name", app, &name);
204         if (class_set || name_set) {
205             xmlNodePtr n, c;
206             ObAppSettings *settings = config_create_app_settings();;
207
208             if (name_set)
209                 settings->name = g_pattern_spec_new(name);
210
211             if (class_set)
212                 settings->class = g_pattern_spec_new(class);
213
214             if (parse_attr_string("role", app, &role))
215                 settings->role = g_pattern_spec_new(role);
216
217             if ((n = parse_find_node("decor", app->children)))
218                 if (!parse_contains("default", doc, n))
219                     settings->decor = parse_bool(doc, n);
220
221             if ((n = parse_find_node("shade", app->children)))
222                 if (!parse_contains("default", doc, n))
223                     settings->shade = parse_bool(doc, n);
224
225             if ((n = parse_find_node("position", app->children))) {
226                 if ((c = parse_find_node("x", n->children)))
227                     if (!parse_contains("default", doc, c)) {
228                         config_parse_gravity_coord(doc, c,
229                                                    &settings->position.x);
230                         x_pos_given = TRUE;
231                     }
232
233                 if (x_pos_given && (c = parse_find_node("y", n->children)))
234                     if (!parse_contains("default", doc, c)) {
235                         config_parse_gravity_coord(doc, c,
236                                                    &settings->position.y);
237                         settings->pos_given = TRUE;
238                     }
239
240                 if (settings->pos_given &&
241                     (c = parse_find_node("monitor", n->children)))
242                     if (!parse_contains("default", doc, c)) {
243                         gchar *s = parse_string(doc, c);
244                         if (!g_ascii_strcasecmp(s, "mouse"))
245                             settings->monitor = 0;
246                         else
247                             settings->monitor = parse_int(doc, c) + 1;
248                         g_free(s);
249                     }
250
251                 parse_attr_bool("force", n, &settings->pos_force);
252             }
253
254             if ((n = parse_find_node("focus", app->children)))
255                 if (!parse_contains("default", doc, n))
256                     settings->focus = parse_bool(doc, n);
257
258             if ((n = parse_find_node("desktop", app->children))) {
259                 if (!parse_contains("default", doc, n)) {
260                     gchar *s = parse_string(doc, n);
261                     if (!g_ascii_strcasecmp(s, "all"))
262                         settings->desktop = DESKTOP_ALL;
263                     else {
264                         gint i = parse_int(doc, n);
265                         if (i > 0)
266                             settings->desktop = i;
267                     }
268                     g_free(s);
269                 }
270             }
271
272             if ((n = parse_find_node("layer", app->children)))
273                 if (!parse_contains("default", doc, n)) {
274                     gchar *s = parse_string(doc, n);
275                     if (!g_ascii_strcasecmp(s, "above"))
276                         settings->layer = 1;
277                     else if (!g_ascii_strcasecmp(s, "below"))
278                         settings->layer = -1;
279                     else
280                         settings->layer = 0;
281                     g_free(s);
282                 }
283
284             if ((n = parse_find_node("iconic", app->children)))
285                 if (!parse_contains("default", doc, n))
286                     settings->iconic = parse_bool(doc, n);
287
288             if ((n = parse_find_node("skip_pager", app->children)))
289                 if (!parse_contains("default", doc, n))
290                     settings->skip_pager = parse_bool(doc, n);
291
292             if ((n = parse_find_node("skip_taskbar", app->children)))
293                 if (!parse_contains("default", doc, n))
294                     settings->skip_taskbar = parse_bool(doc, n);
295
296             if ((n = parse_find_node("fullscreen", app->children)))
297                 if (!parse_contains("default", doc, n))
298                     settings->fullscreen = parse_bool(doc, n);
299
300             if ((n = parse_find_node("maximized", app->children)))
301                 if (!parse_contains("default", doc, n)) {
302                     gchar *s = parse_string(doc, n);
303                     if (!g_ascii_strcasecmp(s, "horizontal")) {
304                         settings->max_horz = TRUE;
305                         settings->max_vert = FALSE;
306                     } else if (!g_ascii_strcasecmp(s, "vertical")) {
307                         settings->max_horz = FALSE;
308                         settings->max_vert = TRUE;
309                     } else
310                         settings->max_horz = settings->max_vert =
311                             parse_bool(doc, n);
312                     g_free(s);
313                 }
314
315             config_per_app_settings = g_slist_append(config_per_app_settings,
316                                               (gpointer) settings);
317             g_free(name);
318             g_free(class);
319             g_free(role);
320             name = class = role = NULL;
321         }
322
323         app = parse_find_node("application", app->next);
324     }
325 }
326
327 /*
328
329 <keybind key="C-x">
330   <action name="ChangeDesktop">
331     <desktop>3</desktop>
332   </action>
333 </keybind>
334
335 */
336
337 static void parse_key(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
338                       GList *keylist)
339 {
340     gchar *key;
341     xmlNodePtr n;
342     gboolean is_chroot = FALSE;
343
344     if (!parse_attr_string("key", node, &key))
345         return;
346
347     parse_attr_bool("chroot", node, &is_chroot);
348
349     keylist = g_list_append(keylist, key);
350
351     if ((n = parse_find_node("keybind", node->children))) {
352         while (n) {
353             parse_key(i, doc, n, keylist);
354             n = parse_find_node("keybind", n->next);
355         }
356     }
357     else if ((n = parse_find_node("action", node->children))) {
358         while (n) {
359             ObActionsAct *action;
360
361             action = actions_parse(i, doc, n);
362             if (action)
363                 keyboard_bind(keylist, action);
364             n = parse_find_node("action", n->next);
365         }
366     }
367
368     if (is_chroot)
369         keyboard_chroot(keylist);
370
371     g_free(key);
372     keylist = g_list_delete_link(keylist, g_list_last(keylist));
373 }
374
375 static void parse_keyboard(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
376                            gpointer data)
377 {
378     xmlNodePtr n;
379     gchar *key;
380
381     keyboard_unbind_all();
382
383     if ((n = parse_find_node("chainQuitKey", node->children))) {
384         key = parse_string(doc, n);
385         translate_key(key, &config_keyboard_reset_state,
386                       &config_keyboard_reset_keycode);
387         g_free(key);
388     }
389
390     if ((n = parse_find_node("keybind", node->children)))
391         while (n) {
392             parse_key(i, doc, n, NULL);
393             n = parse_find_node("keybind", n->next);
394         }
395 }
396
397 /*
398
399 <context name="Titlebar">
400   <mousebind button="Left" action="Press">
401     <action name="Raise"></action>
402   </mousebind>
403 </context>
404
405 */
406
407 static void parse_mouse(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
408                         gpointer data)
409 {
410     xmlNodePtr n, nbut, nact;
411     gchar *buttonstr;
412     gchar *contextstr;
413     ObMouseAction mact;
414
415     mouse_unbind_all();
416
417     node = node->children;
418
419     if ((n = parse_find_node("dragThreshold", node)))
420         config_mouse_threshold = parse_int(doc, n);
421     if ((n = parse_find_node("doubleClickTime", node)))
422         config_mouse_dclicktime = parse_int(doc, n);
423     if ((n = parse_find_node("screenEdgeWarpTime", node)))
424         config_mouse_screenedgetime = parse_int(doc, n);
425
426     n = parse_find_node("context", node);
427     while (n) {
428         if (!parse_attr_string("name", n, &contextstr))
429             goto next_n;
430         nbut = parse_find_node("mousebind", n->children);
431         while (nbut) {
432             if (!parse_attr_string("button", nbut, &buttonstr))
433                 goto next_nbut;
434             if (parse_attr_contains("press", nbut, "action")) {
435                 mact = OB_MOUSE_ACTION_PRESS;
436             } else if (parse_attr_contains("release", nbut, "action")) {
437                 mact = OB_MOUSE_ACTION_RELEASE;
438             } else if (parse_attr_contains("click", nbut, "action")) {
439                 mact = OB_MOUSE_ACTION_CLICK;
440             } else if (parse_attr_contains("doubleclick", nbut,"action")) {
441                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
442             } else if (parse_attr_contains("drag", nbut, "action")) {
443                 mact = OB_MOUSE_ACTION_MOTION;
444             } else
445                 goto next_nbut;
446             nact = parse_find_node("action", nbut->children);
447             while (nact) {
448                 ObActionsAct *action;
449
450                 if ((action = actions_parse(i, doc, nact)))
451                     mouse_bind(buttonstr, contextstr, mact, action);
452                 nact = parse_find_node("action", nact->next);
453             }
454             g_free(buttonstr);
455         next_nbut:
456             nbut = parse_find_node("mousebind", nbut->next);
457         }
458         g_free(contextstr);
459     next_n:
460         n = parse_find_node("context", n->next);
461     }
462 }
463
464 static void parse_focus(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
465                         gpointer data)
466 {
467     xmlNodePtr n;
468
469     node = node->children;
470
471     if ((n = parse_find_node("focusNew", node)))
472         config_focus_new = parse_bool(doc, n);
473     if ((n = parse_find_node("followMouse", node)))
474         config_focus_follow = parse_bool(doc, n);
475     if ((n = parse_find_node("focusDelay", node)))
476         config_focus_delay = parse_int(doc, n);
477     if ((n = parse_find_node("raiseOnFocus", node)))
478         config_focus_raise = parse_bool(doc, n);
479     if ((n = parse_find_node("focusLast", node)))
480         config_focus_last = parse_bool(doc, n);
481     if ((n = parse_find_node("underMouse", node)))
482         config_focus_under_mouse = parse_bool(doc, n);
483 }
484
485 static void parse_placement(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
486                             gpointer data)
487 {
488     xmlNodePtr n;
489
490     node = node->children;
491
492     if ((n = parse_find_node("policy", node)))
493         if (parse_contains("UnderMouse", doc, n))
494             config_place_policy = OB_PLACE_POLICY_MOUSE;
495     if ((n = parse_find_node("center", node)))
496         config_place_center = parse_bool(doc, n);
497     if ((n = parse_find_node("monitor", node))) {
498         if (parse_contains("active", doc, n))
499             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
500         else if (parse_contains("mouse", doc, n))
501             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
502     }
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 = (unsigned) 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         if (parse_contains("Top", doc, n))
662             config_resize_popup_pos = OB_RESIZE_POS_TOP;
663         else if (parse_contains("Center", doc, n))
664             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
665         else if (parse_contains("Fixed", doc, n)) {
666             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
667
668             if ((n = parse_find_node("popupFixedPosition", node))) {
669                 xmlNodePtr n2;
670
671                 if ((n2 = parse_find_node("x", n->children)))
672                     config_parse_gravity_coord(doc, n2,
673                                                &config_resize_popup_fixed.x);
674                 if ((n2 = parse_find_node("y", n->children)))
675                     config_parse_gravity_coord(doc, n2,
676                                                &config_resize_popup_fixed.y);
677             }
678         }
679     }
680 }
681
682 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
683                        gpointer data)
684 {
685     xmlNodePtr n;
686
687     node = node->children;
688
689     if ((n = parse_find_node("position", node))) {
690         if (parse_contains("TopLeft", doc, n))
691             config_dock_floating = FALSE,
692             config_dock_pos = OB_DIRECTION_NORTHWEST;
693         else if (parse_contains("Top", doc, n))
694             config_dock_floating = FALSE,
695             config_dock_pos = OB_DIRECTION_NORTH;
696         else if (parse_contains("TopRight", doc, n))
697             config_dock_floating = FALSE,
698             config_dock_pos = OB_DIRECTION_NORTHEAST;
699         else if (parse_contains("Right", doc, n))
700             config_dock_floating = FALSE,
701             config_dock_pos = OB_DIRECTION_EAST;
702         else if (parse_contains("BottomRight", doc, n))
703             config_dock_floating = FALSE,
704             config_dock_pos = OB_DIRECTION_SOUTHEAST;
705         else if (parse_contains("Bottom", doc, n))
706             config_dock_floating = FALSE,
707             config_dock_pos = OB_DIRECTION_SOUTH;
708         else if (parse_contains("BottomLeft", doc, n))
709             config_dock_floating = FALSE,
710             config_dock_pos = OB_DIRECTION_SOUTHWEST;
711         else if (parse_contains("Left", doc, n))
712             config_dock_floating = FALSE,
713             config_dock_pos = OB_DIRECTION_WEST;
714         else if (parse_contains("Floating", doc, n))
715             config_dock_floating = TRUE;
716     }
717     if (config_dock_floating) {
718         if ((n = parse_find_node("floatingX", node)))
719             config_dock_x = parse_int(doc, n);
720         if ((n = parse_find_node("floatingY", node)))
721             config_dock_y = parse_int(doc, n);
722     } else {
723         if ((n = parse_find_node("noStrut", node)))
724             config_dock_nostrut = parse_bool(doc, n);
725     }
726     if ((n = parse_find_node("stacking", node))) {
727         if (parse_contains("above", doc, n))
728             config_dock_layer = OB_STACKING_LAYER_ABOVE;
729         else if (parse_contains("normal", doc, n))
730             config_dock_layer = OB_STACKING_LAYER_NORMAL;
731         else if (parse_contains("below", doc, n))
732             config_dock_layer = OB_STACKING_LAYER_BELOW;
733     }
734     if ((n = parse_find_node("direction", node))) {
735         if (parse_contains("horizontal", doc, n))
736             config_dock_orient = OB_ORIENTATION_HORZ;
737         else if (parse_contains("vertical", doc, n))
738             config_dock_orient = OB_ORIENTATION_VERT;
739     }
740     if ((n = parse_find_node("autoHide", node)))
741         config_dock_hide = parse_bool(doc, n);
742     if ((n = parse_find_node("hideDelay", node)))
743         config_dock_hide_delay = parse_int(doc, n);
744     if ((n = parse_find_node("showDelay", node)))
745         config_dock_show_delay = parse_int(doc, n);
746     if ((n = parse_find_node("moveButton", node))) {
747         gchar *str = parse_string(doc, n);
748         guint b, s;
749         if (translate_button(str, &s, &b)) {
750             config_dock_app_move_button = b;
751             config_dock_app_move_modifiers = s;
752         } else {
753             g_message(_("Invalid button '%s' specified in config file"), str);
754         }
755         g_free(str);
756     }
757 }
758
759 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
760                        gpointer data)
761 {
762     xmlNodePtr n;
763     for (node = node->children; node; node = node->next) {
764         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
765             gchar *c;
766
767             c = parse_string(doc, node);
768             config_menu_files = g_slist_append(config_menu_files,
769                                                parse_expand_tilde(c));
770             g_free(c);
771         }
772         if ((n = parse_find_node("hideDelay", node)))
773             config_menu_hide_delay = parse_int(doc, n);
774         if ((n = parse_find_node("middle", node)))
775             config_menu_middle = parse_bool(doc, n);
776         if ((n = parse_find_node("submenuShowDelay", node)))
777             config_submenu_show_delay = parse_int(doc, n);
778         if ((n = parse_find_node("applicationIcons", node)))
779             config_menu_client_list_icons = parse_bool(doc, n);
780     }
781 }
782
783 static void parse_resistance(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
784                              gpointer data)
785 {
786     xmlNodePtr n;
787
788     node = node->children;
789     if ((n = parse_find_node("strength", node)))
790         config_resist_win = parse_int(doc, n);
791     if ((n = parse_find_node("screen_edge_strength", node)))
792         config_resist_edge = parse_int(doc, n);
793 }
794
795 typedef struct
796 {
797     const gchar *key;
798     const gchar *actname;
799 } ObDefKeyBind;
800
801 static void bind_default_keyboard(void)
802 {
803     ObDefKeyBind *it;
804     ObDefKeyBind binds[] = {
805         { "A-Tab", "NextWindow" },
806         { "S-A-Tab", "PreviousWindow" },
807         { "A-F4", "Close" },
808         { NULL, NULL }
809     };
810     for (it = binds; it->key; ++it) {
811         GList *l = g_list_append(NULL, g_strdup(it->key));
812         keyboard_bind(l, actions_parse_string(it->actname));
813     }
814 }
815
816 typedef struct
817 {
818     const gchar *button;
819     const gchar *context;
820     const ObMouseAction mact;
821     const gchar *actname;
822 } ObDefMouseBind;
823
824 static void bind_default_mouse(void)
825 {
826     ObDefMouseBind *it;
827     ObDefMouseBind binds[] = {
828         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
829         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
830         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
831         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
832         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
833         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
834         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
835         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
836         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
837         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
838         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
839         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
840         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
841         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
842         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
843         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
844         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
845         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
846         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
847         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
848         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
849         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
850         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
851         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
852         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
853         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
854         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
855         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
856         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
857         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
858         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
859         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
860         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
861         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
862         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
863         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
864         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
865         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
866         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
867         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
868         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
869         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
870         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
871         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
872         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
873         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
874         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
875         { NULL, NULL, 0, NULL }
876     };
877
878     for (it = binds; it->button; ++it)
879         mouse_bind(it->button, it->context, it->mact,
880                    actions_parse_string(it->actname));
881 }
882
883 void config_startup(ObParseInst *i)
884 {
885     config_focus_new = TRUE;
886     config_focus_follow = FALSE;
887     config_focus_delay = 0;
888     config_focus_raise = FALSE;
889     config_focus_last = TRUE;
890     config_focus_under_mouse = FALSE;
891
892     parse_register(i, "focus", parse_focus, NULL);
893
894     config_place_policy = OB_PLACE_POLICY_SMART;
895     config_place_center = TRUE;
896     config_place_monitor = OB_PLACE_MONITOR_ANY;
897
898     parse_register(i, "placement", parse_placement, NULL);
899
900     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
901
902     parse_register(i, "margins", parse_margins, NULL);
903
904     config_theme = NULL;
905
906     config_animate_iconify = TRUE;
907     config_title_layout = g_strdup("NLIMC");
908     config_theme_keepborder = TRUE;
909
910     config_font_activewindow = NULL;
911     config_font_inactivewindow = NULL;
912     config_font_menuitem = NULL;
913     config_font_menutitle = NULL;
914
915     parse_register(i, "theme", parse_theme, NULL);
916
917     config_desktops_num = 4;
918     config_screen_firstdesk = 1;
919     config_desktops_names = NULL;
920     config_desktop_popup_time = 875;
921
922     parse_register(i, "desktops", parse_desktops, NULL);
923
924     config_resize_redraw = TRUE;
925     config_resize_popup_show = 1; /* nonpixel increments */
926     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
927     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
928     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
929
930     parse_register(i, "resize", parse_resize, NULL);
931
932     config_dock_layer = OB_STACKING_LAYER_ABOVE;
933     config_dock_pos = OB_DIRECTION_NORTHEAST;
934     config_dock_floating = FALSE;
935     config_dock_nostrut = FALSE;
936     config_dock_x = 0;
937     config_dock_y = 0;
938     config_dock_orient = OB_ORIENTATION_VERT;
939     config_dock_hide = FALSE;
940     config_dock_hide_delay = 300;
941     config_dock_show_delay = 300;
942     config_dock_app_move_button = 2; /* middle */
943     config_dock_app_move_modifiers = 0;
944
945     parse_register(i, "dock", parse_dock, NULL);
946
947     translate_key("C-g", &config_keyboard_reset_state,
948                   &config_keyboard_reset_keycode);
949
950     bind_default_keyboard();
951
952     parse_register(i, "keyboard", parse_keyboard, NULL);
953
954     config_mouse_threshold = 8;
955     config_mouse_dclicktime = 200;
956     config_mouse_screenedgetime = 400;
957
958     bind_default_mouse();
959
960     parse_register(i, "mouse", parse_mouse, NULL);
961
962     config_resist_win = 10;
963     config_resist_edge = 20;
964
965     parse_register(i, "resistance", parse_resistance, NULL);
966
967     config_menu_hide_delay = 250;
968     config_menu_middle = FALSE;
969     config_submenu_show_delay = 0;
970     config_menu_client_list_icons = TRUE;
971     config_menu_files = NULL;
972
973     parse_register(i, "menu", parse_menu, NULL);
974
975     config_per_app_settings = NULL;
976
977     parse_register(i, "applications", parse_per_app_settings, NULL);
978 }
979
980 void config_shutdown(void)
981 {
982     GSList *it;
983
984     g_free(config_theme);
985
986     g_free(config_title_layout);
987
988     RrFontClose(config_font_activewindow);
989     RrFontClose(config_font_inactivewindow);
990     RrFontClose(config_font_menuitem);
991     RrFontClose(config_font_menutitle);
992     RrFontClose(config_font_osd);
993
994     for (it = config_desktops_names; it; it = g_slist_next(it))
995         g_free(it->data);
996     g_slist_free(config_desktops_names);
997
998     for (it = config_menu_files; it; it = g_slist_next(it))
999         g_free(it->data);
1000     g_slist_free(config_menu_files);
1001
1002     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1003         ObAppSettings *itd = (ObAppSettings *)it->data;
1004         if (itd->name)  g_pattern_spec_free(itd->name);
1005         if (itd->role)  g_pattern_spec_free(itd->role);
1006         if (itd->class) g_pattern_spec_free(itd->class);
1007         g_free(it->data);
1008     }
1009     g_slist_free(config_per_app_settings);
1010 }