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