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