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