dont build the old action stuff.
[dana/openbox.git] / openbox / config.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    config.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "config.h"
21 #include "keyboard.h"
22 #include "mouse.h"
23 #include "actions.h"
24 #include "prop.h"
25 #include "translate.h"
26 #include "client.h"
27 #include "screen.h"
28 #include "parser/parse.h"
29 #include "openbox.h"
30 #include "gettext.h"
31
32 gboolean config_focus_new;
33 gboolean config_focus_follow;
34 guint    config_focus_delay;
35 gboolean config_focus_raise;
36 gboolean config_focus_last;
37 gboolean config_focus_under_mouse;
38
39 ObPlacePolicy config_place_policy;
40 gboolean      config_place_center;
41
42 gchar   *config_theme;
43 gboolean config_theme_keepborder;
44
45 gchar   *config_title_layout;
46
47 gboolean config_animate_iconify;
48
49 RrFont *config_font_activewindow;
50 RrFont *config_font_inactivewindow;
51 RrFont *config_font_menuitem;
52 RrFont *config_font_menutitle;
53 RrFont *config_font_osd;
54
55 gint    config_desktops_num;
56 GSList *config_desktops_names;
57 guint   config_screen_firstdesk;
58
59 gboolean config_resize_redraw;
60 gboolean config_resize_four_corners;
61 gint     config_resize_popup_show;
62 gint     config_resize_popup_pos;
63
64 ObStackingLayer config_dock_layer;
65 gboolean        config_dock_floating;
66 gboolean        config_dock_nostrut;
67 ObDirection     config_dock_pos;
68 gint            config_dock_x;
69 gint            config_dock_y;
70 ObOrientation   config_dock_orient;
71 gboolean        config_dock_hide;
72 guint           config_dock_hide_delay;
73 guint           config_dock_show_delay;
74 guint           config_dock_app_move_button;
75 guint           config_dock_app_move_modifiers;
76
77 guint config_keyboard_reset_keycode;
78 guint config_keyboard_reset_state;
79
80 gint config_mouse_threshold;
81 gint config_mouse_dclicktime;
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             ObActionsAct *action;
362             
363             action = actions_parse(i, doc, n);
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     ObMouseAction mact;
416
417     mouse_unbind_all();
418
419     node = node->children;
420     
421     if ((n = parse_find_node("dragThreshold", node)))
422         config_mouse_threshold = parse_int(doc, n);
423     if ((n = parse_find_node("doubleClickTime", node)))
424         config_mouse_dclicktime = parse_int(doc, n);
425
426     n = parse_find_node("context", node);
427     while (n) {
428         if (!parse_attr_string("name", n, &contextstr))
429             goto next_n;
430         nbut = parse_find_node("mousebind", n->children);
431         while (nbut) {
432             if (!parse_attr_string("button", nbut, &buttonstr))
433                 goto next_nbut;
434             if (parse_attr_contains("press", nbut, "action")) {
435                 mact = OB_MOUSE_ACTION_PRESS;
436             } else if (parse_attr_contains("release", nbut, "action")) {
437                 mact = OB_MOUSE_ACTION_RELEASE;
438             } else if (parse_attr_contains("click", nbut, "action")) {
439                 mact = OB_MOUSE_ACTION_CLICK;
440             } else if (parse_attr_contains("doubleclick", nbut,"action")) {
441                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
442             } else if (parse_attr_contains("drag", nbut, "action")) {
443                 mact = OB_MOUSE_ACTION_MOTION;
444             } else
445                 goto next_nbut;
446             nact = parse_find_node("action", nbut->children);
447             while (nact) {
448                 ObActionsAct *action;
449
450                 if ((action = actions_parse(i, doc, nact)))
451                     mouse_bind(buttonstr, contextstr, mact, action);
452                 nact = parse_find_node("action", nact->next);
453             }
454             g_free(buttonstr);
455         next_nbut:
456             nbut = parse_find_node("mousebind", nbut->next);
457         }
458         g_free(contextstr);
459     next_n:
460         n = parse_find_node("context", n->next);
461     }
462 }
463
464 static void parse_focus(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
465                         gpointer d)
466 {
467     xmlNodePtr n;
468
469     node = node->children;
470     
471     if ((n = parse_find_node("focusNew", node)))
472         config_focus_new = parse_bool(doc, n);
473     if ((n = parse_find_node("followMouse", node)))
474         config_focus_follow = parse_bool(doc, n);
475     if ((n = parse_find_node("focusDelay", node)))
476         config_focus_delay = parse_int(doc, n) * 1000;
477     if ((n = parse_find_node("raiseOnFocus", node)))
478         config_focus_raise = parse_bool(doc, n);
479     if ((n = parse_find_node("focusLast", node)))
480         config_focus_last = parse_bool(doc, n);
481     if ((n = parse_find_node("underMouse", node)))
482         config_focus_under_mouse = parse_bool(doc, n);
483 }
484
485 static void parse_placement(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
486                             gpointer d)
487 {
488     xmlNodePtr n;
489
490     node = node->children;
491     
492     if ((n = parse_find_node("policy", node)))
493         if (parse_contains("UnderMouse", doc, n))
494             config_place_policy = OB_PLACE_POLICY_MOUSE;
495     if ((n = parse_find_node("center", node)))
496         config_place_center = parse_bool(doc, n);
497 }
498
499 static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
500                         gpointer d)
501 {
502     xmlNodePtr n;
503
504     node = node->children;
505
506     if ((n = parse_find_node("name", node))) {
507         gchar *c;
508
509         g_free(config_theme);
510         c = parse_string(doc, n);
511         config_theme = parse_expand_tilde(c);
512         g_free(c);
513     }
514     if ((n = parse_find_node("titleLayout", node))) {
515         gchar *c, *d;
516
517         g_free(config_title_layout);
518         config_title_layout = parse_string(doc, n);
519
520         /* replace duplicates with spaces */
521         for (c = config_title_layout; *c != '\0'; ++c)
522             for (d = c+1; *d != '\0'; ++d)
523                 if (*c == *d) *d = ' ';
524     }
525     if ((n = parse_find_node("keepBorder", node)))
526         config_theme_keepborder = parse_bool(doc, n);
527     if ((n = parse_find_node("animateIconify", node)))
528         config_animate_iconify = parse_bool(doc, n);
529
530     n = parse_find_node("font", node);
531     while (n) {
532         xmlNodePtr   fnode;
533         RrFont     **font;
534         gchar       *name = g_strdup(RrDefaultFontFamily);
535         gint         size = RrDefaultFontSize;
536         RrFontWeight weight = RrDefaultFontWeight;
537         RrFontSlant  slant = RrDefaultFontSlant;
538
539         if (parse_attr_contains("ActiveWindow", n, "place"))
540             font = &config_font_activewindow;
541         else if (parse_attr_contains("InactiveWindow", n, "place"))
542             font = &config_font_inactivewindow;
543         else if (parse_attr_contains("MenuHeader", n, "place"))
544             font = &config_font_menutitle;
545         else if (parse_attr_contains("MenuItem", n, "place"))
546             font = &config_font_menuitem;
547         else if (parse_attr_contains("OnScreenDisplay", n, "place"))
548             font = &config_font_osd;
549         else
550             goto next_font;
551
552         if ((fnode = parse_find_node("name", n->children))) {
553             g_free(name);
554             name = parse_string(doc, fnode);
555         }
556         if ((fnode = parse_find_node("size", n->children))) {
557             int s = parse_int(doc, fnode);
558             if (s > 0) size = s;
559         }
560         if ((fnode = parse_find_node("weight", n->children))) {
561             gchar *w = parse_string(doc, fnode);
562             if (!g_ascii_strcasecmp(w, "Bold"))
563                 weight = RR_FONTWEIGHT_BOLD;
564             g_free(w);
565         }
566         if ((fnode = parse_find_node("slant", n->children))) {
567             gchar *s = parse_string(doc, fnode);
568             if (!g_ascii_strcasecmp(s, "Italic"))
569                 slant = RR_FONTSLANT_ITALIC;
570             if (!g_ascii_strcasecmp(s, "Oblique"))
571                 slant = RR_FONTSLANT_OBLIQUE;
572             g_free(s);
573         }
574
575         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
576         g_free(name);
577     next_font:
578         n = parse_find_node("font", n->next);
579     }
580 }
581
582 static void parse_desktops(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
583                            gpointer d)
584 {
585     xmlNodePtr n;
586
587     node = node->children;
588     
589     if ((n = parse_find_node("number", node))) {
590         gint d = parse_int(doc, n);
591         if (d > 0)
592             config_desktops_num = d;
593     }
594     if ((n = parse_find_node("firstdesk", node))) {
595         gint d = parse_int(doc, n);
596         if (d > 0)
597             config_screen_firstdesk = (unsigned) d;
598     }
599     if ((n = parse_find_node("names", node))) {
600         GSList *it;
601         xmlNodePtr nname;
602
603         for (it = config_desktops_names; it; it = it->next)
604             g_free(it->data);
605         g_slist_free(config_desktops_names);
606         config_desktops_names = NULL;
607
608         nname = parse_find_node("name", n->children);
609         while (nname) {
610             config_desktops_names = g_slist_append(config_desktops_names,
611                                                    parse_string(doc, nname));
612             nname = parse_find_node("name", nname->next);
613         }
614     }
615 }
616
617 static void parse_resize(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
618                          gpointer d)
619 {
620     xmlNodePtr n;
621
622     node = node->children;
623     
624     if ((n = parse_find_node("drawContents", node)))
625         config_resize_redraw = parse_bool(doc, n);
626     if ((n = parse_find_node("popupShow", node))) {
627         config_resize_popup_show = parse_int(doc, n);
628         if (parse_contains("Always", doc, n))
629             config_resize_popup_show = 2;
630         else if (parse_contains("Never", doc, n))
631             config_resize_popup_show = 0;
632         else if (parse_contains("Nonpixel", doc, n))
633             config_resize_popup_show = 1;
634     }
635     if ((n = parse_find_node("popupPosition", node))) {
636         config_resize_popup_pos = parse_int(doc, n);
637         if (parse_contains("Top", doc, n))
638             config_resize_popup_pos = 1;
639         else if (parse_contains("Center", doc, n))
640             config_resize_popup_pos = 0;
641     }
642 }
643
644 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
645                        gpointer d)
646 {
647     xmlNodePtr n;
648
649     node = node->children;
650
651     if ((n = parse_find_node("position", node))) {
652         if (parse_contains("TopLeft", doc, n))
653             config_dock_floating = FALSE,
654             config_dock_pos = OB_DIRECTION_NORTHWEST;
655         else if (parse_contains("Top", doc, n))
656             config_dock_floating = FALSE,
657             config_dock_pos = OB_DIRECTION_NORTH;
658         else if (parse_contains("TopRight", doc, n))
659             config_dock_floating = FALSE,
660             config_dock_pos = OB_DIRECTION_NORTHEAST;
661         else if (parse_contains("Right", doc, n))
662             config_dock_floating = FALSE,
663             config_dock_pos = OB_DIRECTION_EAST;
664         else if (parse_contains("BottomRight", doc, n))
665             config_dock_floating = FALSE,
666             config_dock_pos = OB_DIRECTION_SOUTHEAST;
667         else if (parse_contains("Bottom", doc, n))
668             config_dock_floating = FALSE,
669             config_dock_pos = OB_DIRECTION_SOUTH;
670         else if (parse_contains("BottomLeft", doc, n))
671             config_dock_floating = FALSE,
672             config_dock_pos = OB_DIRECTION_SOUTHWEST;
673         else if (parse_contains("Left", doc, n))
674             config_dock_floating = FALSE,
675             config_dock_pos = OB_DIRECTION_WEST;
676         else if (parse_contains("Floating", doc, n))
677             config_dock_floating = TRUE;
678     }
679     if (config_dock_floating) {
680         if ((n = parse_find_node("floatingX", node)))
681             config_dock_x = parse_int(doc, n);
682         if ((n = parse_find_node("floatingY", node)))
683             config_dock_y = parse_int(doc, n);
684     } else {
685         if ((n = parse_find_node("noStrut", node)))
686             config_dock_nostrut = parse_bool(doc, n);
687     }
688     if ((n = parse_find_node("stacking", node))) {
689         if (parse_contains("above", doc, n))
690             config_dock_layer = OB_STACKING_LAYER_ABOVE;
691         else if (parse_contains("normal", doc, n))
692             config_dock_layer = OB_STACKING_LAYER_NORMAL;
693         else if (parse_contains("below", doc, n))
694             config_dock_layer = OB_STACKING_LAYER_BELOW;
695     }
696     if ((n = parse_find_node("direction", node))) {
697         if (parse_contains("horizontal", doc, n))
698             config_dock_orient = OB_ORIENTATION_HORZ;
699         else if (parse_contains("vertical", doc, n))
700             config_dock_orient = OB_ORIENTATION_VERT;
701     }
702     if ((n = parse_find_node("autoHide", node)))
703         config_dock_hide = parse_bool(doc, n);
704     if ((n = parse_find_node("hideDelay", node)))
705         config_dock_hide_delay = parse_int(doc, n) * 1000;
706     if ((n = parse_find_node("showDelay", node)))
707         config_dock_show_delay = parse_int(doc, n) * 1000;
708     if ((n = parse_find_node("moveButton", node))) {
709         gchar *str = parse_string(doc, n);
710         guint b, s;
711         if (translate_button(str, &s, &b)) {
712             config_dock_app_move_button = b;
713             config_dock_app_move_modifiers = s;
714         } else {
715             g_message(_("Invalid button '%s' specified in config file"), str);
716         }
717         g_free(str);
718     }
719 }
720
721 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
722                        gpointer d)
723 {
724     xmlNodePtr n;
725     for (node = node->children; node; node = node->next) {
726         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
727             gchar *c;
728
729             c = parse_string(doc, node);
730             config_menu_files = g_slist_append(config_menu_files,
731                                                parse_expand_tilde(c));
732             g_free(c);
733         }
734         if ((n = parse_find_node("hideDelay", node)))
735             config_menu_hide_delay = parse_int(doc, n);
736         if ((n = parse_find_node("middle", node)))
737             config_menu_middle = parse_bool(doc, n);
738         if ((n = parse_find_node("submenuShowDelay", node)))
739             config_submenu_show_delay = parse_int(doc, n);
740         if ((n = parse_find_node("applicationIcons", node)))
741             config_menu_client_list_icons = parse_bool(doc, n);
742     }
743 }
744    
745 static void parse_resistance(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node, 
746                              gpointer d)
747 {
748     xmlNodePtr n;
749
750     node = node->children;
751     if ((n = parse_find_node("strength", node)))
752         config_resist_win = parse_int(doc, n);
753     if ((n = parse_find_node("screen_edge_strength", node)))
754         config_resist_edge = parse_int(doc, n);
755 }
756
757 typedef struct
758 {
759     const gchar *key;
760     const gchar *actname;
761 } ObDefKeyBind;
762
763 static void bind_default_keyboard()
764 {
765     ObDefKeyBind *it;
766     ObDefKeyBind binds[] = {
767         { "A-Tab", "NextWindow" },
768         { "S-A-Tab", "PreviousWindow" },
769         { "A-F4", "Close" },
770         { NULL, NULL }
771     };
772
773     for (it = binds; it->key; ++it) {
774         GList *l = g_list_append(NULL, g_strdup(it->key));
775         keyboard_bind(l, actions_parse_string(it->actname));
776         g_list_free(l);
777     }
778 }
779
780 typedef struct
781 {
782     const gchar *button;
783     const gchar *context;
784     const ObMouseAction mact;
785     const gchar *actname;
786 } ObDefMouseBind;
787
788 static void bind_default_mouse()
789 {
790     ObDefMouseBind *it;
791     ObDefMouseBind binds[] = {
792         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
793         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
794         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
795         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
796         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
797         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
798         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
799         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
800         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
801         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
802         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
803         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
804         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
805         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
806         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
807         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
808         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
809         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
810         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
811         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
812         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
813         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
814         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
815         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
816         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
817         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
818         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
819         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
820         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
821         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
822         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
823         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
824         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximizeFull" },
825         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
826         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
827         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
828         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
829         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
830         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
831         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
832         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
833         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
834         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
835         { NULL, NULL, 0, NULL }
836     };
837
838     for (it = binds; it->button; ++it)
839         mouse_bind(it->button, it->context, it->mact,
840                    actions_parse_string(it->actname));
841 }
842
843 void config_startup(ObParseInst *i)
844 {
845     config_focus_new = TRUE;
846     config_focus_follow = FALSE;
847     config_focus_delay = 0;
848     config_focus_raise = FALSE;
849     config_focus_last = TRUE;
850     config_focus_under_mouse = FALSE;
851
852     parse_register(i, "focus", parse_focus, NULL);
853
854     config_place_policy = OB_PLACE_POLICY_SMART;
855     config_place_center = TRUE;
856
857     parse_register(i, "placement", parse_placement, NULL);
858
859     config_theme = NULL;
860
861     config_animate_iconify = TRUE;
862     config_title_layout = g_strdup("NLIMC");
863     config_theme_keepborder = TRUE;
864
865     config_font_activewindow = NULL;
866     config_font_inactivewindow = NULL;
867     config_font_menuitem = NULL;
868     config_font_menutitle = NULL;
869
870     parse_register(i, "theme", parse_theme, NULL);
871
872     config_desktops_num = 4;
873     config_screen_firstdesk = 1;
874     config_desktops_names = NULL;
875
876     parse_register(i, "desktops", parse_desktops, NULL);
877
878     config_resize_redraw = TRUE;
879     config_resize_four_corners = FALSE;
880     config_resize_popup_show = 1; /* nonpixel increments */
881     config_resize_popup_pos = 0;  /* center of client */
882
883     parse_register(i, "resize", parse_resize, NULL);
884
885     config_dock_layer = OB_STACKING_LAYER_ABOVE;
886     config_dock_pos = OB_DIRECTION_NORTHEAST;
887     config_dock_floating = FALSE;
888     config_dock_nostrut = FALSE;
889     config_dock_x = 0;
890     config_dock_y = 0;
891     config_dock_orient = OB_ORIENTATION_VERT;
892     config_dock_hide = FALSE;
893     config_dock_hide_delay = 300;
894     config_dock_show_delay = 300;
895     config_dock_app_move_button = 2; /* middle */
896     config_dock_app_move_modifiers = 0;
897
898     parse_register(i, "dock", parse_dock, NULL);
899
900     translate_key("C-g", &config_keyboard_reset_state,
901                   &config_keyboard_reset_keycode);
902
903     bind_default_keyboard();
904
905     parse_register(i, "keyboard", parse_keyboard, NULL);
906
907     config_mouse_threshold = 8;
908     config_mouse_dclicktime = 200;
909
910     bind_default_mouse();
911
912     parse_register(i, "mouse", parse_mouse, NULL);
913
914     config_resist_win = 10;
915     config_resist_edge = 20;
916
917     parse_register(i, "resistance", parse_resistance, NULL);
918
919     config_menu_hide_delay = 250;
920     config_menu_middle = FALSE;
921     config_submenu_show_delay = 0;
922     config_menu_client_list_icons = TRUE;
923     config_menu_files = NULL;
924
925     parse_register(i, "menu", parse_menu, NULL);
926
927     config_per_app_settings = NULL;
928
929     parse_register(i, "applications", parse_per_app_settings, NULL);
930 }
931
932 void config_shutdown()
933 {
934     GSList *it;
935
936     g_free(config_theme);
937
938     g_free(config_title_layout);
939
940     RrFontClose(config_font_activewindow);
941     RrFontClose(config_font_inactivewindow);
942     RrFontClose(config_font_menuitem);
943     RrFontClose(config_font_menutitle);
944
945     for (it = config_desktops_names; it; it = g_slist_next(it))
946         g_free(it->data);
947     g_slist_free(config_desktops_names);
948
949     for (it = config_menu_files; it; it = g_slist_next(it))
950         g_free(it->data);
951     g_slist_free(config_menu_files);
952
953     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
954         ObAppSettings *itd = (ObAppSettings *)it->data;
955         if (itd->name)  g_pattern_spec_free(itd->name);
956         if (itd->role)  g_pattern_spec_free(itd->role);
957         if (itd->class) g_pattern_spec_free(itd->class);
958         g_free(it->data);
959     }
960     g_slist_free(config_per_app_settings);
961 }