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