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