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