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