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