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