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