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