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