]> icculus.org git repositories - dana/openbox.git/blob - openbox/config.c
56e625d4c3d9c712cc25bc447c6e59e0d8e1c8ca
[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 gboolean      config_place_active;
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("active", node)))
495         config_place_active = parse_bool(doc, n);
496 }
497
498 static void parse_margins(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
499                           gpointer data)
500 {
501     xmlNodePtr n;
502
503     node = node->children;
504
505     if ((n = parse_find_node("top", node)))
506         config_margins.top = MAX(0, parse_int(doc, n));
507     if ((n = parse_find_node("left", node)))
508         config_margins.left = MAX(0, parse_int(doc, n));
509     if ((n = parse_find_node("right", node)))
510         config_margins.right = MAX(0, parse_int(doc, n));
511     if ((n = parse_find_node("bottom", node)))
512         config_margins.bottom = MAX(0, parse_int(doc, n));
513 }
514
515 static void parse_theme(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
516                         gpointer data)
517 {
518     xmlNodePtr n;
519
520     node = node->children;
521
522     if ((n = parse_find_node("name", node))) {
523         gchar *c;
524
525         g_free(config_theme);
526         c = parse_string(doc, n);
527         config_theme = parse_expand_tilde(c);
528         g_free(c);
529     }
530     if ((n = parse_find_node("titleLayout", node))) {
531         gchar *c, *d;
532
533         g_free(config_title_layout);
534         config_title_layout = parse_string(doc, n);
535
536         /* replace duplicates with spaces */
537         for (c = config_title_layout; *c != '\0'; ++c)
538             for (d = c+1; *d != '\0'; ++d)
539                 if (*c == *d) *d = ' ';
540     }
541     if ((n = parse_find_node("keepBorder", node)))
542         config_theme_keepborder = parse_bool(doc, n);
543     if ((n = parse_find_node("animateIconify", node)))
544         config_animate_iconify = parse_bool(doc, n);
545
546     n = parse_find_node("font", node);
547     while (n) {
548         xmlNodePtr   fnode;
549         RrFont     **font;
550         gchar       *name = g_strdup(RrDefaultFontFamily);
551         gint         size = RrDefaultFontSize;
552         RrFontWeight weight = RrDefaultFontWeight;
553         RrFontSlant  slant = RrDefaultFontSlant;
554
555         if (parse_attr_contains("ActiveWindow", n, "place"))
556             font = &config_font_activewindow;
557         else if (parse_attr_contains("InactiveWindow", n, "place"))
558             font = &config_font_inactivewindow;
559         else if (parse_attr_contains("MenuHeader", n, "place"))
560             font = &config_font_menutitle;
561         else if (parse_attr_contains("MenuItem", n, "place"))
562             font = &config_font_menuitem;
563         else if (parse_attr_contains("OnScreenDisplay", n, "place"))
564             font = &config_font_osd;
565         else
566             goto next_font;
567
568         if ((fnode = parse_find_node("name", n->children))) {
569             g_free(name);
570             name = parse_string(doc, fnode);
571         }
572         if ((fnode = parse_find_node("size", n->children))) {
573             int s = parse_int(doc, fnode);
574             if (s > 0) size = s;
575         }
576         if ((fnode = parse_find_node("weight", n->children))) {
577             gchar *w = parse_string(doc, fnode);
578             if (!g_ascii_strcasecmp(w, "Bold"))
579                 weight = RR_FONTWEIGHT_BOLD;
580             g_free(w);
581         }
582         if ((fnode = parse_find_node("slant", n->children))) {
583             gchar *s = parse_string(doc, fnode);
584             if (!g_ascii_strcasecmp(s, "Italic"))
585                 slant = RR_FONTSLANT_ITALIC;
586             if (!g_ascii_strcasecmp(s, "Oblique"))
587                 slant = RR_FONTSLANT_OBLIQUE;
588             g_free(s);
589         }
590
591         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
592         g_free(name);
593     next_font:
594         n = parse_find_node("font", n->next);
595     }
596 }
597
598 static void parse_desktops(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
599                            gpointer data)
600 {
601     xmlNodePtr n;
602
603     node = node->children;
604
605     if ((n = parse_find_node("number", node))) {
606         gint d = parse_int(doc, n);
607         if (d > 0)
608             config_desktops_num = (unsigned) d;
609     }
610     if ((n = parse_find_node("firstdesk", node))) {
611         gint d = parse_int(doc, n);
612         if (d > 0)
613             config_screen_firstdesk = (unsigned) d;
614     }
615     if ((n = parse_find_node("names", node))) {
616         GSList *it;
617         xmlNodePtr nname;
618
619         for (it = config_desktops_names; it; it = it->next)
620             g_free(it->data);
621         g_slist_free(config_desktops_names);
622         config_desktops_names = NULL;
623
624         nname = parse_find_node("name", n->children);
625         while (nname) {
626             config_desktops_names = g_slist_append(config_desktops_names,
627                                                    parse_string(doc, nname));
628             nname = parse_find_node("name", nname->next);
629         }
630     }
631     if ((n = parse_find_node("popupTime", node)))
632         config_desktop_popup_time = parse_int(doc, n);
633 }
634
635 static void parse_resize(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
636                          gpointer data)
637 {
638     xmlNodePtr n;
639
640     node = node->children;
641
642     if ((n = parse_find_node("drawContents", node)))
643         config_resize_redraw = parse_bool(doc, n);
644     if ((n = parse_find_node("popupShow", node))) {
645         config_resize_popup_show = parse_int(doc, n);
646         if (parse_contains("Always", doc, n))
647             config_resize_popup_show = 2;
648         else if (parse_contains("Never", doc, n))
649             config_resize_popup_show = 0;
650         else if (parse_contains("Nonpixel", doc, n))
651             config_resize_popup_show = 1;
652     }
653     if ((n = parse_find_node("popupPosition", node))) {
654         if (parse_contains("Top", doc, n))
655             config_resize_popup_pos = OB_RESIZE_POS_TOP;
656         else if (parse_contains("Center", doc, n))
657             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
658         else if (parse_contains("Fixed", doc, n)) {
659             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
660
661             if ((n = parse_find_node("popupFixedPosition", node))) {
662                 xmlNodePtr n2;
663
664                 if ((n2 = parse_find_node("x", n->children)))
665                     config_parse_gravity_coord(doc, n2,
666                                                &config_resize_popup_fixed.x);
667                 if ((n2 = parse_find_node("y", n->children)))
668                     config_parse_gravity_coord(doc, n2,
669                                                &config_resize_popup_fixed.y);
670             }
671         }
672     }
673 }
674
675 static void parse_dock(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
676                        gpointer data)
677 {
678     xmlNodePtr n;
679
680     node = node->children;
681
682     if ((n = parse_find_node("position", node))) {
683         if (parse_contains("TopLeft", doc, n))
684             config_dock_floating = FALSE,
685             config_dock_pos = OB_DIRECTION_NORTHWEST;
686         else if (parse_contains("Top", doc, n))
687             config_dock_floating = FALSE,
688             config_dock_pos = OB_DIRECTION_NORTH;
689         else if (parse_contains("TopRight", doc, n))
690             config_dock_floating = FALSE,
691             config_dock_pos = OB_DIRECTION_NORTHEAST;
692         else if (parse_contains("Right", doc, n))
693             config_dock_floating = FALSE,
694             config_dock_pos = OB_DIRECTION_EAST;
695         else if (parse_contains("BottomRight", doc, n))
696             config_dock_floating = FALSE,
697             config_dock_pos = OB_DIRECTION_SOUTHEAST;
698         else if (parse_contains("Bottom", doc, n))
699             config_dock_floating = FALSE,
700             config_dock_pos = OB_DIRECTION_SOUTH;
701         else if (parse_contains("BottomLeft", doc, n))
702             config_dock_floating = FALSE,
703             config_dock_pos = OB_DIRECTION_SOUTHWEST;
704         else if (parse_contains("Left", doc, n))
705             config_dock_floating = FALSE,
706             config_dock_pos = OB_DIRECTION_WEST;
707         else if (parse_contains("Floating", doc, n))
708             config_dock_floating = TRUE;
709     }
710     if (config_dock_floating) {
711         if ((n = parse_find_node("floatingX", node)))
712             config_dock_x = parse_int(doc, n);
713         if ((n = parse_find_node("floatingY", node)))
714             config_dock_y = parse_int(doc, n);
715     } else {
716         if ((n = parse_find_node("noStrut", node)))
717             config_dock_nostrut = parse_bool(doc, n);
718     }
719     if ((n = parse_find_node("stacking", node))) {
720         if (parse_contains("above", doc, n))
721             config_dock_layer = OB_STACKING_LAYER_ABOVE;
722         else if (parse_contains("normal", doc, n))
723             config_dock_layer = OB_STACKING_LAYER_NORMAL;
724         else if (parse_contains("below", doc, n))
725             config_dock_layer = OB_STACKING_LAYER_BELOW;
726     }
727     if ((n = parse_find_node("direction", node))) {
728         if (parse_contains("horizontal", doc, n))
729             config_dock_orient = OB_ORIENTATION_HORZ;
730         else if (parse_contains("vertical", doc, n))
731             config_dock_orient = OB_ORIENTATION_VERT;
732     }
733     if ((n = parse_find_node("autoHide", node)))
734         config_dock_hide = parse_bool(doc, n);
735     if ((n = parse_find_node("hideDelay", node)))
736         config_dock_hide_delay = parse_int(doc, n);
737     if ((n = parse_find_node("showDelay", node)))
738         config_dock_show_delay = parse_int(doc, n);
739     if ((n = parse_find_node("moveButton", node))) {
740         gchar *str = parse_string(doc, n);
741         guint b, s;
742         if (translate_button(str, &s, &b)) {
743             config_dock_app_move_button = b;
744             config_dock_app_move_modifiers = s;
745         } else {
746             g_message(_("Invalid button '%s' specified in config file"), str);
747         }
748         g_free(str);
749     }
750 }
751
752 static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
753                        gpointer data)
754 {
755     xmlNodePtr n;
756     for (node = node->children; node; node = node->next) {
757         if (!xmlStrcasecmp(node->name, (const xmlChar*) "file")) {
758             gchar *c;
759
760             c = parse_string(doc, node);
761             config_menu_files = g_slist_append(config_menu_files,
762                                                parse_expand_tilde(c));
763             g_free(c);
764         }
765         if ((n = parse_find_node("hideDelay", node)))
766             config_menu_hide_delay = parse_int(doc, n);
767         if ((n = parse_find_node("middle", node)))
768             config_menu_middle = parse_bool(doc, n);
769         if ((n = parse_find_node("submenuShowDelay", node)))
770             config_submenu_show_delay = parse_int(doc, n);
771         if ((n = parse_find_node("applicationIcons", node)))
772             config_menu_client_list_icons = parse_bool(doc, n);
773     }
774 }
775
776 static void parse_resistance(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
777                              gpointer data)
778 {
779     xmlNodePtr n;
780
781     node = node->children;
782     if ((n = parse_find_node("strength", node)))
783         config_resist_win = parse_int(doc, n);
784     if ((n = parse_find_node("screen_edge_strength", node)))
785         config_resist_edge = parse_int(doc, n);
786 }
787
788 typedef struct
789 {
790     const gchar *key;
791     const gchar *actname;
792 } ObDefKeyBind;
793
794 static void bind_default_keyboard(void)
795 {
796     ObDefKeyBind *it;
797     ObDefKeyBind binds[] = {
798         { "A-Tab", "NextWindow" },
799         { "S-A-Tab", "PreviousWindow" },
800         { "A-F4", "Close" },
801         { NULL, NULL }
802     };
803     for (it = binds; it->key; ++it) {
804         GList *l = g_list_append(NULL, g_strdup(it->key));
805         keyboard_bind(l, actions_parse_string(it->actname));
806     }
807 }
808
809 typedef struct
810 {
811     const gchar *button;
812     const gchar *context;
813     const ObMouseAction mact;
814     const gchar *actname;
815 } ObDefMouseBind;
816
817 static void bind_default_mouse(void)
818 {
819     ObDefMouseBind *it;
820     ObDefMouseBind binds[] = {
821         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
822         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
823         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
824         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
825         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
826         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
827         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
828         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
829         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
830         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
831         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
832         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
833         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
834         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
835         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
836         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
837         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
838         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
839         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
840         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
841         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
842         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
843         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
844         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
845         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
846         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
847         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
848         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
849         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
850         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
851         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
852         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
853         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
854         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
855         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
856         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
857         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
858         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
859         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
860         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
861         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
862         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
863         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
864         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
865         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
866         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
867         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
868         { NULL, NULL, 0, NULL }
869     };
870
871     for (it = binds; it->button; ++it)
872         mouse_bind(it->button, it->context, it->mact,
873                    actions_parse_string(it->actname));
874 }
875
876 void config_startup(ObParseInst *i)
877 {
878     config_focus_new = TRUE;
879     config_focus_follow = FALSE;
880     config_focus_delay = 0;
881     config_focus_raise = FALSE;
882     config_focus_last = TRUE;
883     config_focus_under_mouse = FALSE;
884
885     parse_register(i, "focus", parse_focus, NULL);
886
887     config_place_policy = OB_PLACE_POLICY_SMART;
888     config_place_center = TRUE;
889     config_place_active = FALSE;
890
891     parse_register(i, "placement", parse_placement, NULL);
892
893     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
894
895     parse_register(i, "margins", parse_margins, NULL);
896
897     config_theme = NULL;
898
899     config_animate_iconify = TRUE;
900     config_title_layout = g_strdup("NLIMC");
901     config_theme_keepborder = TRUE;
902
903     config_font_activewindow = NULL;
904     config_font_inactivewindow = NULL;
905     config_font_menuitem = NULL;
906     config_font_menutitle = NULL;
907
908     parse_register(i, "theme", parse_theme, NULL);
909
910     config_desktops_num = 4;
911     config_screen_firstdesk = 1;
912     config_desktops_names = NULL;
913     config_desktop_popup_time = 875;
914
915     parse_register(i, "desktops", parse_desktops, NULL);
916
917     config_resize_redraw = TRUE;
918     config_resize_popup_show = 1; /* nonpixel increments */
919     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
920     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
921     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
922
923     parse_register(i, "resize", parse_resize, NULL);
924
925     config_dock_layer = OB_STACKING_LAYER_ABOVE;
926     config_dock_pos = OB_DIRECTION_NORTHEAST;
927     config_dock_floating = FALSE;
928     config_dock_nostrut = FALSE;
929     config_dock_x = 0;
930     config_dock_y = 0;
931     config_dock_orient = OB_ORIENTATION_VERT;
932     config_dock_hide = FALSE;
933     config_dock_hide_delay = 300;
934     config_dock_show_delay = 300;
935     config_dock_app_move_button = 2; /* middle */
936     config_dock_app_move_modifiers = 0;
937
938     parse_register(i, "dock", parse_dock, NULL);
939
940     translate_key("C-g", &config_keyboard_reset_state,
941                   &config_keyboard_reset_keycode);
942
943     bind_default_keyboard();
944
945     parse_register(i, "keyboard", parse_keyboard, NULL);
946
947     config_mouse_threshold = 8;
948     config_mouse_dclicktime = 200;
949     config_mouse_screenedgetime = 400;
950
951     bind_default_mouse();
952
953     parse_register(i, "mouse", parse_mouse, NULL);
954
955     config_resist_win = 10;
956     config_resist_edge = 20;
957
958     parse_register(i, "resistance", parse_resistance, NULL);
959
960     config_menu_hide_delay = 250;
961     config_menu_middle = FALSE;
962     config_submenu_show_delay = 0;
963     config_menu_client_list_icons = TRUE;
964     config_menu_files = NULL;
965
966     parse_register(i, "menu", parse_menu, NULL);
967
968     config_per_app_settings = NULL;
969
970     parse_register(i, "applications", parse_per_app_settings, NULL);
971 }
972
973 void config_shutdown(void)
974 {
975     GSList *it;
976
977     g_free(config_theme);
978
979     g_free(config_title_layout);
980
981     RrFontClose(config_font_activewindow);
982     RrFontClose(config_font_inactivewindow);
983     RrFontClose(config_font_menuitem);
984     RrFontClose(config_font_menutitle);
985
986     for (it = config_desktops_names; it; it = g_slist_next(it))
987         g_free(it->data);
988     g_slist_free(config_desktops_names);
989
990     for (it = config_menu_files; it; it = g_slist_next(it))
991         g_free(it->data);
992     g_slist_free(config_menu_files);
993
994     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
995         ObAppSettings *itd = (ObAppSettings *)it->data;
996         if (itd->name)  g_pattern_spec_free(itd->name);
997         if (itd->role)  g_pattern_spec_free(itd->role);
998         if (itd->class) g_pattern_spec_free(itd->class);
999         g_free(it->data);
1000     }
1001     g_slist_free(config_per_app_settings);
1002 }