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