maybe the new actions framework is kinda there now
[mikachu/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 "actions.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             ObActionsDefinition *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     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
428     n = parse_find_node("context", node);
429     while (n) {
430         if (!parse_attr_string("name", n, &contextstr))
431             goto next_n;
432         nbut = parse_find_node("mousebind", n->children);
433         while (nbut) {
434             if (!parse_attr_string("button", nbut, &buttonstr))
435                 goto next_nbut;
436             if (parse_attr_contains("press", nbut, "action")) {
437                 uact = OB_USER_ACTION_MOUSE_PRESS;
438                 mact = OB_MOUSE_ACTION_PRESS;
439             } else if (parse_attr_contains("release", nbut, "action")) {
440                 uact = OB_USER_ACTION_MOUSE_RELEASE;
441                 mact = OB_MOUSE_ACTION_RELEASE;
442             } else if (parse_attr_contains("click", nbut, "action")) {
443                 uact = OB_USER_ACTION_MOUSE_CLICK;
444                 mact = OB_MOUSE_ACTION_CLICK;
445             } else if (parse_attr_contains("doubleclick", nbut,"action")) {
446                 uact = OB_USER_ACTION_MOUSE_DOUBLE_CLICK;
447                 mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
448             } else if (parse_attr_contains("drag", nbut, "action")) {
449                 uact = OB_USER_ACTION_MOUSE_MOTION;
450                 mact = OB_MOUSE_ACTION_MOTION;
451             } else
452                 goto next_nbut;
453             nact = parse_find_node("action", nbut->children);
454             while (nact) {
455                 if ((action = action_parse(i, doc, nact, uact)))
456                     mouse_bind(buttonstr, contextstr, mact, action);
457                 nact = parse_find_node("action", nact->next);
458             }
459             g_free(buttonstr);
460         next_nbut:
461             nbut = parse_find_node("mousebind", nbut->next);
462         }
463         g_free(contextstr);
464     next_n:
465         n = parse_find_node("context", n->next);
466     }
467 }
468
469 static void parse_focus(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
470                         gpointer d)
471 {
472     xmlNodePtr n;
473
474     node = node->children;
475     
476     if ((n = parse_find_node("focusNew", node)))
477         config_focus_new = parse_bool(doc, n);
478     if ((n = parse_find_node("followMouse", node)))
479         config_focus_follow = parse_bool(doc, n);
480     if ((n = parse_find_node("focusDelay", node)))
481         config_focus_delay = parse_int(doc, n) * 1000;
482     if ((n = parse_find_node("raiseOnFocus", node)))
483         config_focus_raise = parse_bool(doc, n);
484     if ((n = parse_find_node("focusLast", node)))
485         config_focus_last = parse_bool(doc, n);
486     if ((n = parse_find_node("underMouse", node)))
487         config_focus_under_mouse = parse_bool(doc, n);
488 }
489
490 static void parse_placement(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
491                             gpointer d)
492 {
493     xmlNodePtr n;
494
495     node = node->children;
496     
497     if ((n = parse_find_node("policy", node)))
498         if (parse_contains("UnderMouse", doc, n))
499             config_place_policy = OB_PLACE_POLICY_MOUSE;
500     if ((n = parse_find_node("center", node)))
501         config_place_center = parse_bool(doc, n);
502 }
503
504 static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
505                         gpointer d)
506 {
507     xmlNodePtr n;
508
509     node = node->children;
510
511     if ((n = parse_find_node("name", node))) {
512         gchar *c;
513
514         g_free(config_theme);
515         c = parse_string(doc, n);
516         config_theme = parse_expand_tilde(c);
517         g_free(c);
518     }
519     if ((n = parse_find_node("titleLayout", node))) {
520         gchar *c, *d;
521
522         g_free(config_title_layout);
523         config_title_layout = parse_string(doc, n);
524
525         /* replace duplicates with spaces */
526         for (c = config_title_layout; *c != '\0'; ++c)
527             for (d = c+1; *d != '\0'; ++d)
528                 if (*c == *d) *d = ' ';
529     }
530     if ((n = parse_find_node("keepBorder", node)))
531         config_theme_keepborder = parse_bool(doc, n);
532     if ((n = parse_find_node("animateIconify", node)))
533         config_animate_iconify = parse_bool(doc, n);
534
535     n = parse_find_node("font", node);
536     while (n) {
537         xmlNodePtr   fnode;
538         RrFont     **font;
539         gchar       *name = g_strdup(RrDefaultFontFamily);
540         gint         size = RrDefaultFontSize;
541         RrFontWeight weight = RrDefaultFontWeight;
542         RrFontSlant  slant = RrDefaultFontSlant;
543
544         if (parse_attr_contains("ActiveWindow", n, "place"))
545             font = &config_font_activewindow;
546         else if (parse_attr_contains("InactiveWindow", n, "place"))
547             font = &config_font_inactivewindow;
548         else if (parse_attr_contains("MenuHeader", n, "place"))
549             font = &config_font_menutitle;
550         else if (parse_attr_contains("MenuItem", n, "place"))
551             font = &config_font_menuitem;
552         else if (parse_attr_contains("OnScreenDisplay", n, "place"))
553             font = &config_font_osd;
554         else
555             goto next_font;
556
557         if ((fnode = parse_find_node("name", n->children))) {
558             g_free(name);
559             name = parse_string(doc, fnode);
560         }
561         if ((fnode = parse_find_node("size", n->children))) {
562             int s = parse_int(doc, fnode);
563             if (s > 0) size = s;
564         }
565         if ((fnode = parse_find_node("weight", n->children))) {
566             gchar *w = parse_string(doc, fnode);
567             if (!g_ascii_strcasecmp(w, "Bold"))
568                 weight = RR_FONTWEIGHT_BOLD;
569             g_free(w);
570         }
571         if ((fnode = parse_find_node("slant", n->children))) {
572             gchar *s = parse_string(doc, fnode);
573             if (!g_ascii_strcasecmp(s, "Italic"))
574                 slant = RR_FONTSLANT_ITALIC;
575             if (!g_ascii_strcasecmp(s, "Oblique"))
576                 slant = RR_FONTSLANT_OBLIQUE;
577             g_free(s);
578         }
579
580         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
581         g_free(name);
582     next_font:
583         n = parse_find_node("font", n->next);
584     }
585 }
586
587 static void parse_desktops(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
588                            gpointer d)
589 {
590     xmlNodePtr n;
591
592     node = node->children;
593     
594     if ((n = parse_find_node("number", node))) {
595         gint d = parse_int(doc, n);
596         if (d > 0)
597             config_desktops_num = d;
598     }
599     if ((n = parse_find_node("firstdesk", node))) {
600         gint d = parse_int(doc, n);
601         if (d > 0)
602             config_screen_firstdesk = (unsigned) d;
603     }
604     if ((n = parse_find_node("names", node))) {
605         GSList *it;
606         xmlNodePtr nname;
607
608         for (it = config_desktops_names; it; it = it->next)
609             g_free(it->data);
610         g_slist_free(config_desktops_names);
611         config_desktops_names = NULL;
612
613         nname = parse_find_node("name", n->children);
614         while (nname) {
615             config_desktops_names = g_slist_append(config_desktops_names,
616                                                    parse_string(doc, nname));
617             nname = parse_find_node("name", nname->next);
618         }
619     }
620 }
621
622 static void parse_resize(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
623                          gpointer d)
624 {
625     xmlNodePtr n;
626
627     node = node->children;
628     
629     if ((n = parse_find_node("drawContents", node)))
630         config_resize_redraw = parse_bool(doc, n);
631     if ((n = parse_find_node("popupShow", node))) {
632         config_resize_popup_show = parse_int(doc, n);
633         if (parse_contains("Always", doc, n))
634             config_resize_popup_show = 2;
635         else if (parse_contains("Never", doc, n))
636             config_resize_popup_show = 0;
637         else if (parse_contains("Nonpixel", doc, n))
638             config_resize_popup_show = 1;
639     }
640     if ((n = parse_find_node("popupPosition", node))) {
641         config_resize_popup_pos = parse_int(doc, n);
642         if (parse_contains("Top", doc, n))
643             config_resize_popup_pos = 1;
644         else if (parse_contains("Center", doc, n))
645             config_resize_popup_pos = 0;
646     }
647 }
648
649 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
650                        gpointer d)
651 {
652     xmlNodePtr n;
653
654     node = node->children;
655
656     if ((n = parse_find_node("position", node))) {
657         if (parse_contains("TopLeft", doc, n))
658             config_dock_floating = FALSE,
659             config_dock_pos = OB_DIRECTION_NORTHWEST;
660         else if (parse_contains("Top", doc, n))
661             config_dock_floating = FALSE,
662             config_dock_pos = OB_DIRECTION_NORTH;
663         else if (parse_contains("TopRight", doc, n))
664             config_dock_floating = FALSE,
665             config_dock_pos = OB_DIRECTION_NORTHEAST;
666         else if (parse_contains("Right", doc, n))
667             config_dock_floating = FALSE,
668             config_dock_pos = OB_DIRECTION_EAST;
669         else if (parse_contains("BottomRight", doc, n))
670             config_dock_floating = FALSE,
671             config_dock_pos = OB_DIRECTION_SOUTHEAST;
672         else if (parse_contains("Bottom", doc, n))
673             config_dock_floating = FALSE,
674             config_dock_pos = OB_DIRECTION_SOUTH;
675         else if (parse_contains("BottomLeft", doc, n))
676             config_dock_floating = FALSE,
677             config_dock_pos = OB_DIRECTION_SOUTHWEST;
678         else if (parse_contains("Left", doc, n))
679             config_dock_floating = FALSE,
680             config_dock_pos = OB_DIRECTION_WEST;
681         else if (parse_contains("Floating", doc, n))
682             config_dock_floating = TRUE;
683     }
684     if (config_dock_floating) {
685         if ((n = parse_find_node("floatingX", node)))
686             config_dock_x = parse_int(doc, n);
687         if ((n = parse_find_node("floatingY", node)))
688             config_dock_y = parse_int(doc, n);
689     } else {
690         if ((n = parse_find_node("noStrut", node)))
691             config_dock_nostrut = parse_bool(doc, n);
692     }
693     if ((n = parse_find_node("stacking", node))) {
694         if (parse_contains("above", doc, n))
695             config_dock_layer = OB_STACKING_LAYER_ABOVE;
696         else if (parse_contains("normal", doc, n))
697             config_dock_layer = OB_STACKING_LAYER_NORMAL;
698         else if (parse_contains("below", doc, n))
699             config_dock_layer = OB_STACKING_LAYER_BELOW;
700     }
701     if ((n = parse_find_node("direction", node))) {
702         if (parse_contains("horizontal", doc, n))
703             config_dock_orient = OB_ORIENTATION_HORZ;
704         else if (parse_contains("vertical", doc, n))
705             config_dock_orient = OB_ORIENTATION_VERT;
706     }
707     if ((n = parse_find_node("autoHide", node)))
708         config_dock_hide = parse_bool(doc, n);
709     if ((n = parse_find_node("hideDelay", node)))
710         config_dock_hide_delay = parse_int(doc, n) * 1000;
711     if ((n = parse_find_node("showDelay", node)))
712         config_dock_show_delay = parse_int(doc, n) * 1000;
713     if ((n = parse_find_node("moveButton", node))) {
714         gchar *str = parse_string(doc, n);
715         guint b, s;
716         if (translate_button(str, &s, &b)) {
717             config_dock_app_move_button = b;
718             config_dock_app_move_modifiers = s;
719         } else {
720             g_message(_("Invalid button '%s' specified in config file"), str);
721         }
722         g_free(str);
723     }
724 }
725
726 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
727                        gpointer d)
728 {
729     xmlNodePtr n;
730     for (node = node->children; node; node = node->next) {
731         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
732             gchar *c;
733
734             c = parse_string(doc, node);
735             config_menu_files = g_slist_append(config_menu_files,
736                                                parse_expand_tilde(c));
737             g_free(c);
738         }
739         if ((n = parse_find_node("hideDelay", node)))
740             config_menu_hide_delay = parse_int(doc, n);
741         if ((n = parse_find_node("middle", node)))
742             config_menu_middle = parse_bool(doc, n);
743         if ((n = parse_find_node("submenuShowDelay", node)))
744             config_submenu_show_delay = parse_int(doc, n);
745         if ((n = parse_find_node("applicationIcons", node)))
746             config_menu_client_list_icons = parse_bool(doc, n);
747     }
748 }
749    
750 static void parse_resistance(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node, 
751                              gpointer d)
752 {
753     xmlNodePtr n;
754
755     node = node->children;
756     if ((n = parse_find_node("strength", node)))
757         config_resist_win = parse_int(doc, n);
758     if ((n = parse_find_node("screen_edge_strength", node)))
759         config_resist_edge = parse_int(doc, n);
760 }
761
762 typedef struct
763 {
764     const gchar *key;
765     const gchar *actname;
766 } ObDefKeyBind;
767
768 static void bind_default_keyboard()
769 {
770     ObDefKeyBind *it;
771     ObDefKeyBind binds[] = {
772         { "A-Tab", "NextWindow" },
773         { "S-A-Tab", "PreviousWindow" },
774         { "A-F4", "Close" },
775         { NULL, NULL }
776     };
777
778     for (it = binds; it->key; ++it) {
779         GList *l = g_list_append(NULL, g_strdup(it->key));
780         keyboard_bind(l, action_from_string(it->actname,
781                                             OB_USER_ACTION_KEYBOARD_KEY));
782     }
783 }
784
785 typedef struct
786 {
787     const gchar *button;
788     const gchar *context;
789     const ObMouseAction mact;
790     const gchar *actname;
791 } ObDefMouseBind;
792
793 static void bind_default_mouse()
794 {
795     ObDefMouseBind *it;
796     ObDefMouseBind binds[] = {
797         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
798         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
799         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
800         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
801         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
802         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
803         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
804         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
805         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
806         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
807         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
808         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
809         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
810         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
811         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
812         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
813         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
814         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
815         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
816         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
817         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
818         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
819         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
820         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
821         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
822         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
823         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
824         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
825         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
826         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
827         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
828         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
829         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximizeFull" },
830         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
831         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
832         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
833         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
834         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
835         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
836         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
837         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
838         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
839         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
840         { NULL, NULL, 0, NULL }
841     };
842
843     for (it = binds; it->button; ++it) {
844         ObUserAction uact;
845         switch (it->mact) {
846         case OB_MOUSE_ACTION_PRESS:
847             uact = OB_USER_ACTION_MOUSE_PRESS; break;
848         case OB_MOUSE_ACTION_RELEASE:
849             uact = OB_USER_ACTION_MOUSE_RELEASE; break;
850         case OB_MOUSE_ACTION_CLICK:
851             uact = OB_USER_ACTION_MOUSE_CLICK; break;
852         case OB_MOUSE_ACTION_DOUBLE_CLICK:
853             uact = OB_USER_ACTION_MOUSE_DOUBLE_CLICK; break;
854         case OB_MOUSE_ACTION_MOTION:
855             uact = OB_USER_ACTION_MOUSE_MOTION; break;
856         default:
857             g_assert_not_reached();
858         }
859         mouse_bind(it->button, it->context, it->mact,
860                    action_from_string(it->actname, uact));
861     }
862 }
863
864 void config_startup(ObParseInst *i)
865 {
866     config_focus_new = TRUE;
867     config_focus_follow = FALSE;
868     config_focus_delay = 0;
869     config_focus_raise = FALSE;
870     config_focus_last = TRUE;
871     config_focus_under_mouse = FALSE;
872
873     parse_register(i, "focus", parse_focus, NULL);
874
875     config_place_policy = OB_PLACE_POLICY_SMART;
876     config_place_center = TRUE;
877
878     parse_register(i, "placement", parse_placement, NULL);
879
880     config_theme = NULL;
881
882     config_animate_iconify = TRUE;
883     config_title_layout = g_strdup("NLIMC");
884     config_theme_keepborder = TRUE;
885
886     config_font_activewindow = NULL;
887     config_font_inactivewindow = NULL;
888     config_font_menuitem = NULL;
889     config_font_menutitle = NULL;
890
891     parse_register(i, "theme", parse_theme, NULL);
892
893     config_desktops_num = 4;
894     config_screen_firstdesk = 1;
895     config_desktops_names = NULL;
896
897     parse_register(i, "desktops", parse_desktops, NULL);
898
899     config_resize_redraw = TRUE;
900     config_resize_four_corners = FALSE;
901     config_resize_popup_show = 1; /* nonpixel increments */
902     config_resize_popup_pos = 0;  /* center of client */
903
904     parse_register(i, "resize", parse_resize, NULL);
905
906     config_dock_layer = OB_STACKING_LAYER_ABOVE;
907     config_dock_pos = OB_DIRECTION_NORTHEAST;
908     config_dock_floating = FALSE;
909     config_dock_nostrut = FALSE;
910     config_dock_x = 0;
911     config_dock_y = 0;
912     config_dock_orient = OB_ORIENTATION_VERT;
913     config_dock_hide = FALSE;
914     config_dock_hide_delay = 300;
915     config_dock_show_delay = 300;
916     config_dock_app_move_button = 2; /* middle */
917     config_dock_app_move_modifiers = 0;
918
919     parse_register(i, "dock", parse_dock, NULL);
920
921     translate_key("C-g", &config_keyboard_reset_state,
922                   &config_keyboard_reset_keycode);
923
924     bind_default_keyboard();
925
926     parse_register(i, "keyboard", parse_keyboard, NULL);
927
928     config_mouse_threshold = 8;
929     config_mouse_dclicktime = 200;
930
931     bind_default_mouse();
932
933     parse_register(i, "mouse", parse_mouse, NULL);
934
935     config_resist_win = 10;
936     config_resist_edge = 20;
937
938     parse_register(i, "resistance", parse_resistance, NULL);
939
940     config_menu_hide_delay = 250;
941     config_menu_middle = FALSE;
942     config_submenu_show_delay = 0;
943     config_menu_client_list_icons = TRUE;
944     config_menu_files = NULL;
945
946     parse_register(i, "menu", parse_menu, NULL);
947
948     config_per_app_settings = NULL;
949
950     parse_register(i, "applications", parse_per_app_settings, NULL);
951 }
952
953 void config_shutdown()
954 {
955     GSList *it;
956
957     g_free(config_theme);
958
959     g_free(config_title_layout);
960
961     RrFontClose(config_font_activewindow);
962     RrFontClose(config_font_inactivewindow);
963     RrFontClose(config_font_menuitem);
964     RrFontClose(config_font_menutitle);
965
966     for (it = config_desktops_names; it; it = g_slist_next(it))
967         g_free(it->data);
968     g_slist_free(config_desktops_names);
969
970     for (it = config_menu_files; it; it = g_slist_next(it))
971         g_free(it->data);
972     g_slist_free(config_menu_files);
973
974     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
975         ObAppSettings *itd = (ObAppSettings *)it->data;
976         if (itd->name)  g_pattern_spec_free(itd->name);
977         if (itd->role)  g_pattern_spec_free(itd->role);
978         if (itd->class) g_pattern_spec_free(itd->class);
979         g_free(it->data);
980     }
981     g_slist_free(config_per_app_settings);
982 }