889bbcb378ee384e2cbe43f072150dc92605ed2e
[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
89 gint     config_mouse_threshold;
90 gint     config_mouse_dclicktime;
91 gint     config_mouse_screenedgetime;
92 gboolean config_mouse_screenedgewarp;
93
94 guint    config_menu_hide_delay;
95 gboolean config_menu_middle;
96 guint    config_submenu_show_delay;
97 guint    config_submenu_hide_delay;
98 gboolean config_menu_manage_desktops;
99 gboolean config_menu_show_icons;
100
101 GSList *config_menu_files;
102
103 gint     config_resist_win;
104 gint     config_resist_edge;
105
106 GSList *config_per_app_settings;
107
108 ObAppSettings* config_create_app_settings(void)
109 {
110     ObAppSettings *settings = g_slice_new0(ObAppSettings);
111     settings->type = -1;
112     settings->decor = -1;
113     settings->shade = -1;
114     settings->monitor_type = OB_PLACE_MONITOR_ANY;
115     settings->monitor = -1;
116     settings->focus = -1;
117     settings->desktop = 0;
118     settings->layer = -2;
119     settings->iconic = -1;
120     settings->skip_pager = -1;
121     settings->skip_taskbar = -1;
122     settings->fullscreen = -1;
123     settings->max_horz = -1;
124     settings->max_vert = -1;
125     return settings;
126 }
127
128 #define copy_if(setting, default) \
129   if (src->setting != default) dst->setting = src->setting
130 void config_app_settings_copy_non_defaults(const ObAppSettings *src,
131                                            ObAppSettings *dst)
132 {
133     g_assert(src != NULL);
134     g_assert(dst != NULL);
135
136     copy_if(type, (ObClientType)-1);
137     copy_if(decor, -1);
138     copy_if(shade, -1);
139     copy_if(monitor_type, OB_PLACE_MONITOR_ANY);
140     copy_if(monitor, -1);
141     copy_if(focus, -1);
142     copy_if(desktop, 0);
143     copy_if(layer, -2);
144     copy_if(iconic, -1);
145     copy_if(skip_pager, -1);
146     copy_if(skip_taskbar, -1);
147     copy_if(fullscreen, -1);
148     copy_if(max_horz, -1);
149     copy_if(max_vert, -1);
150
151     if (src->pos_given) {
152         dst->pos_given = TRUE;
153         dst->pos_force = src->pos_force;
154         dst->position = src->position;
155         /* monitor is copied above */
156     }
157
158     if (src->size_given) {
159         dst->size_given = TRUE;
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
167 void config_parse_relative_number(gchar *s, gint *num, gint *denom)
168 {
169     *num = strtol(s, &s, 10);
170
171     if (*s == '%') {
172         *denom = 100;
173     } else if (*s == '/') {
174         *denom = atoi(s+1);
175     }
176 }
177
178 void config_parse_gravity_coord(xmlNodePtr node, GravityCoord *c)
179 {
180     gchar *s = obt_xml_node_string(node);
181     if (!g_ascii_strcasecmp(s, "center"))
182         c->center = TRUE;
183     else {
184         gchar *ps = s;
185         if (s[0] == '-')
186             c->opposite = TRUE;
187         if (s[0] == '-' || s[0] == '+')
188             ps++;
189         config_parse_relative_number(ps, &c->pos, &c->denom);
190     }
191     g_free(s);
192 }
193
194 /*
195   <applications>
196     <application name="aterm">
197       <decor>false</decor>
198     </application>
199     <application name="Rhythmbox">
200       <layer>above</layer>
201       <position>
202         <x>700</x>
203         <y>0</y>
204         <monitor>1</monitor>
205       </position>
206       .. there is a lot more settings available
207     </application>
208   </applications>
209 */
210
211 static void parse_single_per_app_settings(xmlNodePtr app,
212                                           ObAppSettings *settings)
213 {
214     xmlNodePtr n, c;
215     gboolean x_pos_given = FALSE;
216     gboolean width_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                     width_given = TRUE;
269                 g_free(s);
270             }
271         }
272
273         if (width_given && (c = obt_xml_find_node(n->children, "height"))) {
274             gchar *s = obt_xml_node_string(c);
275             config_parse_relative_number(s,
276                                          &settings->height_num,
277                                          &settings->height_denom);
278             if (settings->height_num > 0 && settings->height_denom >= 0)
279                 settings->size_given = TRUE;
280             g_free(s);
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
512 /*
513
514 <context name="Titlebar">
515   <mousebind button="Left" action="Press">
516     <action name="Raise"></action>
517   </mousebind>
518 </context>
519
520 */
521
522 static void parse_mouse(xmlNodePtr node, gpointer d)
523 {
524     xmlNodePtr n, nbut, nact;
525     gchar *buttonstr;
526     gchar *cxstr;
527     ObMouseAction mact;
528
529     mouse_unbind_all();
530
531     node = node->children;
532
533     if ((n = obt_xml_find_node(node, "dragThreshold")))
534         config_mouse_threshold = obt_xml_node_int(n);
535     if ((n = obt_xml_find_node(node, "doubleClickTime")))
536         config_mouse_dclicktime = obt_xml_node_int(n);
537     if ((n = obt_xml_find_node(node, "screenEdgeWarpTime"))) {
538         config_mouse_screenedgetime = obt_xml_node_int(n);
539         /* minimum value of 25 for this property, when it is 1 and you hit the
540            edge it basically never stops */
541         if (config_mouse_screenedgetime && config_mouse_screenedgetime < 25)
542             config_mouse_screenedgetime = 25;
543     }
544     if ((n = obt_xml_find_node(node, "screenEdgeWarpMouse")))
545         config_mouse_screenedgewarp = obt_xml_node_bool(n);
546
547     n = obt_xml_find_node(node, "context");
548     while (n) {
549         gchar *modcxstr;
550         ObFrameContext cx;
551
552         if (!obt_xml_attr_string(n, "name", &cxstr))
553             goto next_n;
554
555         modcxstr = g_strdup(cxstr); /* make a copy to mutilate */
556         while (frame_next_context_from_string(modcxstr, &cx)) {
557             if (!cx) {
558                 gchar *s = strchr(modcxstr, ' ');
559                 if (s) {
560                     *s = '\0';
561                     g_message(_("Invalid context \"%s\" in mouse binding"),
562                               modcxstr);
563                     *s = ' ';
564                 }
565                 continue;
566             }
567
568             nbut = obt_xml_find_node(n->children, "mousebind");
569             while (nbut) {
570                 if (!obt_xml_attr_string(nbut, "button", &buttonstr))
571                     goto next_nbut;
572                 if (obt_xml_attr_contains(nbut, "action", "press"))
573                     mact = OB_MOUSE_ACTION_PRESS;
574                 else if (obt_xml_attr_contains(nbut, "action", "release"))
575                     mact = OB_MOUSE_ACTION_RELEASE;
576                 else if (obt_xml_attr_contains(nbut, "action", "click"))
577                     mact = OB_MOUSE_ACTION_CLICK;
578                 else if (obt_xml_attr_contains(nbut, "action","doubleclick"))
579                     mact = OB_MOUSE_ACTION_DOUBLE_CLICK;
580                 else if (obt_xml_attr_contains(nbut, "action", "drag"))
581                     mact = OB_MOUSE_ACTION_MOTION;
582                 else
583                     goto next_nbut;
584
585                 nact = obt_xml_find_node(nbut->children, "action");
586                 while (nact) {
587                     ObActionsAct *action;
588
589                     if ((action = actions_parse(nact))) {
590                         gchar *p = buttonstr;
591                         while (*p) {
592                             gchar *s = strchr(p, ' ');
593                             if (s) {
594                                 *s = '\0';
595                             } else {
596                                 s = p;
597                                 while (*++s);
598                                 s--;
599                             }
600                             mouse_bind(p, cx, mact, action);
601                             actions_act_ref(action); /* ref the action for each binding */
602                             p = s+1;
603                         }
604                         actions_act_unref(action); /* remove the extra ref */
605                     }
606                     nact = obt_xml_find_node(nact->next, "action");
607                 }
608             g_free(buttonstr);
609             next_nbut:
610             nbut = obt_xml_find_node(nbut->next, "mousebind");
611             }
612         }
613         g_free(modcxstr);
614         g_free(cxstr);
615     next_n:
616         n = obt_xml_find_node(n->next, "context");
617     }
618 }
619
620 static void parse_focus(xmlNodePtr node, gpointer d)
621 {
622     xmlNodePtr n;
623
624     node = node->children;
625
626     if ((n = obt_xml_find_node(node, "focusNew")))
627         config_focus_new = obt_xml_node_bool(n);
628     if ((n = obt_xml_find_node(node, "followMouse")))
629         config_focus_follow = obt_xml_node_bool(n);
630     if ((n = obt_xml_find_node(node, "focusDelay")))
631         config_focus_delay = obt_xml_node_int(n);
632     if ((n = obt_xml_find_node(node, "raiseOnFocus")))
633         config_focus_raise = obt_xml_node_bool(n);
634     if ((n = obt_xml_find_node(node, "focusLast")))
635         config_focus_last = obt_xml_node_bool(n);
636     if ((n = obt_xml_find_node(node, "underMouse")))
637         config_focus_under_mouse = obt_xml_node_bool(n);
638     if ((n = obt_xml_find_node(node, "unfocusOnLeave")))
639         config_unfocus_leave = obt_xml_node_bool(n);
640 }
641
642 static void parse_placement(xmlNodePtr node, gpointer d)
643 {
644     xmlNodePtr n;
645
646     node = node->children;
647
648     if ((n = obt_xml_find_node(node, "policy")))
649         if (obt_xml_node_contains(n, "UnderMouse"))
650             config_place_policy = OB_PLACE_POLICY_MOUSE;
651         if (obt_xml_node_contains(n, "Random"))
652             config_place_policy = OB_PLACE_POLICY_RANDOM;
653     if ((n = obt_xml_find_node(node, "monitor"))) {
654         if (obt_xml_node_contains(n, "active"))
655             config_place_monitor = OB_PLACE_MONITOR_ACTIVE;
656         else if (obt_xml_node_contains(n, "mouse"))
657             config_place_monitor = OB_PLACE_MONITOR_MOUSE;
658         else if (obt_xml_node_contains(n, "any"))
659             config_place_monitor = OB_PLACE_MONITOR_ANY;
660     }
661     if ((n = obt_xml_find_node(node, "primaryMonitor"))) {
662         config_primary_monitor_index = obt_xml_node_int(n);
663         if (!config_primary_monitor_index) {
664             if (obt_xml_node_contains(n, "mouse"))
665                 config_primary_monitor = OB_PLACE_MONITOR_MOUSE;
666         }
667     }
668 }
669
670 static void parse_margins(xmlNodePtr node, gpointer d)
671 {
672     xmlNodePtr n;
673
674     node = node->children;
675
676     if ((n = obt_xml_find_node(node, "top")))
677         config_margins.top = MAX(0, obt_xml_node_int(n));
678     if ((n = obt_xml_find_node(node, "left")))
679         config_margins.left = MAX(0, obt_xml_node_int(n));
680     if ((n = obt_xml_find_node(node, "right")))
681         config_margins.right = MAX(0, obt_xml_node_int(n));
682     if ((n = obt_xml_find_node(node, "bottom")))
683         config_margins.bottom = MAX(0, obt_xml_node_int(n));
684 }
685
686 static void parse_theme(xmlNodePtr node, gpointer d)
687 {
688     xmlNodePtr n;
689
690     node = node->children;
691
692     if ((n = obt_xml_find_node(node, "name"))) {
693         gchar *c;
694
695         g_free(config_theme);
696         c = obt_xml_node_string(n);
697         config_theme = obt_paths_expand_tilde(c);
698         g_free(c);
699     }
700     if ((n = obt_xml_find_node(node, "titleLayout"))) {
701         gchar *c, *d;
702
703         g_free(config_title_layout);
704         config_title_layout = obt_xml_node_string(n);
705
706         /* replace duplicates with spaces */
707         for (c = config_title_layout; *c != '\0'; ++c)
708             for (d = c+1; *d != '\0'; ++d)
709                 if (*c == *d) *d = ' ';
710     }
711     if ((n = obt_xml_find_node(node, "keepBorder")))
712         config_theme_keepborder = obt_xml_node_bool(n);
713     if ((n = obt_xml_find_node(node, "animateIconify")))
714         config_animate_iconify = obt_xml_node_bool(n);
715     if ((n = obt_xml_find_node(node, "windowListIconSize"))) {
716         config_theme_window_list_icon_size = obt_xml_node_int(n);
717         if (config_theme_window_list_icon_size < 16)
718             config_theme_window_list_icon_size = 16;
719         else if (config_theme_window_list_icon_size > 96)
720             config_theme_window_list_icon_size = 96;
721     }
722
723     n = obt_xml_find_node(node, "font");
724     while (n) {
725         xmlNodePtr   fnode;
726         RrFont     **font;
727         gchar       *name = g_strdup(RrDefaultFontFamily);
728         gint         size = RrDefaultFontSize;
729         RrFontWeight weight = RrDefaultFontWeight;
730         RrFontSlant  slant = RrDefaultFontSlant;
731
732         if (obt_xml_attr_contains(n, "place", "ActiveWindow"))
733             font = &config_font_activewindow;
734         else if (obt_xml_attr_contains(n, "place", "InactiveWindow"))
735             font = &config_font_inactivewindow;
736         else if (obt_xml_attr_contains(n, "place", "MenuHeader"))
737             font = &config_font_menutitle;
738         else if (obt_xml_attr_contains(n, "place", "MenuItem"))
739             font = &config_font_menuitem;
740         else if (obt_xml_attr_contains(n, "place", "ActiveOnScreenDisplay"))
741             font = &config_font_activeosd;
742         else if (obt_xml_attr_contains(n, "place", "OnScreenDisplay"))
743             font = &config_font_activeosd;
744         else if (obt_xml_attr_contains(n, "place","InactiveOnScreenDisplay"))
745             font = &config_font_inactiveosd;
746         else
747             goto next_font;
748
749         if ((fnode = obt_xml_find_node(n->children, "name"))) {
750             g_free(name);
751             name = obt_xml_node_string(fnode);
752         }
753         if ((fnode = obt_xml_find_node(n->children, "size"))) {
754             int s = obt_xml_node_int(fnode);
755             if (s > 0) size = s;
756         }
757         if ((fnode = obt_xml_find_node(n->children, "weight"))) {
758             gchar *w = obt_xml_node_string(fnode);
759             if (!g_ascii_strcasecmp(w, "Bold"))
760                 weight = RR_FONTWEIGHT_BOLD;
761             g_free(w);
762         }
763         if ((fnode = obt_xml_find_node(n->children, "slant"))) {
764             gchar *s = obt_xml_node_string(fnode);
765             if (!g_ascii_strcasecmp(s, "Italic"))
766                 slant = RR_FONTSLANT_ITALIC;
767             if (!g_ascii_strcasecmp(s, "Oblique"))
768                 slant = RR_FONTSLANT_OBLIQUE;
769             g_free(s);
770         }
771
772         *font = RrFontOpen(ob_rr_inst, name, size, weight, slant);
773         g_free(name);
774
775         if ((fnode = obt_xml_find_node(n->children, "description"))) {
776             gchar *s = obt_xml_node_string(fnode);
777             RrFontDescriptionFromString(*font, s);
778             g_free(s);
779         }
780
781     next_font:
782         n = obt_xml_find_node(n->next, "font");
783     }
784 }
785
786 static void parse_desktops(xmlNodePtr node, gpointer d)
787 {
788     xmlNodePtr n;
789
790     node = node->children;
791
792     if ((n = obt_xml_find_node(node, "number"))) {
793         gint d = obt_xml_node_int(n);
794         if (d > 0)
795             config_desktops_num = (unsigned) d;
796     }
797     if ((n = obt_xml_find_node(node, "firstdesk"))) {
798         gint d = obt_xml_node_int(n);
799         if (d > 0)
800             config_screen_firstdesk = (unsigned) d;
801     }
802     if ((n = obt_xml_find_node(node, "emulatexinerama")))
803         config_emulate_xinerama = obt_xml_node_bool(n);
804     if ((n = obt_xml_find_node(node, "names"))) {
805         GSList *it;
806         xmlNodePtr nname;
807
808         for (it = config_desktops_names; it; it = it->next)
809             g_free(it->data);
810         g_slist_free(config_desktops_names);
811         config_desktops_names = NULL;
812
813         nname = obt_xml_find_node(n->children, "name");
814         while (nname) {
815             config_desktops_names =
816                 g_slist_append(config_desktops_names,
817                                obt_xml_node_string(nname));
818             nname = obt_xml_find_node(nname->next, "name");
819         }
820     }
821     if ((n = obt_xml_find_node(node, "popupTime")))
822         config_desktop_popup_time = obt_xml_node_int(n);
823 }
824
825 static void parse_resize(xmlNodePtr node, gpointer d)
826 {
827     xmlNodePtr n;
828
829     node = node->children;
830
831     if ((n = obt_xml_find_node(node, "drawContents")))
832         config_resize_redraw = obt_xml_node_bool(n);
833     if ((n = obt_xml_find_node(node, "popupShow"))) {
834         config_resize_popup_show = obt_xml_node_int(n);
835         if (obt_xml_node_contains(n, "Always"))
836             config_resize_popup_show = 2;
837         else if (obt_xml_node_contains(n, "Never"))
838             config_resize_popup_show = 0;
839         else if (obt_xml_node_contains(n, "Nonpixel"))
840             config_resize_popup_show = 1;
841     }
842     if ((n = obt_xml_find_node(node, "popupPosition"))) {
843         if (obt_xml_node_contains(n, "Top"))
844             config_resize_popup_pos = OB_RESIZE_POS_TOP;
845         else if (obt_xml_node_contains(n, "Center"))
846             config_resize_popup_pos = OB_RESIZE_POS_CENTER;
847         else if (obt_xml_node_contains(n, "Fixed")) {
848             config_resize_popup_pos = OB_RESIZE_POS_FIXED;
849
850             if ((n = obt_xml_find_node(node, "popupFixedPosition"))) {
851                 xmlNodePtr n2;
852
853                 if ((n2 = obt_xml_find_node(n->children, "x")))
854                     config_parse_gravity_coord(n2,
855                                                &config_resize_popup_fixed.x);
856                 if ((n2 = obt_xml_find_node(n->children, "y")))
857                     config_parse_gravity_coord(n2,
858                                                &config_resize_popup_fixed.y);
859
860                 config_resize_popup_fixed.x.pos =
861                     MAX(config_resize_popup_fixed.x.pos, 0);
862                 config_resize_popup_fixed.y.pos =
863                     MAX(config_resize_popup_fixed.y.pos, 0);
864             }
865         }
866     }
867 }
868
869 static void parse_dock(xmlNodePtr node, gpointer d)
870 {
871     xmlNodePtr n;
872
873     node = node->children;
874
875     if ((n = obt_xml_find_node(node, "position"))) {
876         if (obt_xml_node_contains(n, "TopLeft"))
877             config_dock_floating = FALSE,
878             config_dock_pos = OB_DIRECTION_NORTHWEST;
879         else if (obt_xml_node_contains(n, "Top"))
880             config_dock_floating = FALSE,
881             config_dock_pos = OB_DIRECTION_NORTH;
882         else if (obt_xml_node_contains(n, "TopRight"))
883             config_dock_floating = FALSE,
884             config_dock_pos = OB_DIRECTION_NORTHEAST;
885         else if (obt_xml_node_contains(n, "Right"))
886             config_dock_floating = FALSE,
887             config_dock_pos = OB_DIRECTION_EAST;
888         else if (obt_xml_node_contains(n, "BottomRight"))
889             config_dock_floating = FALSE,
890             config_dock_pos = OB_DIRECTION_SOUTHEAST;
891         else if (obt_xml_node_contains(n, "Bottom"))
892             config_dock_floating = FALSE,
893             config_dock_pos = OB_DIRECTION_SOUTH;
894         else if (obt_xml_node_contains(n, "BottomLeft"))
895             config_dock_floating = FALSE,
896             config_dock_pos = OB_DIRECTION_SOUTHWEST;
897         else if (obt_xml_node_contains(n, "Left"))
898             config_dock_floating = FALSE,
899             config_dock_pos = OB_DIRECTION_WEST;
900         else if (obt_xml_node_contains(n, "Floating"))
901             config_dock_floating = TRUE;
902     }
903     if (config_dock_floating) {
904         if ((n = obt_xml_find_node(node, "floatingX")))
905             config_dock_x = obt_xml_node_int(n);
906         if ((n = obt_xml_find_node(node, "floatingY")))
907             config_dock_y = obt_xml_node_int(n);
908     } else {
909         if ((n = obt_xml_find_node(node, "noStrut")))
910             config_dock_nostrut = obt_xml_node_bool(n);
911     }
912     if ((n = obt_xml_find_node(node, "stacking"))) {
913         if (obt_xml_node_contains(n, "normal"))
914             config_dock_layer = OB_STACKING_LAYER_NORMAL;
915         else if (obt_xml_node_contains(n, "below"))
916             config_dock_layer = OB_STACKING_LAYER_BELOW;
917         else if (obt_xml_node_contains(n, "above"))
918             config_dock_layer = OB_STACKING_LAYER_ABOVE;
919     }
920     if ((n = obt_xml_find_node(node, "direction"))) {
921         if (obt_xml_node_contains(n, "horizontal"))
922             config_dock_orient = OB_ORIENTATION_HORZ;
923         else if (obt_xml_node_contains(n, "vertical"))
924             config_dock_orient = OB_ORIENTATION_VERT;
925     }
926     if ((n = obt_xml_find_node(node, "autoHide")))
927         config_dock_hide = obt_xml_node_bool(n);
928     if ((n = obt_xml_find_node(node, "hideDelay")))
929         config_dock_hide_delay = obt_xml_node_int(n);
930     if ((n = obt_xml_find_node(node, "showDelay")))
931         config_dock_show_delay = obt_xml_node_int(n);
932     if ((n = obt_xml_find_node(node, "moveButton"))) {
933         gchar *str = obt_xml_node_string(n);
934         guint b, s;
935         if (translate_button(str, &s, &b)) {
936             config_dock_app_move_button = b;
937             config_dock_app_move_modifiers = s;
938         } else {
939             g_message(_("Invalid button \"%s\" specified in config file"), str);
940         }
941         g_free(str);
942     }
943 }
944
945 static void parse_menu(xmlNodePtr node, gpointer d)
946 {
947     xmlNodePtr n;
948     node = node->children;
949
950     if ((n = obt_xml_find_node(node, "hideDelay")))
951         config_menu_hide_delay = obt_xml_node_int(n);
952     if ((n = obt_xml_find_node(node, "middle")))
953         config_menu_middle = obt_xml_node_bool(n);
954     if ((n = obt_xml_find_node(node, "submenuShowDelay")))
955         config_submenu_show_delay = obt_xml_node_int(n);
956     if ((n = obt_xml_find_node(node, "submenuHideDelay")))
957         config_submenu_hide_delay = obt_xml_node_int(n);
958     if ((n = obt_xml_find_node(node, "manageDesktops")))
959         config_menu_manage_desktops = obt_xml_node_bool(n);
960     if ((n = obt_xml_find_node(node, "showIcons"))) {
961         config_menu_show_icons = obt_xml_node_bool(n);
962 #if !defined(USE_IMLIB2) && !defined(USE_LIBRSVG)
963         if (config_menu_show_icons)
964             g_message(_("Openbox was compiled without image loading support. Icons in menus will not be loaded."));
965 #endif
966     }
967
968     while ((node = obt_xml_find_node(node, "file"))) {
969             gchar *c = obt_xml_node_string(node);
970             config_menu_files = g_slist_append(config_menu_files,
971                                                obt_paths_expand_tilde(c));
972             g_free(c);
973             node = node->next;
974     }
975 }
976
977 static void parse_resistance(xmlNodePtr node, gpointer d)
978 {
979     xmlNodePtr n;
980
981     node = node->children;
982     if ((n = obt_xml_find_node(node, "strength")))
983         config_resist_win = obt_xml_node_int(n);
984     if ((n = obt_xml_find_node(node, "screen_edge_strength")))
985         config_resist_edge = obt_xml_node_int(n);
986 }
987
988 typedef struct
989 {
990     const gchar *key;
991     const gchar *actname;
992 } ObDefKeyBind;
993
994 static void bind_default_keyboard(void)
995 {
996     ObDefKeyBind *it;
997     ObDefKeyBind binds[] = {
998         { "A-Tab", "NextWindow" },
999         { "S-A-Tab", "PreviousWindow" },
1000         { "A-F4", "Close" },
1001         { NULL, NULL }
1002     };
1003     for (it = binds; it->key; ++it) {
1004         GList *l = g_list_append(NULL, g_strdup(it->key));
1005         keyboard_bind(l, actions_parse_string(it->actname), TRUE);
1006     }
1007 }
1008
1009 typedef struct
1010 {
1011     const gchar *button;
1012     const gchar *context;
1013     const ObMouseAction mact;
1014     const gchar *actname;
1015 } ObDefMouseBind;
1016
1017 static void bind_default_mouse(void)
1018 {
1019     ObDefMouseBind *it;
1020     ObDefMouseBind binds[] = {
1021         { "Left", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1022         { "Middle", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1023         { "Right", "Client", OB_MOUSE_ACTION_PRESS, "Focus" },
1024         { "Left", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1025         { "Middle", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1026         { "Right", "Desktop", OB_MOUSE_ACTION_PRESS, "Focus" },
1027         { "Left", "Titlebar", OB_MOUSE_ACTION_PRESS, "Focus" },
1028         { "Left", "Bottom", OB_MOUSE_ACTION_PRESS, "Focus" },
1029         { "Left", "BLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1030         { "Left", "BRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1031         { "Left", "TLCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1032         { "Left", "TRCorner", OB_MOUSE_ACTION_PRESS, "Focus" },
1033         { "Left", "Close", OB_MOUSE_ACTION_PRESS, "Focus" },
1034         { "Left", "Maximize", OB_MOUSE_ACTION_PRESS, "Focus" },
1035         { "Left", "Iconify", OB_MOUSE_ACTION_PRESS, "Focus" },
1036         { "Left", "Icon", OB_MOUSE_ACTION_PRESS, "Focus" },
1037         { "Left", "AllDesktops", OB_MOUSE_ACTION_PRESS, "Focus" },
1038         { "Left", "Shade", OB_MOUSE_ACTION_PRESS, "Focus" },
1039         { "Left", "Client", OB_MOUSE_ACTION_CLICK, "Raise" },
1040         { "Left", "Titlebar", OB_MOUSE_ACTION_CLICK, "Raise" },
1041         { "Middle", "Titlebar", OB_MOUSE_ACTION_CLICK, "Lower" },
1042         { "Left", "BLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1043         { "Left", "BRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1044         { "Left", "TLCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1045         { "Left", "TRCorner", OB_MOUSE_ACTION_CLICK, "Raise" },
1046         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Raise" },
1047         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "Raise" },
1048         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Raise" },
1049         { "Left", "Icon", OB_MOUSE_ACTION_CLICK, "Raise" },
1050         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "Raise" },
1051         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "Raise" },
1052         { "Left", "Close", OB_MOUSE_ACTION_CLICK, "Close" },
1053         { "Left", "Maximize", OB_MOUSE_ACTION_CLICK, "ToggleMaximize" },
1054         { "Left", "Iconify", OB_MOUSE_ACTION_CLICK, "Iconify" },
1055         { "Left", "AllDesktops", OB_MOUSE_ACTION_CLICK, "ToggleOmnipresent" },
1056         { "Left", "Shade", OB_MOUSE_ACTION_CLICK, "ToggleShade" },
1057         { "Left", "TLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1058         { "Left", "TRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1059         { "Left", "BLCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1060         { "Left", "BRCorner", OB_MOUSE_ACTION_MOTION, "Resize" },
1061         { "Left", "Top", OB_MOUSE_ACTION_MOTION, "Resize" },
1062         { "Left", "Bottom", OB_MOUSE_ACTION_MOTION, "Resize" },
1063         { "Left", "Left", OB_MOUSE_ACTION_MOTION, "Resize" },
1064         { "Left", "Right", OB_MOUSE_ACTION_MOTION, "Resize" },
1065         { "Left", "Titlebar", OB_MOUSE_ACTION_MOTION, "Move" },
1066         { "A-Left", "Frame", OB_MOUSE_ACTION_MOTION, "Move" },
1067         { "A-Middle", "Frame", OB_MOUSE_ACTION_MOTION, "Resize" },
1068         { NULL, NULL, 0, NULL }
1069     };
1070
1071     for (it = binds; it->button; ++it)
1072         mouse_bind(it->button, frame_context_from_string(it->context),
1073                    it->mact, actions_parse_string(it->actname));
1074 }
1075
1076 void config_startup(ObtXmlInst *i)
1077 {
1078     config_focus_new = TRUE;
1079     config_focus_follow = FALSE;
1080     config_focus_delay = 0;
1081     config_focus_raise = FALSE;
1082     config_focus_last = TRUE;
1083     config_focus_under_mouse = FALSE;
1084     config_unfocus_leave = FALSE;
1085
1086     obt_xml_register(i, "focus", parse_focus, NULL);
1087
1088     config_place_policy = OB_PLACE_POLICY_SMART;
1089     config_place_monitor = OB_PLACE_MONITOR_PRIMARY;
1090
1091     config_primary_monitor_index = 1;
1092     config_primary_monitor = OB_PLACE_MONITOR_ACTIVE;
1093
1094     obt_xml_register(i, "placement", parse_placement, NULL);
1095
1096     STRUT_PARTIAL_SET(config_margins, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
1097
1098     obt_xml_register(i, "margins", parse_margins, NULL);
1099
1100     config_theme = NULL;
1101
1102     config_animate_iconify = TRUE;
1103     config_title_layout = g_strdup("NLIMC");
1104     config_theme_keepborder = TRUE;
1105     config_theme_window_list_icon_size = 36;
1106
1107     config_font_activewindow = NULL;
1108     config_font_inactivewindow = NULL;
1109     config_font_menuitem = NULL;
1110     config_font_menutitle = NULL;
1111     config_font_activeosd = NULL;
1112     config_font_inactiveosd = NULL;
1113
1114     obt_xml_register(i, "theme", parse_theme, NULL);
1115
1116     config_desktops_num = 4;
1117     config_screen_firstdesk = 1;
1118     config_emulate_xinerama = FALSE;
1119     config_desktops_names = NULL;
1120     config_desktop_popup_time = 875;
1121
1122     obt_xml_register(i, "desktops", parse_desktops, NULL);
1123
1124     config_resize_redraw = TRUE;
1125     config_resize_popup_show = 1; /* nonpixel increments */
1126     config_resize_popup_pos = OB_RESIZE_POS_CENTER;
1127     GRAVITY_COORD_SET(config_resize_popup_fixed.x, 0, FALSE, FALSE);
1128     GRAVITY_COORD_SET(config_resize_popup_fixed.y, 0, FALSE, FALSE);
1129
1130     obt_xml_register(i, "resize", parse_resize, NULL);
1131
1132     config_dock_layer = OB_STACKING_LAYER_ABOVE;
1133     config_dock_pos = OB_DIRECTION_NORTHEAST;
1134     config_dock_floating = FALSE;
1135     config_dock_nostrut = FALSE;
1136     config_dock_x = 0;
1137     config_dock_y = 0;
1138     config_dock_orient = OB_ORIENTATION_VERT;
1139     config_dock_hide = FALSE;
1140     config_dock_hide_delay = 300;
1141     config_dock_show_delay = 300;
1142     config_dock_app_move_button = 2; /* middle */
1143     config_dock_app_move_modifiers = 0;
1144
1145     obt_xml_register(i, "dock", parse_dock, NULL);
1146
1147     translate_key("C-g", &config_keyboard_reset_state,
1148                   &config_keyboard_reset_keycode);
1149
1150     bind_default_keyboard();
1151
1152     obt_xml_register(i, "keyboard", parse_keyboard, NULL);
1153
1154     config_mouse_threshold = 8;
1155     config_mouse_dclicktime = 500;
1156     config_mouse_screenedgetime = 400;
1157     config_mouse_screenedgewarp = FALSE;
1158
1159     bind_default_mouse();
1160
1161     obt_xml_register(i, "mouse", parse_mouse, NULL);
1162
1163     config_resist_win = 10;
1164     config_resist_edge = 20;
1165
1166     obt_xml_register(i, "resistance", parse_resistance, NULL);
1167
1168     config_menu_hide_delay = 250;
1169     config_menu_middle = FALSE;
1170     config_submenu_show_delay = 100;
1171     config_submenu_hide_delay = 400;
1172     config_menu_manage_desktops = TRUE;
1173     config_menu_files = NULL;
1174     config_menu_show_icons = TRUE;
1175
1176     obt_xml_register(i, "menu", parse_menu, NULL);
1177
1178     config_per_app_settings = NULL;
1179
1180     obt_xml_register(i, "applications", parse_per_app_settings, NULL);
1181 }
1182
1183 void config_shutdown(void)
1184 {
1185     GSList *it;
1186
1187     g_free(config_theme);
1188
1189     g_free(config_title_layout);
1190
1191     RrFontClose(config_font_activewindow);
1192     RrFontClose(config_font_inactivewindow);
1193     RrFontClose(config_font_menuitem);
1194     RrFontClose(config_font_menutitle);
1195     RrFontClose(config_font_activeosd);
1196     RrFontClose(config_font_inactiveosd);
1197
1198     for (it = config_desktops_names; it; it = g_slist_next(it))
1199         g_free(it->data);
1200     g_slist_free(config_desktops_names);
1201
1202     for (it = config_menu_files; it; it = g_slist_next(it))
1203         g_free(it->data);
1204     g_slist_free(config_menu_files);
1205
1206     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
1207         ObAppSettings *itd = (ObAppSettings *)it->data;
1208         if (itd->name) g_pattern_spec_free(itd->name);
1209         if (itd->role) g_pattern_spec_free(itd->role);
1210         if (itd->title) g_pattern_spec_free(itd->title);
1211         if (itd->class) g_pattern_spec_free(itd->class);
1212         if (itd->group_name) g_pattern_spec_free(itd->group_name);
1213         if (itd->group_class) g_pattern_spec_free(itd->group_class);
1214         g_slice_free(ObAppSettings, it->data);
1215     }
1216     g_slist_free(config_per_app_settings);
1217 }