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