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