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