1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Marco preferences */
4 
5 /*
6  * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc.
7  * Copyright (C) 2006 Elijah Newren
8  * Copyright (C) 2008 Thomas Thurman
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301, USA.
24  */
25 
26 #include <config.h>
27 #include <glib/gi18n-lib.h>
28 
29 #include "prefs.h"
30 #include "ui.h"
31 #include "util.h"
32 #include <gdk/gdk.h>
33 #include <gio/gio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 
37 #define MAX_REASONABLE_WORKSPACES 36
38 
39 #define MAX_COMMANDS (32 + NUM_EXTRA_COMMANDS)
40 #define NUM_EXTRA_COMMANDS 2
41 #define SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 2)
42 #define WIN_SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 1)
43 
44 /* If you add a key, it needs updating in init() and in the GSettings
45  * notify listener and of course in the .gschema file.
46  *
47  * Keys which are handled by one of the unified handlers below are
48  * not given a name here, because the purpose of the unified handlers
49  * is that keys should be referred to exactly once.
50  */
51 #define KEY_GENERAL_SCHEMA "org.mate.Marco.general"
52 #define KEY_GENERAL_TITLEBAR_FONT "titlebar-font"
53 #define KEY_GENERAL_NUM_WORKSPACES "num-workspaces"
54 #define KEY_GENERAL_COMPOSITOR "compositing-manager"
55 #define KEY_GENERAL_COMPOSITOR_FAST_ALT_TAB "compositing-fast-alt-tab"
56 #define KEY_GENERAL_CENTER_NEW_WINDOWS "center-new-windows"
57 #define KEY_GENERAL_ICON_SIZE "icon-size"
58 #define KEY_GENERAL_ALT_TAB_MAX_COLUMNS "alt-tab-max-columns"
59 #define KEY_GENERAL_ALT_TAB_EXPAND_TO_FIT_TITLE "alt-tab-expand-to-fit-title"
60 
61 #define KEY_COMMAND_SCHEMA "org.mate.Marco.keybinding-commands"
62 #define KEY_COMMAND_PREFIX "command-"
63 
64 #define KEY_SCREEN_BINDINGS_SCHEMA "org.mate.Marco.global-keybindings"
65 
66 #define KEY_WINDOW_BINDINGS_SCHEMA "org.mate.Marco.window-keybindings"
67 
68 #define KEY_WORKSPACE_NAME_SCHEMA "org.mate.Marco.workspace-names"
69 #define KEY_WORKSPACE_NAME_PREFIX "name-"
70 
71 #define KEY_MATE_INTERFACE_SCHEMA "org.mate.interface"
72 #define KEY_MATE_INTERFACE_ACCESSIBILITY "accessibility"
73 #define KEY_MATE_INTERFACE_ENABLE_ANIMATIONS "enable-animations"
74 
75 #define KEY_MATE_TERMINAL_SCHEMA "org.mate.applications-terminal"
76 #define KEY_MATE_TERMINAL_COMMAND "exec"
77 
78 #define KEY_MATE_MOUSE_SCHEMA "org.mate.peripherals-mouse"
79 #define KEY_MATE_MOUSE_CURSOR_THEME "cursor-theme"
80 #define KEY_MATE_MOUSE_CURSOR_SIZE "cursor-size"
81 
82 #define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
83 
84 static GSettings *settings_general;
85 static GSettings *settings_command;
86 static GSettings *settings_screen_bindings;
87 static GSettings *settings_window_bindings;
88 static GSettings *settings_workspace_names;
89 static GSettings *settings_mate_interface;
90 static GSettings *settings_mate_terminal;
91 static GSettings *settings_mate_mouse;
92 static GHashTable *settings_schemas;
93 
94 static GList *changes = NULL;
95 static guint changed_idle;
96 static GList *listeners = NULL;
97 
98 static gboolean use_system_font = FALSE;
99 static PangoFontDescription *titlebar_font = NULL;
100 static MetaVirtualModifier mouse_button_mods = Mod1Mask;
101 static MetaFocusMode focus_mode = META_FOCUS_MODE_CLICK;
102 static MetaFocusNewWindows focus_new_windows = META_FOCUS_NEW_WINDOWS_SMART;
103 static gboolean raise_on_click = TRUE;
104 static gboolean attach_modal_dialogs = FALSE;
105 static char* current_theme = NULL;
106 static int num_workspaces = 4;
107 static MetaWrapStyle wrap_style = META_WRAP_NONE;
108 static MetaActionTitlebar action_double_click_titlebar = META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE;
109 static MetaActionTitlebar action_middle_click_titlebar = META_ACTION_TITLEBAR_LOWER;
110 static MetaActionTitlebar action_right_click_titlebar = META_ACTION_TITLEBAR_MENU;
111 static gboolean application_based = FALSE;
112 static gboolean disable_workarounds = FALSE;
113 static gboolean auto_raise = FALSE;
114 static gboolean auto_raise_delay = 500;
115 static gboolean provide_visual_bell = FALSE;
116 static gboolean bell_is_audible = TRUE;
117 static gboolean reduced_resources = FALSE;
118 static gboolean mate_accessibility = FALSE;
119 static gboolean mate_animations = TRUE;
120 static char *cursor_theme = NULL;
121 static int   cursor_size = 24;
122 static int   icon_size   = META_DEFAULT_ICON_SIZE;
123 static int   alt_tab_max_columns = META_DEFAULT_ALT_TAB_MAX_COLUMNS;
124 static gboolean alt_tab_expand_to_fit_title = META_DEFAULT_ALT_TAB_EXPAND_TO_FIT_TITLE;
125 static gboolean use_force_compositor_manager = FALSE;
126 static gboolean force_compositor_manager = FALSE;
127 static gboolean compositing_manager = FALSE;
128 static gboolean compositing_fast_alt_tab = FALSE;
129 static gboolean resize_with_right_button = FALSE;
130 static gboolean show_tab_border = FALSE;
131 static gboolean center_new_windows = FALSE;
132 static gboolean force_fullscreen = TRUE;
133 static gboolean allow_tiling = FALSE;
134 static gboolean allow_top_tiling = TRUE;
135 static gboolean allow_tile_cycling = TRUE;
136 static GList *show_desktop_skip_list = NULL;
137 
138 static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;
139 static MetaButtonLayout button_layout;
140 
141 /* The screenshot commands are at the end */
142 static char *commands[MAX_COMMANDS] = { NULL, };
143 
144 static char *terminal_command = NULL;
145 
146 static char *workspace_names[MAX_REASONABLE_WORKSPACES] = { NULL, };
147 
148 static gboolean handle_preference_update_enum (const gchar *key, GSettings *settings);
149 
150 static gboolean update_key_binding     (const char *name,
151                                         gchar *value);
152 static gboolean update_command            (const char  *name,
153                                            const char  *value);
154 static gboolean update_workspace_name     (const char  *name,
155                                            const char  *value);
156 
157 static void change_notify (GSettings *settings,
158                            gchar *key,
159                            gpointer user_data);
160 
161 static char* settings_key_for_workspace_name (int i);
162 
163 static void queue_changed (MetaPreference  pref);
164 
165 #if 0
166 static void     cleanup_error             (GError **error);
167 #endif
168 
169 static void maybe_give_disable_workarounds_warning (void);
170 
171 static void titlebar_handler (MetaPreference, const gchar*, gboolean*);
172 static void theme_name_handler (MetaPreference, const gchar*, gboolean*);
173 static void mouse_button_mods_handler (MetaPreference, const gchar*, gboolean*);
174 static void button_layout_handler (MetaPreference, const gchar*, gboolean*);
175 static void show_desktop_skip_list_handler (MetaPreference, const gchar*, gboolean*);
176 
177 static gboolean update_binding            (MetaKeyPref *binding,
178                                            gchar  *value);
179 
180 static void     init_bindings             (GSettings *);
181 static void     init_screen_bindings      (void);
182 static void     init_window_bindings      (void);
183 static void     init_commands             (void);
184 static void     init_workspace_names      (void);
185 
186 static MetaPlacementMode placement_mode = META_PLACEMENT_MODE_AUTOMATIC;
187 
188 typedef struct
189 {
190   MetaPrefsChangedFunc func;
191   gpointer data;
192 } MetaPrefsListener;
193 
194 /**
195  * The details of one preference which is constrained to be
196  * one of a small number of string values-- in other words,
197  * an enumeration.
198  *
199  * We could have done this other ways.  One particularly attractive
200  * possibility would have been to represent the entire symbol table
201  * as a space-separated string literal in the list of symtabs, so
202  * the focus mode enums could have been represented simply by
203  * "click sloppy mouse".  However, the simplicity gained would have
204  * been outweighed by the bugs caused when the ordering of the enum
205  * strings got out of sync with the actual enum statement.  Also,
206  * there is existing library code to use this kind of symbol tables.
207  *
208  * Other things we might consider doing to clean this up in the
209  * future include:
210  *
211  *   - most of the keys begin with the same prefix, and perhaps we
212  *     could assume it if they don't start with a slash
213  *
214  *   - there are several cases where a single identifier could be used
215  *     to generate an entire entry, and perhaps this could be done
216  *     with a macro.  (This would reduce clarity, however, and is
217  *     probably a bad thing.)
218  *
219  *   - these types all begin with a gchar* (and contain a MetaPreference)
220  *     and we can factor out the repeated code in the handlers by taking
221  *     advantage of this using some kind of union arrangement.
222  */
223 typedef struct
224 {
225   gchar *key;
226   gchar *schema;
227   MetaPreference pref;
228   gint *target;
229 } MetaEnumPreference;
230 
231 typedef struct
232 {
233   gchar *key;
234   gchar *schema;
235   MetaPreference pref;
236   gboolean *target;
237   gboolean becomes_true_on_destruction;
238 } MetaBoolPreference;
239 
240 typedef struct
241 {
242   gchar *key;
243   gchar *schema;
244   MetaPreference pref;
245 
246   /**
247    * A handler.  Many of the string preferences aren't stored as
248    * strings and need parsing; others of them have default values
249    * which can't be solved in the general case.  If you include a
250    * function pointer here, it will be called before the string
251    * value is written out to the target variable.
252    *
253    * The function is passed two arguments: the preference, and
254    * the new string as a gchar*.  It returns a gboolean;
255    * only if this is true, the listeners will be informed that
256    * the preference has changed.
257    *
258    * This may be NULL.  If it is, see "target", below.
259    */
260   void (*handler) (MetaPreference pref,
261                      const gchar *string_value,
262                      gboolean *inform_listeners);
263 
264   /**
265    * Where to write the incoming string.
266    *
267    * This must be NULL if the handler is non-NULL.
268    * If the incoming string is NULL, no change will be made.
269    */
270   gchar **target;
271 
272 } MetaStringPreference;
273 
274 #define METAINTPREFERENCE_NO_CHANGE_ON_DESTROY G_MININT
275 
276 typedef struct
277 {
278   gchar *key;
279   gchar *schema;
280   MetaPreference pref;
281   gint *target;
282   /**
283    * Minimum and maximum values of the integer.
284    * If the new value is out of bounds, it will be discarded with a warning.
285    */
286   gint minimum, maximum;
287   /**
288    * Value to use if the key is destroyed.
289    * If this is METAINTPREFERENCE_NO_CHANGE_ON_DESTROY, it will
290    * not be changed when the key is destroyed.
291    */
292   gint value_if_destroyed;
293 } MetaIntPreference;
294 
295 /* FIXMEs: */
296 /* @@@ Don't use NULL lines at the end; glib can tell you how big it is */
297 /* @@@ /apps/marco/general should be assumed if first char is not / */
298 /* @@@ Will it ever be possible to merge init and update? If not, why not? */
299 
300 static MetaEnumPreference preferences_enum[] =
301   {
302     { "focus-new-windows",
303       KEY_GENERAL_SCHEMA,
304       META_PREF_FOCUS_NEW_WINDOWS,
305       (gint *) &focus_new_windows,
306     },
307     { "focus-mode",
308       KEY_GENERAL_SCHEMA,
309       META_PREF_FOCUS_MODE,
310       (gint *) &focus_mode,
311     },
312     { "wrap-style",
313       KEY_GENERAL_SCHEMA,
314       META_PREF_WRAP_STYLE,
315       (gint *) &wrap_style,
316     },
317     { "visual-bell-type",
318       KEY_GENERAL_SCHEMA,
319       META_PREF_VISUAL_BELL_TYPE,
320       (gint *) &visual_bell_type,
321     },
322     { "action-double-click-titlebar",
323       KEY_GENERAL_SCHEMA,
324       META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR,
325       (gint *) &action_double_click_titlebar,
326     },
327     { "action-middle-click-titlebar",
328       KEY_GENERAL_SCHEMA,
329       META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR,
330       (gint *) &action_middle_click_titlebar,
331     },
332     { "action-right-click-titlebar",
333       KEY_GENERAL_SCHEMA,
334       META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
335       (gint *) &action_right_click_titlebar,
336     },
337     { "placement-mode",
338       KEY_GENERAL_SCHEMA,
339       META_PREF_PLACEMENT_MODE,
340       (gint *) &placement_mode,
341     },
342     { NULL, NULL, 0, NULL },
343   };
344 
345 static MetaBoolPreference preferences_bool[] =
346   {
347     { "raise-on-click",
348       KEY_GENERAL_SCHEMA,
349       META_PREF_RAISE_ON_CLICK,
350       &raise_on_click,
351       TRUE,
352     },
353     { "titlebar-uses-system-font",
354       KEY_GENERAL_SCHEMA,
355       META_PREF_TITLEBAR_FONT, /* note! shares a pref */
356       &use_system_font,
357       TRUE,
358     },
359     { "application-based",
360       KEY_GENERAL_SCHEMA,
361       META_PREF_APPLICATION_BASED,
362       NULL, /* feature is known but disabled */
363       FALSE,
364     },
365     { "disable-workarounds",
366       KEY_GENERAL_SCHEMA,
367       META_PREF_DISABLE_WORKAROUNDS,
368       &disable_workarounds,
369       FALSE,
370     },
371     { "auto-raise",
372       KEY_GENERAL_SCHEMA,
373       META_PREF_AUTO_RAISE,
374       &auto_raise,
375       FALSE,
376     },
377     { "visual-bell",
378       KEY_GENERAL_SCHEMA,
379       META_PREF_VISUAL_BELL,
380       &provide_visual_bell, /* FIXME: change the name: it's confusing */
381       FALSE,
382     },
383     { "audible-bell",
384       KEY_GENERAL_SCHEMA,
385       META_PREF_AUDIBLE_BELL,
386       &bell_is_audible, /* FIXME: change the name: it's confusing */
387       FALSE,
388     },
389     { "reduced-resources",
390       KEY_GENERAL_SCHEMA,
391       META_PREF_REDUCED_RESOURCES,
392       &reduced_resources,
393       FALSE,
394     },
395     { "accessibility",
396       KEY_MATE_INTERFACE_SCHEMA,
397       META_PREF_MATE_ACCESSIBILITY,
398       &mate_accessibility,
399       FALSE,
400     },
401     { "enable-animations",
402       KEY_MATE_INTERFACE_SCHEMA,
403       META_PREF_MATE_ANIMATIONS,
404       &mate_animations,
405       TRUE,
406     },
407     { KEY_GENERAL_COMPOSITOR,
408       KEY_GENERAL_SCHEMA,
409       META_PREF_COMPOSITING_MANAGER,
410       &compositing_manager,
411       FALSE,
412     },
413     { "compositing-fast-alt-tab",
414       KEY_GENERAL_SCHEMA,
415       META_PREF_COMPOSITING_FAST_ALT_TAB,
416       &compositing_fast_alt_tab,
417       FALSE,
418     },
419     { "resize-with-right-button",
420       KEY_GENERAL_SCHEMA,
421       META_PREF_RESIZE_WITH_RIGHT_BUTTON,
422       &resize_with_right_button,
423       FALSE,
424     },
425     { "show-tab-border",
426       KEY_GENERAL_SCHEMA,
427       META_PREF_SHOW_TAB_BORDER,
428       &show_tab_border,
429       FALSE,
430     },
431     { "center-new-windows",
432       KEY_GENERAL_SCHEMA,
433       META_PREF_CENTER_NEW_WINDOWS,
434       &center_new_windows,
435       FALSE,
436     },
437     { "allow-tiling",
438       KEY_GENERAL_SCHEMA,
439       META_PREF_ALLOW_TILING,
440       &allow_tiling,
441       FALSE,
442     },
443     { "allow-top-tiling",
444       KEY_GENERAL_SCHEMA,
445       META_PREF_ALLOW_TOP_TILING,
446       &allow_top_tiling,
447       FALSE,
448     },
449     { "allow-tile-cycling",
450       KEY_GENERAL_SCHEMA,
451       META_PREF_ALLOW_TILE_CYCLING,
452       &allow_tile_cycling,
453       FALSE,
454     },
455     { "alt-tab-expand-to-fit-title",
456       KEY_GENERAL_SCHEMA,
457       META_PREF_ALT_TAB_EXPAND_TO_FIT_TITLE,
458       &alt_tab_expand_to_fit_title,
459       META_DEFAULT_ALT_TAB_EXPAND_TO_FIT_TITLE,
460     },
461     { NULL, NULL, 0, NULL, FALSE },
462   };
463 
464 static MetaStringPreference preferences_string[] =
465   {
466     { "mouse-button-modifier",
467       KEY_GENERAL_SCHEMA,
468       META_PREF_MOUSE_BUTTON_MODS,
469       mouse_button_mods_handler,
470       NULL,
471     },
472     { "theme",
473       KEY_GENERAL_SCHEMA,
474       META_PREF_THEME,
475       theme_name_handler,
476       NULL,
477     },
478     { KEY_GENERAL_TITLEBAR_FONT,
479       KEY_GENERAL_SCHEMA,
480       META_PREF_TITLEBAR_FONT,
481       titlebar_handler,
482       NULL,
483     },
484     { KEY_MATE_TERMINAL_COMMAND,
485       KEY_MATE_TERMINAL_SCHEMA,
486       META_PREF_TERMINAL_COMMAND,
487       NULL,
488       &terminal_command,
489     },
490     { "button-layout",
491       KEY_GENERAL_SCHEMA,
492       META_PREF_BUTTON_LAYOUT,
493       button_layout_handler,
494       NULL,
495     },
496     { "cursor-theme",
497       KEY_MATE_MOUSE_SCHEMA,
498       META_PREF_CURSOR_THEME,
499       NULL,
500       &cursor_theme,
501     },
502     { "show-desktop-skip-list",
503       KEY_GENERAL_SCHEMA,
504       META_PREF_SHOW_DESKTOP_SKIP_LIST,
505       &show_desktop_skip_list_handler,
506       NULL,
507     },
508     { NULL, NULL, 0, NULL, NULL },
509   };
510 
511 static MetaIntPreference preferences_int[] =
512   {
513     { "num-workspaces",
514       KEY_GENERAL_SCHEMA,
515       META_PREF_NUM_WORKSPACES,
516       &num_workspaces,
517       /* I would actually recommend we change the destroy value to 4
518        * and get rid of METAINTPREFERENCE_NO_CHANGE_ON_DESTROY entirely.
519        *  -- tthurman
520        */
521       1, MAX_REASONABLE_WORKSPACES, METAINTPREFERENCE_NO_CHANGE_ON_DESTROY,
522     },
523     { "auto-raise-delay",
524       KEY_GENERAL_SCHEMA,
525       META_PREF_AUTO_RAISE_DELAY,
526       &auto_raise_delay,
527       0, 10000, 0,
528       /* @@@ Get rid of MAX_REASONABLE_AUTO_RAISE_DELAY */
529     },
530     { "cursor-size",
531       KEY_MATE_MOUSE_SCHEMA,
532       META_PREF_CURSOR_SIZE,
533       &cursor_size,
534       1, 128, 24,
535     },
536     { "icon-size",
537       KEY_GENERAL_SCHEMA,
538       META_PREF_ICON_SIZE,
539       &icon_size,
540       META_MIN_ICON_SIZE, META_MAX_ICON_SIZE, META_DEFAULT_ICON_SIZE,
541     },
542     { "alt-tab-max-columns",
543       KEY_GENERAL_SCHEMA,
544       META_PREF_ALT_TAB_MAX_COLUMNS,
545       &alt_tab_max_columns,
546       META_MIN_ALT_TAB_MAX_COLUMNS,
547       META_MAX_ALT_TAB_MAX_COLUMNS,
548       META_DEFAULT_ALT_TAB_MAX_COLUMNS,
549     },
550     { NULL, NULL, 0, NULL, 0, 0, 0, },
551   };
552 
553 static void
handle_preference_init_enum(void)554 handle_preference_init_enum (void)
555 {
556   MetaEnumPreference *cursor = preferences_enum;
557 
558   while (cursor->key!=NULL)
559     {
560       gint value;
561 
562       if (cursor->target==NULL)
563         {
564           ++cursor;
565           continue;
566         }
567 
568       value = g_settings_get_enum (SETTINGS (cursor->schema),
569                                    cursor->key);
570       *cursor->target = value;
571 
572       ++cursor;
573     }
574 }
575 
576 static void
handle_preference_init_bool(void)577 handle_preference_init_bool (void)
578 {
579   MetaBoolPreference *cursor = preferences_bool;
580 
581   while (cursor->key!=NULL)
582     {
583       if (cursor->target!=NULL)
584         *cursor->target = g_settings_get_boolean (SETTINGS (cursor->schema), cursor->key);
585 
586       ++cursor;
587     }
588 
589   maybe_give_disable_workarounds_warning ();
590 }
591 
592 static void
handle_preference_init_string(void)593 handle_preference_init_string (void)
594 {
595   MetaStringPreference *cursor = preferences_string;
596 
597   while (cursor->key!=NULL)
598     {
599       gchar *value;
600       gboolean dummy = TRUE;
601 
602       /* the string "value" will be newly allocated */
603       value = g_settings_get_string (SETTINGS (cursor->schema),
604                                      cursor->key);
605 
606       if (cursor->handler)
607         {
608           if (cursor->target)
609             meta_bug ("%s has both a target and a handler\n", cursor->key);
610 
611           cursor->handler (cursor->pref, value, &dummy);
612 
613           g_free (value);
614         }
615       else if (cursor->target)
616         {
617           if (*(cursor->target))
618             g_free (*(cursor->target));
619 
620           *(cursor->target) = value;
621         }
622 
623       ++cursor;
624     }
625 }
626 
627 static void
handle_preference_init_int(void)628 handle_preference_init_int (void)
629 {
630   MetaIntPreference *cursor = preferences_int;
631 
632   while (cursor->key!=NULL)
633     {
634       gint value;
635 
636       value = g_settings_get_int (SETTINGS (cursor->schema),
637                                   cursor->key);
638 
639       if (value < cursor->minimum || value > cursor->maximum)
640         {
641           /* FIXME: check if this can be avoided by GSettings */
642           meta_warning (_("%d stored in GSettings key %s is out of range %d to %d\n"),
643                         value, cursor->key,  cursor->minimum, cursor->maximum);
644           /* Former behaviour for out-of-range values was:
645            *   - number of workspaces was clamped;
646            *   - auto raise delay was always reset to zero even if too high!;
647            *   - cursor size was ignored.
648            *
649            * These seem to be meaningless variations.  If they did
650            * have meaning we could have put them into MetaIntPreference.
651            * The last of these is the closest to how we behave for
652            * other types, so I think we should standardise on that.
653            */
654         }
655       else if (cursor->target)
656         *cursor->target = value;
657 
658       ++cursor;
659     }
660 }
661 
662 static gboolean
handle_preference_update_enum(const gchar * key,GSettings * settings)663 handle_preference_update_enum (const gchar *key, GSettings *settings)
664 {
665   MetaEnumPreference *cursor = preferences_enum;
666   gint old_value;
667 
668   while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
669     ++cursor;
670 
671   if (cursor->key==NULL)
672     /* Didn't recognise that key. */
673     return FALSE;
674 
675   /* We need to know whether the value changes, so
676    * store the current value away.
677    */
678 
679   old_value = * ((gint *) cursor->target);
680 
681   /* Now look it up... */
682   *cursor->target = g_settings_get_enum (settings, key);
683 
684   /* Did it change?  If so, tell the listeners about it. */
685 
686   if (old_value != *((gint *) cursor->target))
687     queue_changed (cursor->pref);
688 
689   return TRUE;
690 }
691 
692 static gboolean
handle_preference_update_bool(const gchar * key,GSettings * settings)693 handle_preference_update_bool (const gchar *key, GSettings *settings)
694 {
695   MetaBoolPreference *cursor = preferences_bool;
696   gboolean old_value;
697 
698   while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
699     ++cursor;
700 
701   if (cursor->key==NULL)
702     /* Didn't recognise that key. */
703     return FALSE;
704 
705   if (cursor->target==NULL)
706     /* No work for us to do. */
707     return TRUE;
708 
709   /* We need to know whether the value changes, so
710    * store the current value away.
711    */
712 
713   old_value = * ((gboolean *) cursor->target);
714 
715   /* Now look it up... */
716 
717   *((gboolean *) cursor->target) = g_settings_get_boolean (settings, key);
718 
719   /* Did it change?  If so, tell the listeners about it. */
720 
721   if (old_value != *((gboolean *) cursor->target))
722     queue_changed (cursor->pref);
723 
724   if (cursor->pref==META_PREF_DISABLE_WORKAROUNDS)
725     maybe_give_disable_workarounds_warning ();
726 
727   return TRUE;
728 }
729 
730 static gboolean
handle_preference_update_string(const gchar * key,GSettings * settings)731 handle_preference_update_string (const gchar *key, GSettings *settings)
732 {
733   MetaStringPreference *cursor = preferences_string;
734   gchar *value;
735   gboolean inform_listeners = TRUE;
736 
737   while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
738     ++cursor;
739 
740   if (cursor->key==NULL)
741     /* Didn't recognise that key. */
742     return FALSE;
743 
744   value = g_settings_get_string (settings, key);
745 
746   if (cursor->handler)
747     cursor->handler (cursor->pref, value, &inform_listeners);
748   else if (cursor->target)
749     {
750       if (*(cursor->target))
751         g_free(*(cursor->target));
752 
753       if (value!=NULL)
754         *(cursor->target) = g_strdup (value);
755       else
756         *(cursor->target) = NULL;
757 
758       inform_listeners =
759         (value==NULL && *(cursor->target)==NULL) ||
760         (value!=NULL && *(cursor->target)!=NULL &&
761          strcmp (value, *(cursor->target))==0);
762     }
763 
764   if (inform_listeners)
765     queue_changed (cursor->pref);
766 
767   g_free (value);
768 
769   return TRUE;
770 }
771 
772 static gboolean
handle_preference_update_int(const gchar * key,GSettings * settings)773 handle_preference_update_int (const gchar *key, GSettings *settings)
774 {
775   MetaIntPreference *cursor = preferences_int;
776   gint value;
777 
778   while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
779     ++cursor;
780 
781   if (cursor->key==NULL)
782     /* Didn't recognise that key. */
783     return FALSE;
784 
785   if (cursor->target==NULL)
786     /* No work for us to do. */
787     return TRUE;
788 
789   value = g_settings_get_int (settings, key);
790 
791   if (value < cursor->minimum || value > cursor->maximum)
792     {
793       /* FIXME! GSettings, instead of MateConf, has Minimum/Maximun in schema!
794        * But some preferences depends on costants for minimum/maximum values */
795       meta_warning (_("%d stored in GSettings key %s is out of range %d to %d\n"),
796                     value, cursor->key,
797                     cursor->minimum, cursor->maximum);
798       return TRUE;
799     }
800 
801   /* Did it change?  If so, tell the listeners about it. */
802 
803   if (*cursor->target != value)
804     {
805       *cursor->target = value;
806       queue_changed (cursor->pref);
807     }
808 
809   return TRUE;
810 
811 }
812 
813 /****************************************************************************/
814 /* Listeners.                                                               */
815 /****************************************************************************/
816 
817 void
meta_prefs_add_listener(MetaPrefsChangedFunc func,gpointer data)818 meta_prefs_add_listener (MetaPrefsChangedFunc func,
819                          gpointer             data)
820 {
821   MetaPrefsListener *l;
822 
823   l = g_new (MetaPrefsListener, 1);
824   l->func = func;
825   l->data = data;
826 
827   listeners = g_list_prepend (listeners, l);
828 }
829 
830 void
meta_prefs_remove_listener(MetaPrefsChangedFunc func,gpointer data)831 meta_prefs_remove_listener (MetaPrefsChangedFunc func,
832                             gpointer             data)
833 {
834   GList *tmp;
835 
836   tmp = listeners;
837   while (tmp != NULL)
838     {
839       MetaPrefsListener *l = tmp->data;
840 
841       if (l->func == func &&
842           l->data == data)
843         {
844           g_free (l);
845           listeners = g_list_delete_link (listeners, tmp);
846 
847           return;
848         }
849 
850       tmp = tmp->next;
851     }
852 
853   meta_bug ("Did not find listener to remove\n");
854 }
855 
856 static void
emit_changed(MetaPreference pref)857 emit_changed (MetaPreference pref)
858 {
859   GList *tmp;
860   GList *copy;
861 
862   meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed\n",
863               meta_preference_to_string (pref));
864 
865   copy = g_list_copy (listeners);
866 
867   tmp = copy;
868 
869   while (tmp != NULL)
870     {
871       MetaPrefsListener *l = tmp->data;
872 
873       (* l->func) (pref, l->data);
874 
875       tmp = tmp->next;
876     }
877 
878   g_list_free (copy);
879 }
880 
881 static gboolean
changed_idle_handler(gpointer data)882 changed_idle_handler (gpointer data)
883 {
884   GList *tmp;
885   GList *copy;
886 
887   changed_idle = 0;
888 
889   copy = g_list_copy (changes); /* reentrancy paranoia */
890 
891   g_list_free (changes);
892   changes = NULL;
893 
894   tmp = copy;
895   while (tmp != NULL)
896     {
897       MetaPreference pref = GPOINTER_TO_INT (tmp->data);
898 
899       emit_changed (pref);
900 
901       tmp = tmp->next;
902     }
903 
904   g_list_free (copy);
905 
906   return FALSE;
907 }
908 
909 static void
queue_changed(MetaPreference pref)910 queue_changed (MetaPreference pref)
911 {
912   meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s\n",
913               meta_preference_to_string (pref));
914 
915   if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL)
916     changes = g_list_prepend (changes, GINT_TO_POINTER (pref));
917   else
918     meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending\n",
919                 meta_preference_to_string (pref));
920 
921   /* add idle at priority below the GSettings notify idle */
922   /* FIXME is this needed for GSettings too? */
923   if (changed_idle == 0)
924     changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY,
925                                     changed_idle_handler, NULL, NULL);
926 }
927 
928 /****************************************************************************/
929 /* Initialisation.                                                          */
930 /****************************************************************************/
931 
932 void
meta_prefs_init(void)933 meta_prefs_init (void)
934 {
935   if (settings_general != NULL)
936     return;
937 
938   /* returns references which we hold forever */
939   settings_general = g_settings_new (KEY_GENERAL_SCHEMA);
940   settings_command = g_settings_new (KEY_COMMAND_SCHEMA);
941   settings_screen_bindings = g_settings_new (KEY_SCREEN_BINDINGS_SCHEMA);
942   settings_window_bindings = g_settings_new (KEY_WINDOW_BINDINGS_SCHEMA);
943   settings_workspace_names = g_settings_new (KEY_WORKSPACE_NAME_SCHEMA);
944   settings_mate_interface = g_settings_new (KEY_MATE_INTERFACE_SCHEMA);
945   settings_mate_terminal = g_settings_new (KEY_MATE_TERMINAL_SCHEMA);
946   settings_mate_mouse = g_settings_new (KEY_MATE_MOUSE_SCHEMA);
947 
948   settings_schemas = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
949   g_hash_table_insert (settings_schemas, KEY_GENERAL_SCHEMA, settings_general);
950   g_hash_table_insert (settings_schemas, KEY_COMMAND_SCHEMA, settings_command);
951   g_hash_table_insert (settings_schemas, KEY_SCREEN_BINDINGS_SCHEMA, settings_screen_bindings);
952   g_hash_table_insert (settings_schemas, KEY_WINDOW_BINDINGS_SCHEMA, settings_window_bindings);
953   g_hash_table_insert (settings_schemas, KEY_WORKSPACE_NAME_SCHEMA, settings_workspace_names);
954   g_hash_table_insert (settings_schemas, KEY_MATE_INTERFACE_SCHEMA, settings_mate_interface);
955   g_hash_table_insert (settings_schemas, KEY_MATE_TERMINAL_SCHEMA, settings_mate_terminal);
956   g_hash_table_insert (settings_schemas, KEY_MATE_MOUSE_SCHEMA, settings_mate_mouse);
957 
958   g_signal_connect (settings_general, "changed", G_CALLBACK (change_notify), NULL);
959   g_signal_connect (settings_command, "changed", G_CALLBACK (change_notify), NULL);
960   g_signal_connect (settings_screen_bindings, "changed", G_CALLBACK (change_notify), NULL);
961   g_signal_connect (settings_window_bindings, "changed", G_CALLBACK (change_notify), NULL);
962   g_signal_connect (settings_workspace_names, "changed", G_CALLBACK (change_notify), NULL);
963 
964   g_signal_connect (settings_mate_interface, "changed::" KEY_MATE_INTERFACE_ACCESSIBILITY, G_CALLBACK (change_notify), NULL);
965   g_signal_connect (settings_mate_interface, "changed::" KEY_MATE_INTERFACE_ENABLE_ANIMATIONS, G_CALLBACK (change_notify), NULL);
966   g_signal_connect (settings_mate_terminal, "changed::" KEY_MATE_TERMINAL_COMMAND, G_CALLBACK (change_notify), NULL);
967   g_signal_connect (settings_mate_mouse, "changed::" KEY_MATE_MOUSE_CURSOR_THEME, G_CALLBACK (change_notify), NULL);
968   g_signal_connect (settings_mate_mouse, "changed::" KEY_MATE_MOUSE_CURSOR_SIZE, G_CALLBACK (change_notify), NULL);
969 
970   /* Pick up initial values. */
971 
972   handle_preference_init_enum ();
973   handle_preference_init_bool ();
974   handle_preference_init_string ();
975   handle_preference_init_int ();
976 
977   init_screen_bindings ();
978   init_window_bindings ();
979   init_commands ();
980   init_workspace_names ();
981 }
982 
983 /****************************************************************************/
984 /* Updates.                                                                 */
985 /****************************************************************************/
986 
987 gboolean (*preference_update_handler[]) (const gchar*, GSettings*) = {
988   handle_preference_update_enum,
989   handle_preference_update_bool,
990   handle_preference_update_string,
991   handle_preference_update_int,
992   NULL
993 };
994 
995 static void
change_notify(GSettings * settings,gchar * key,gpointer user_data)996 change_notify (GSettings *settings,
997                gchar *key,
998                gpointer user_data)
999 {
1000   gint i=0;
1001 
1002   /* First, search for a handler that might know what to do. */
1003 
1004   /* FIXME: When this is all working, since the first item in every
1005    * array is the gchar* of the key, there's no reason we can't
1006    * find the correct record for that key here and save code duplication.
1007    */
1008 
1009   while (preference_update_handler[i]!=NULL)
1010     {
1011       if (preference_update_handler[i] (key, settings))
1012         return; /* Get rid of this eventually */
1013 
1014       i++;
1015     }
1016 
1017   gchar *schema_name = NULL;
1018   g_object_get (settings, "schema-id", &schema_name, NULL);
1019 
1020   if (g_strcmp0 (schema_name, KEY_WINDOW_BINDINGS_SCHEMA) == 0 ||
1021       g_strcmp0 (schema_name, KEY_SCREEN_BINDINGS_SCHEMA) == 0)
1022     {
1023       gchar *str;
1024       str = g_settings_get_string (settings, key);
1025 
1026       if (update_key_binding (key, str))
1027         queue_changed (META_PREF_KEYBINDINGS);
1028 
1029       g_free(str);
1030     }
1031   else if (g_strcmp0 (schema_name, KEY_COMMAND_SCHEMA) == 0)
1032     {
1033       gchar *str;
1034       str = g_settings_get_string (settings, key);
1035 
1036       if (update_command (key, str))
1037         queue_changed (META_PREF_COMMANDS);
1038 
1039       g_free(str);
1040     }
1041   else if (g_strcmp0 (schema_name, KEY_WORKSPACE_NAME_SCHEMA) == 0)
1042     {
1043       gchar *str;
1044         str = g_settings_get_string (settings, key);
1045 
1046       if (update_workspace_name (key, str))
1047         queue_changed (META_PREF_WORKSPACE_NAMES);
1048 
1049       g_free(str);
1050     }
1051   else
1052     {
1053       /* Is this possible with GSettings? I dont think so! */
1054       meta_topic (META_DEBUG_PREFS, "Key %s doesn't mean anything to Marco\n",
1055                   key);
1056     }
1057   g_free (schema_name);
1058 }
1059 
1060 #if 0
1061 static void
1062 cleanup_error (GError **error)
1063 {
1064   if (*error)
1065     {
1066       meta_warning ("%s\n", (*error)->message);
1067 
1068       g_error_free (*error);
1069       *error = NULL;
1070     }
1071 }
1072 #endif
1073 
1074 /**
1075  * Special case: give a warning the first time disable_workarounds
1076  * is turned on.
1077  */
1078 static void
maybe_give_disable_workarounds_warning(void)1079 maybe_give_disable_workarounds_warning (void)
1080 {
1081   static gboolean first_disable = TRUE;
1082 
1083   if (first_disable && disable_workarounds)
1084     {
1085       first_disable = FALSE;
1086 
1087       meta_warning (_("Workarounds for broken applications disabled. "
1088                       "Some applications may not behave properly.\n"));
1089     }
1090 }
1091 
1092 MetaVirtualModifier
meta_prefs_get_mouse_button_mods(void)1093 meta_prefs_get_mouse_button_mods  (void)
1094 {
1095   return mouse_button_mods;
1096 }
1097 
1098 MetaFocusMode
meta_prefs_get_focus_mode(void)1099 meta_prefs_get_focus_mode (void)
1100 {
1101   return focus_mode;
1102 }
1103 
1104 MetaFocusNewWindows
meta_prefs_get_focus_new_windows(void)1105 meta_prefs_get_focus_new_windows (void)
1106 {
1107   return focus_new_windows;
1108 }
1109 
1110 gboolean
meta_prefs_get_attach_modal_dialogs(void)1111 meta_prefs_get_attach_modal_dialogs (void)
1112 {
1113   return attach_modal_dialogs;
1114 }
1115 
1116 gboolean
meta_prefs_get_raise_on_click(void)1117 meta_prefs_get_raise_on_click (void)
1118 {
1119   return raise_on_click;
1120 }
1121 
1122 const char*
meta_prefs_get_theme(void)1123 meta_prefs_get_theme (void)
1124 {
1125   return current_theme;
1126 }
1127 
1128 const char*
meta_prefs_get_cursor_theme(void)1129 meta_prefs_get_cursor_theme (void)
1130 {
1131   return cursor_theme;
1132 }
1133 
1134 int
meta_prefs_get_cursor_size(void)1135 meta_prefs_get_cursor_size (void)
1136 {
1137   GdkWindow *window = gdk_get_default_root_window ();
1138   gint scale = gdk_window_get_scale_factor (window);
1139 
1140   return cursor_size * scale;
1141 }
1142 
1143 int
meta_prefs_get_icon_size(void)1144 meta_prefs_get_icon_size (void)
1145 {
1146   GdkWindow *window = gdk_get_default_root_window ();
1147   gint scale = gdk_window_get_scale_factor (window);
1148 
1149   return icon_size * scale;
1150 }
1151 
1152 int
meta_prefs_get_alt_tab_max_columns(void)1153 meta_prefs_get_alt_tab_max_columns (void)
1154 {
1155   return alt_tab_max_columns;
1156 }
1157 
1158 gboolean
meta_prefs_get_alt_tab_expand_to_fit_title(void)1159 meta_prefs_get_alt_tab_expand_to_fit_title (void)
1160 {
1161   return alt_tab_expand_to_fit_title;
1162 }
1163 
1164 gboolean
meta_prefs_is_in_skip_list(char * class)1165 meta_prefs_is_in_skip_list (char *class)
1166 {
1167   GList *item;
1168 
1169   for (item = show_desktop_skip_list; item; item = item->next)
1170     {
1171       if (!g_ascii_strcasecmp (class, item->data))
1172         return TRUE;
1173     }
1174   return FALSE;
1175 }
1176 
1177 /****************************************************************************/
1178 /* Handlers for string preferences.                                         */
1179 /****************************************************************************/
1180 
1181 static void
titlebar_handler(MetaPreference pref,const gchar * string_value,gboolean * inform_listeners)1182 titlebar_handler (MetaPreference pref,
1183                   const gchar    *string_value,
1184                   gboolean       *inform_listeners)
1185 {
1186   PangoFontDescription *new_desc = NULL;
1187 
1188   if (string_value)
1189     new_desc = pango_font_description_from_string (string_value);
1190 
1191   if (new_desc == NULL)
1192     {
1193       meta_warning (_("Could not parse font description "
1194                       "\"%s\" from GSettings key %s\n"),
1195                     string_value ? string_value : "(null)",
1196                     KEY_GENERAL_TITLEBAR_FONT);
1197 
1198       *inform_listeners = FALSE;
1199 
1200       return;
1201     }
1202 
1203   /* Is the new description the same as the old? */
1204 
1205   if (titlebar_font &&
1206       pango_font_description_equal (new_desc, titlebar_font))
1207     {
1208       pango_font_description_free (new_desc);
1209       *inform_listeners = FALSE;
1210       return;
1211     }
1212 
1213   /* No, so free the old one and put ours in instead. */
1214 
1215   if (titlebar_font)
1216     pango_font_description_free (titlebar_font);
1217 
1218   titlebar_font = new_desc;
1219 
1220 }
1221 
1222 static void
theme_name_handler(MetaPreference pref,const gchar * string_value,gboolean * inform_listeners)1223 theme_name_handler (MetaPreference pref,
1224                     const gchar *string_value,
1225                     gboolean *inform_listeners)
1226 {
1227   g_free (current_theme);
1228 
1229   /* Fallback crackrock */
1230   if (string_value == NULL)
1231     current_theme = g_strdup ("ClearlooksRe");
1232   else
1233     current_theme = g_strdup (string_value);
1234 }
1235 
1236 static void
mouse_button_mods_handler(MetaPreference pref,const gchar * string_value,gboolean * inform_listeners)1237 mouse_button_mods_handler (MetaPreference pref,
1238                            const gchar *string_value,
1239                            gboolean *inform_listeners)
1240 {
1241   MetaVirtualModifier mods;
1242 
1243   meta_topic (META_DEBUG_KEYBINDINGS,
1244               "Mouse button modifier has new GSettings value \"%s\"\n",
1245               string_value);
1246   if (string_value && meta_ui_parse_modifier (string_value, &mods))
1247     {
1248       mouse_button_mods = mods;
1249     }
1250   else
1251     {
1252       meta_topic (META_DEBUG_KEYBINDINGS,
1253                   "Failed to parse new GSettings value\n");
1254 
1255       meta_warning (_("\"%s\" found in configuration database is "
1256                       "not a valid value for mouse button modifier\n"),
1257                     string_value);
1258 
1259       *inform_listeners = FALSE;
1260     }
1261 }
1262 
1263 static void
show_desktop_skip_list_handler(MetaPreference pref,const gchar * string_value,gboolean * inform_listeners)1264 show_desktop_skip_list_handler (MetaPreference pref,
1265                                 const gchar *string_value,
1266                                 gboolean *inform_listeners)
1267 {
1268   gchar **tokens;
1269   gchar **tok;
1270   GList *item;
1271 
1272   if (show_desktop_skip_list)
1273     {
1274       for (item = show_desktop_skip_list; item; item = item->next)
1275         g_free (item->data);
1276       g_list_free (show_desktop_skip_list);
1277       show_desktop_skip_list = NULL;
1278     }
1279 
1280   if (!(tokens = g_strsplit (string_value, ",", -1)))
1281     return;
1282   for (tok = tokens; tok && *tok; tok++)
1283     {
1284       gchar *stripped = g_strstrip (g_strdup (*tok));
1285       show_desktop_skip_list = g_list_prepend (show_desktop_skip_list, stripped);
1286     }
1287   g_strfreev (tokens);
1288 }
1289 
1290 static gboolean
button_layout_equal(const MetaButtonLayout * a,const MetaButtonLayout * b)1291 button_layout_equal (const MetaButtonLayout *a,
1292                      const MetaButtonLayout *b)
1293 {
1294   int i;
1295 
1296   i = 0;
1297   while (i < MAX_BUTTONS_PER_CORNER)
1298     {
1299       if (a->left_buttons[i] != b->left_buttons[i])
1300         return FALSE;
1301       if (a->right_buttons[i] != b->right_buttons[i])
1302         return FALSE;
1303       if (a->left_buttons_has_spacer[i] != b->left_buttons_has_spacer[i])
1304         return FALSE;
1305       if (a->right_buttons_has_spacer[i] != b->right_buttons_has_spacer[i])
1306         return FALSE;
1307       ++i;
1308     }
1309 
1310   return TRUE;
1311 }
1312 
1313 static MetaButtonFunction
button_function_from_string(const char * str)1314 button_function_from_string (const char *str)
1315 {
1316   /* FIXME: g_settings_get_enum is the obvious way to do this */
1317 
1318   if (strcmp (str, "menu") == 0)
1319     return META_BUTTON_FUNCTION_MENU;
1320   else if (strcmp (str, "appmenu") == 0)
1321     return META_BUTTON_FUNCTION_APPMENU;
1322   else if (strcmp (str, "minimize") == 0)
1323     return META_BUTTON_FUNCTION_MINIMIZE;
1324   else if (strcmp (str, "maximize") == 0)
1325     return META_BUTTON_FUNCTION_MAXIMIZE;
1326   else if (strcmp (str, "close") == 0)
1327     return META_BUTTON_FUNCTION_CLOSE;
1328   else if (strcmp (str, "shade") == 0)
1329     return META_BUTTON_FUNCTION_SHADE;
1330   else if (strcmp (str, "above") == 0)
1331     return META_BUTTON_FUNCTION_ABOVE;
1332   else if (strcmp (str, "stick") == 0)
1333     return META_BUTTON_FUNCTION_STICK;
1334   else
1335     /* don't know; give up */
1336     return META_BUTTON_FUNCTION_LAST;
1337 }
1338 
1339 static MetaButtonFunction
button_opposite_function(MetaButtonFunction ofwhat)1340 button_opposite_function (MetaButtonFunction ofwhat)
1341 {
1342   switch (ofwhat)
1343     {
1344     case META_BUTTON_FUNCTION_SHADE:
1345       return META_BUTTON_FUNCTION_UNSHADE;
1346     case META_BUTTON_FUNCTION_UNSHADE:
1347       return META_BUTTON_FUNCTION_SHADE;
1348 
1349     case META_BUTTON_FUNCTION_ABOVE:
1350       return META_BUTTON_FUNCTION_UNABOVE;
1351     case META_BUTTON_FUNCTION_UNABOVE:
1352       return META_BUTTON_FUNCTION_ABOVE;
1353 
1354     case META_BUTTON_FUNCTION_STICK:
1355       return META_BUTTON_FUNCTION_UNSTICK;
1356     case META_BUTTON_FUNCTION_UNSTICK:
1357       return META_BUTTON_FUNCTION_STICK;
1358 
1359     default:
1360       return META_BUTTON_FUNCTION_LAST;
1361     }
1362 }
1363 
1364 static void
button_layout_handler(MetaPreference pref,const gchar * string_value,gboolean * inform_listeners)1365 button_layout_handler (MetaPreference pref,
1366                          const gchar *string_value,
1367                          gboolean *inform_listeners)
1368 {
1369   MetaButtonLayout new_layout;
1370   char **sides = NULL;
1371   int i;
1372 
1373   /* We need to ignore unknown button functions, for
1374    * compat with future versions
1375    */
1376 
1377   if (string_value)
1378     sides = g_strsplit (string_value, ":", 2);
1379 
1380   if (sides != NULL && sides[0] != NULL)
1381     {
1382       char **buttons;
1383       int b;
1384       gboolean used[META_BUTTON_FUNCTION_LAST];
1385 
1386       i = 0;
1387       while (i < META_BUTTON_FUNCTION_LAST)
1388         {
1389           used[i] = FALSE;
1390           new_layout.left_buttons_has_spacer[i] = FALSE;
1391           ++i;
1392         }
1393 
1394       buttons = g_strsplit (sides[0], ",", -1);
1395       i = 0;
1396       b = 0;
1397       while (buttons[b] != NULL)
1398         {
1399           MetaButtonFunction f = button_function_from_string (buttons[b]);
1400           if (i > 0 && strcmp("spacer", buttons[b]) == 0)
1401             {
1402               new_layout.left_buttons_has_spacer[i-1] = TRUE;
1403               f = button_opposite_function (f);
1404 
1405               if (f != META_BUTTON_FUNCTION_LAST)
1406                 {
1407                   new_layout.left_buttons_has_spacer[i-2] = TRUE;
1408                 }
1409             }
1410           else
1411             {
1412               if (f != META_BUTTON_FUNCTION_LAST && !used[f])
1413                 {
1414                   new_layout.left_buttons[i] = f;
1415                   used[f] = TRUE;
1416                   ++i;
1417 
1418                   f = button_opposite_function (f);
1419 
1420                   if (f != META_BUTTON_FUNCTION_LAST)
1421                       new_layout.left_buttons[i++] = f;
1422 
1423                 }
1424               else
1425                 {
1426                   meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n",
1427                               buttons[b]);
1428                 }
1429             }
1430 
1431           ++b;
1432         }
1433 
1434       new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
1435       new_layout.left_buttons_has_spacer[i] = FALSE;
1436 
1437       g_strfreev (buttons);
1438     }
1439 
1440   if (sides != NULL && sides[0] != NULL && sides[1] != NULL)
1441     {
1442       char **buttons;
1443       int b;
1444       gboolean used[META_BUTTON_FUNCTION_LAST];
1445 
1446       i = 0;
1447       while (i < META_BUTTON_FUNCTION_LAST)
1448         {
1449           used[i] = FALSE;
1450           new_layout.right_buttons_has_spacer[i] = FALSE;
1451           ++i;
1452         }
1453 
1454       buttons = g_strsplit (sides[1], ",", -1);
1455       i = 0;
1456       b = 0;
1457       while (buttons[b] != NULL)
1458         {
1459           MetaButtonFunction f = button_function_from_string (buttons[b]);
1460           if (i > 0 && strcmp("spacer", buttons[b]) == 0)
1461             {
1462               new_layout.right_buttons_has_spacer[i-1] = TRUE;
1463               f = button_opposite_function (f);
1464               if (f != META_BUTTON_FUNCTION_LAST)
1465                 {
1466                   new_layout.right_buttons_has_spacer[i-2] = TRUE;
1467                 }
1468             }
1469           else
1470             {
1471               if (f != META_BUTTON_FUNCTION_LAST && !used[f])
1472                 {
1473                   new_layout.right_buttons[i] = f;
1474                   used[f] = TRUE;
1475                   ++i;
1476 
1477                   f = button_opposite_function (f);
1478 
1479                   if (f != META_BUTTON_FUNCTION_LAST)
1480                       new_layout.right_buttons[i++] = f;
1481 
1482                 }
1483               else
1484                 {
1485                   meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n",
1486                               buttons[b]);
1487                 }
1488             }
1489 
1490           ++b;
1491         }
1492 
1493       new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
1494       new_layout.right_buttons_has_spacer[i] = FALSE;
1495 
1496       g_strfreev (buttons);
1497     }
1498 
1499   g_strfreev (sides);
1500 
1501   /* Invert the button layout for RTL languages */
1502   if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
1503   {
1504     MetaButtonLayout rtl_layout;
1505     int j;
1506 
1507     for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
1508     for (j = 0; j < i; j++)
1509       {
1510         rtl_layout.right_buttons[j] = new_layout.left_buttons[i - j - 1];
1511         if (j == 0)
1512           rtl_layout.right_buttons_has_spacer[i - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
1513         else
1514           rtl_layout.right_buttons_has_spacer[j - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
1515       }
1516     rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST;
1517     rtl_layout.right_buttons_has_spacer[j] = FALSE;
1518 
1519     for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
1520     for (j = 0; j < i; j++)
1521       {
1522         rtl_layout.left_buttons[j] = new_layout.right_buttons[i - j - 1];
1523         if (j == 0)
1524           rtl_layout.left_buttons_has_spacer[i - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
1525         else
1526           rtl_layout.left_buttons_has_spacer[j - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
1527       }
1528     rtl_layout.left_buttons[j] = META_BUTTON_FUNCTION_LAST;
1529     rtl_layout.left_buttons_has_spacer[j] = FALSE;
1530 
1531     new_layout = rtl_layout;
1532   }
1533 
1534   if (button_layout_equal (&button_layout, &new_layout))
1535     {
1536       /* Same as before, so duck out */
1537       *inform_listeners = FALSE;
1538     }
1539   else
1540     {
1541       button_layout = new_layout;
1542     }
1543 }
1544 
1545 const PangoFontDescription*
meta_prefs_get_titlebar_font(void)1546 meta_prefs_get_titlebar_font (void)
1547 {
1548   if (use_system_font)
1549     return NULL;
1550   else
1551     return titlebar_font;
1552 }
1553 
1554 int
meta_prefs_get_num_workspaces(void)1555 meta_prefs_get_num_workspaces (void)
1556 {
1557   return num_workspaces;
1558 }
1559 
1560 MetaWrapStyle
meta_prefs_get_wrap_style(void)1561 meta_prefs_get_wrap_style (void)
1562 {
1563   return wrap_style;
1564 }
1565 
1566 gboolean
meta_prefs_get_application_based(void)1567 meta_prefs_get_application_based (void)
1568 {
1569   return FALSE; /* For now, we never want this to do anything */
1570 
1571   return application_based;
1572 }
1573 
1574 gboolean
meta_prefs_get_disable_workarounds(void)1575 meta_prefs_get_disable_workarounds (void)
1576 {
1577   return disable_workarounds;
1578 }
1579 
1580 #define MAX_REASONABLE_AUTO_RAISE_DELAY 10000
1581 
1582 #ifdef WITH_VERBOSE_MODE
1583 const char*
meta_preference_to_string(MetaPreference pref)1584 meta_preference_to_string (MetaPreference pref)
1585 {
1586   /* FIXME: another case for g_settings_get_enum */
1587   switch (pref)
1588     {
1589     case META_PREF_MOUSE_BUTTON_MODS:
1590       return "MOUSE_BUTTON_MODS";
1591 
1592     case META_PREF_FOCUS_MODE:
1593       return "FOCUS_MODE";
1594 
1595     case META_PREF_FOCUS_NEW_WINDOWS:
1596       return "FOCUS_NEW_WINDOWS";
1597 
1598     case META_PREF_ATTACH_MODAL_DIALOGS:
1599       return "ATTACH_MODAL_DIALOGS";
1600 
1601     case META_PREF_RAISE_ON_CLICK:
1602       return "RAISE_ON_CLICK";
1603 
1604     case META_PREF_THEME:
1605       return "THEME";
1606 
1607     case META_PREF_TITLEBAR_FONT:
1608       return "TITLEBAR_FONT";
1609 
1610     case META_PREF_NUM_WORKSPACES:
1611       return "NUM_WORKSPACES";
1612 
1613     case META_PREF_WRAP_STYLE:
1614       return "WRAP_STYLE";
1615 
1616     case META_PREF_APPLICATION_BASED:
1617       return "APPLICATION_BASED";
1618 
1619     case META_PREF_KEYBINDINGS:
1620       return "KEYBINDINGS";
1621 
1622     case META_PREF_DISABLE_WORKAROUNDS:
1623       return "DISABLE_WORKAROUNDS";
1624 
1625     case META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR:
1626       return "ACTION_DOUBLE_CLICK_TITLEBAR";
1627 
1628     case META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR:
1629       return "ACTION_MIDDLE_CLICK_TITLEBAR";
1630 
1631     case META_PREF_ACTION_RIGHT_CLICK_TITLEBAR:
1632       return "ACTION_RIGHT_CLICK_TITLEBAR";
1633 
1634     case META_PREF_AUTO_RAISE:
1635       return "AUTO_RAISE";
1636 
1637     case META_PREF_AUTO_RAISE_DELAY:
1638       return "AUTO_RAISE_DELAY";
1639 
1640     case META_PREF_COMMANDS:
1641       return "COMMANDS";
1642 
1643     case META_PREF_TERMINAL_COMMAND:
1644       return "TERMINAL_COMMAND";
1645 
1646     case META_PREF_BUTTON_LAYOUT:
1647       return "BUTTON_LAYOUT";
1648 
1649     case META_PREF_WORKSPACE_NAMES:
1650       return "WORKSPACE_NAMES";
1651 
1652     case META_PREF_VISUAL_BELL:
1653       return "VISUAL_BELL";
1654 
1655     case META_PREF_AUDIBLE_BELL:
1656       return "AUDIBLE_BELL";
1657 
1658     case META_PREF_VISUAL_BELL_TYPE:
1659       return "VISUAL_BELL_TYPE";
1660 
1661     case META_PREF_REDUCED_RESOURCES:
1662       return "REDUCED_RESOURCES";
1663 
1664     case META_PREF_MATE_ACCESSIBILITY:
1665       return "MATE_ACCESSIBILTY";
1666 
1667     case META_PREF_MATE_ANIMATIONS:
1668       return "MATE_ANIMATIONS";
1669 
1670     case META_PREF_CURSOR_THEME:
1671       return "CURSOR_THEME";
1672 
1673     case META_PREF_CURSOR_SIZE:
1674       return "CURSOR_SIZE";
1675 
1676     case META_PREF_ICON_SIZE:
1677       return "ICON_SIZE";
1678 
1679     case META_PREF_ALT_TAB_MAX_COLUMNS:
1680       return "ALT_TAB_MAX_COLUMNS";
1681 
1682     case META_PREF_ALT_TAB_EXPAND_TO_FIT_TITLE:
1683       return "ALT_TAB_EXPAND_TO_FIT_TITLE";
1684 
1685     case META_PREF_COMPOSITING_MANAGER:
1686       return "COMPOSITING_MANAGER";
1687 
1688     case META_PREF_COMPOSITING_FAST_ALT_TAB:
1689       return "COMPOSITING_FAST_ALT_TAB";
1690 
1691     case META_PREF_CENTER_NEW_WINDOWS:
1692       return "CENTER_NEW_WINDOWS";
1693 
1694     case META_PREF_RESIZE_WITH_RIGHT_BUTTON:
1695       return "RESIZE_WITH_RIGHT_BUTTON";
1696 
1697     case META_PREF_SHOW_TAB_BORDER:
1698       return "SHOW_TAB_BORDER";
1699 
1700     case META_PREF_FORCE_FULLSCREEN:
1701       return "FORCE_FULLSCREEN";
1702 
1703     case META_PREF_ALLOW_TILING:
1704       return "ALLOW_TILING";
1705 
1706     case META_PREF_ALLOW_TOP_TILING:
1707       return "ALLOW_TOP_TILING";
1708 
1709     case META_PREF_ALLOW_TILE_CYCLING:
1710       return "ALLOW_TILE_CYCLING";
1711 
1712     case META_PREF_PLACEMENT_MODE:
1713       return "PLACEMENT_MODE";
1714 
1715     case META_PREF_SHOW_DESKTOP_SKIP_LIST:
1716       return "SHOW_DESKTOP_SKIP_LIST";
1717     }
1718 
1719   return "(unknown)";
1720 }
1721 #endif /* WITH_VERBOSE_MODE */
1722 
1723 void
meta_prefs_set_num_workspaces(int n_workspaces)1724 meta_prefs_set_num_workspaces (int n_workspaces)
1725 {
1726   if (n_workspaces < 1)
1727     n_workspaces = 1;
1728   if (n_workspaces > MAX_REASONABLE_WORKSPACES)
1729     n_workspaces = MAX_REASONABLE_WORKSPACES;
1730 
1731   g_settings_set_int (settings_general,
1732                       KEY_GENERAL_NUM_WORKSPACES,
1733                       n_workspaces);
1734 
1735 }
1736 
1737 #define keybind(name, handler, param, flags) \
1738   { #name, NULL, !!(flags & BINDING_REVERSES), !!(flags & BINDING_PER_WINDOW) },
1739 static MetaKeyPref key_bindings[] = {
1740 #include "all-keybindings.h"
1741   { NULL, NULL, FALSE }
1742 };
1743 #undef keybind
1744 
1745 static void
init_bindings(GSettings * settings)1746 init_bindings (GSettings *settings)
1747 {
1748   GSettingsSchema *schema;
1749   gchar **list = NULL;
1750   gchar *str_val = NULL;
1751 
1752   g_object_get (settings, "settings-schema", &schema, NULL);
1753   list = g_settings_schema_list_keys (schema);
1754   g_settings_schema_unref (schema);
1755 
1756   while (*list != NULL)
1757     {
1758       str_val = g_settings_get_string (settings, *list);
1759       update_key_binding (*list, str_val);
1760       list++;
1761     }
1762 
1763   g_free (str_val);
1764 }
1765 
1766 static void
init_screen_bindings(void)1767 init_screen_bindings (void)
1768 {
1769   init_bindings (settings_screen_bindings);
1770 }
1771 
1772 static void
init_window_bindings(void)1773 init_window_bindings (void)
1774 {
1775   init_bindings (settings_window_bindings);
1776 }
1777 
1778 static void
init_commands(void)1779 init_commands (void)
1780 {
1781   GSettingsSchema *schema;
1782   gchar **list = NULL;
1783   gchar *str_val = NULL;
1784 
1785   g_object_get (settings_command, "settings-schema", &schema, NULL);
1786   list = g_settings_schema_list_keys (schema);
1787   g_settings_schema_unref (schema);
1788 
1789   while (*list != NULL)
1790     {
1791       str_val = g_settings_get_string (settings_command, *list);
1792       update_command (*list, str_val);
1793       list++;
1794     }
1795 
1796   g_free (str_val);
1797 }
1798 
1799 static void
init_workspace_names(void)1800 init_workspace_names (void)
1801 {
1802   GSettingsSchema *schema;
1803   gchar **list = NULL;
1804   gchar *str_val = NULL;
1805 
1806   g_object_get (settings_workspace_names, "settings-schema", &schema, NULL);
1807   list = g_settings_schema_list_keys (schema);
1808   g_settings_schema_unref (schema);
1809 
1810   while (*list != NULL)
1811     {
1812       str_val = g_settings_get_string (settings_workspace_names, *list);
1813       update_workspace_name (*list, str_val);
1814       list++;
1815     }
1816 
1817   g_free (str_val);
1818 }
1819 
1820 static gboolean
update_binding(MetaKeyPref * binding,gchar * value)1821 update_binding (MetaKeyPref *binding,
1822                 gchar  *value)
1823 {
1824   unsigned int keysym;
1825   unsigned int keycode;
1826   MetaVirtualModifier mods;
1827   MetaKeyCombo *combo;
1828   gboolean changed;
1829 
1830   meta_topic (META_DEBUG_KEYBINDINGS,
1831               "Binding \"%s\" has new GSettings value \"%s\"\n",
1832               binding->name, value ? value : "none");
1833 
1834   keysym = 0;
1835   keycode = 0;
1836   mods = 0;
1837   if (value)
1838     {
1839       if (!meta_ui_parse_accelerator (value, &keysym, &keycode, &mods))
1840         {
1841           meta_topic (META_DEBUG_KEYBINDINGS,
1842                       "Failed to parse new GSettings value\n");
1843           meta_warning (_("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"\n"),
1844                         value, binding->name);
1845 
1846           return FALSE;
1847         }
1848     }
1849 
1850   /* If there isn't already a first element, make one. */
1851   if (!binding->bindings)
1852     {
1853       MetaKeyCombo *blank = g_malloc0 (sizeof (MetaKeyCombo));
1854       binding->bindings = g_slist_alloc();
1855       binding->bindings->data = blank;
1856     }
1857 
1858    combo = binding->bindings->data;
1859 
1860    /* Bug 329676: Bindings which can be shifted must not have no modifiers,
1861     * nor only SHIFT as a modifier.
1862     */
1863 
1864   if (binding->add_shift &&
1865       0 != keysym &&
1866       (META_VIRTUAL_SHIFT_MASK == mods || 0 == mods))
1867     {
1868       gchar *old_setting;
1869 
1870       meta_warning ("Cannot bind \"%s\" to %s: it needs a modifier "
1871                     "such as Ctrl or Alt.\n",
1872                     binding->name,
1873                     value);
1874 
1875       old_setting = meta_ui_accelerator_name (combo->keysym,
1876                                               combo->modifiers);
1877 
1878       if (!strcmp(old_setting, value))
1879         {
1880           /* We were about to set it to the same value
1881            * that it had originally! This must be caused
1882            * by getting an invalid string back from
1883            * meta_ui_accelerator_name. Bail out now
1884            * so we don't get into an infinite loop.
1885            */
1886            g_free (old_setting);
1887            return TRUE;
1888         }
1889 
1890       meta_warning ("Reverting \"%s\" to %s.\n",
1891                     binding->name,
1892                     old_setting);
1893 
1894       /* FIXME: add_shift is currently screen_bindings only, but
1895        * there's no really good reason it should always be.
1896        * So we shouldn't blindly add KEY_SCREEN_BINDINGS_PREFIX
1897        * onto here.
1898        */
1899       g_settings_set_string(settings_screen_bindings,
1900                             binding->name,
1901                             old_setting);
1902 
1903       g_free (old_setting);
1904 
1905       /* The call to g_settings_set_string() will cause this function
1906        * to be called again with the new value, so there's no need to
1907        * carry on.
1908        */
1909       return TRUE;
1910     }
1911 
1912   changed = FALSE;
1913   if (keysym != combo->keysym ||
1914       keycode != combo->keycode ||
1915       mods != combo->modifiers)
1916     {
1917       changed = TRUE;
1918 
1919       combo->keysym = keysym;
1920       combo->keycode = keycode;
1921       combo->modifiers = mods;
1922 
1923       meta_topic (META_DEBUG_KEYBINDINGS,
1924                   "New keybinding for \"%s\" is keysym = 0x%x keycode = 0x%x mods = 0x%x\n",
1925                   binding->name, combo->keysym, combo->keycode,
1926                   combo->modifiers);
1927     }
1928   else
1929     {
1930       meta_topic (META_DEBUG_KEYBINDINGS,
1931                   "Keybinding for \"%s\" is unchanged\n", binding->name);
1932     }
1933 
1934   return changed;
1935 }
1936 
1937 static const gchar*
relative_key(const gchar * key)1938 relative_key (const gchar* key)
1939 {
1940   const gchar* end;
1941 
1942   end = strrchr (key, '/');
1943 
1944   ++end;
1945 
1946   return end;
1947 }
1948 
1949 /* Return value is TRUE if a preference changed and we need to
1950  * notify
1951  */
1952 static gboolean
find_and_update_binding(MetaKeyPref * bindings,const char * name,gchar * value)1953 find_and_update_binding (MetaKeyPref *bindings,
1954                          const char  *name,
1955                          gchar  *value)
1956 {
1957   const char *key;
1958   int i;
1959 
1960   if (*name == '/')
1961     key = relative_key (name);
1962   else
1963     key = name;
1964 
1965   i = 0;
1966   while (bindings[i].name &&
1967          strcmp (key, bindings[i].name) != 0)
1968     ++i;
1969 
1970   if (bindings[i].name)
1971     return update_binding (&bindings[i], value);
1972   else
1973     return FALSE;
1974 }
1975 
1976 static gboolean
update_key_binding(const char * name,gchar * value)1977 update_key_binding (const char *name,
1978                     gchar *value)
1979 {
1980   return find_and_update_binding (key_bindings, name, value);
1981 }
1982 
1983 static gboolean
update_command(const char * name,const char * value)1984 update_command (const char  *name,
1985                 const char  *value)
1986 {
1987   char *p;
1988   int i;
1989 
1990   p = strrchr (name, '-');
1991   if (p == NULL)
1992     {
1993       meta_topic (META_DEBUG_KEYBINDINGS,
1994                   "Command %s has no dash?\n", name);
1995       return FALSE;
1996     }
1997 
1998   ++p;
1999 
2000   if (g_ascii_isdigit (*p))
2001     {
2002       i = atoi (p);
2003       i -= 1; /* count from 0 not 1 */
2004     }
2005   else
2006     {
2007       if (strcmp (name, "command-screenshot") == 0)
2008         {
2009           i = SCREENSHOT_COMMAND_IDX;
2010         }
2011       else if (strcmp (name, "command-window-screenshot") == 0)
2012         {
2013           i = WIN_SCREENSHOT_COMMAND_IDX;
2014         }
2015       else
2016         {
2017           meta_topic (META_DEBUG_KEYBINDINGS,
2018                       "Command %s doesn't end in number?\n", name);
2019           return FALSE;
2020         }
2021     }
2022 
2023   if (i >= MAX_COMMANDS)
2024     {
2025       meta_topic (META_DEBUG_KEYBINDINGS,
2026                   "Command %d is too highly numbered, ignoring\n", i);
2027       return FALSE;
2028     }
2029 
2030   if ((commands[i] == NULL && value == NULL) ||
2031       (commands[i] && value && strcmp (commands[i], value) == 0))
2032     {
2033       meta_topic (META_DEBUG_KEYBINDINGS,
2034                   "Command %d is unchanged\n", i);
2035       return FALSE;
2036     }
2037 
2038   g_free (commands[i]);
2039   commands[i] = g_strdup (value);
2040 
2041   meta_topic (META_DEBUG_KEYBINDINGS,
2042               "Updated command %d to \"%s\"\n",
2043               i, commands[i] ? commands[i] : "none");
2044 
2045   return TRUE;
2046 }
2047 
2048 const char*
meta_prefs_get_command(int i)2049 meta_prefs_get_command (int i)
2050 {
2051   g_return_val_if_fail (i >= 0 && i < MAX_COMMANDS, NULL);
2052 
2053   return commands[i];
2054 }
2055 
2056 char*
meta_prefs_get_settings_key_for_command(int i)2057 meta_prefs_get_settings_key_for_command (int i)
2058 {
2059   char *key;
2060 
2061   switch (i)
2062     {
2063     case SCREENSHOT_COMMAND_IDX:
2064       key = g_strdup (KEY_COMMAND_PREFIX "screenshot");
2065       break;
2066     case WIN_SCREENSHOT_COMMAND_IDX:
2067       key = g_strdup (KEY_COMMAND_PREFIX "window-screenshot");
2068       break;
2069     default:
2070       key = g_strdup_printf (KEY_COMMAND_PREFIX"%d", i + 1);
2071       break;
2072     }
2073 
2074   return key;
2075 }
2076 
2077 const char*
meta_prefs_get_terminal_command(void)2078 meta_prefs_get_terminal_command (void)
2079 {
2080   return terminal_command;
2081 }
2082 
2083 const char*
meta_prefs_get_settings_key_for_terminal_command(void)2084 meta_prefs_get_settings_key_for_terminal_command (void)
2085 {
2086   return KEY_MATE_TERMINAL_COMMAND;
2087 }
2088 
2089 static gboolean
update_workspace_name(const char * name,const char * value)2090 update_workspace_name (const char  *name,
2091                        const char  *value)
2092 {
2093   char *p;
2094   int i;
2095 
2096   p = strrchr (name, '-');
2097   if (p == NULL)
2098     {
2099       meta_topic (META_DEBUG_PREFS,
2100                   "Workspace name %s has no dash?\n", name);
2101       return FALSE;
2102     }
2103 
2104   ++p;
2105 
2106   if (!g_ascii_isdigit (*p))
2107     {
2108       meta_topic (META_DEBUG_PREFS,
2109                   "Workspace name %s doesn't end in number?\n", name);
2110       return FALSE;
2111     }
2112 
2113   i = atoi (p);
2114   i -= 1; /* count from 0 not 1 */
2115 
2116   if (i >= MAX_REASONABLE_WORKSPACES)
2117     {
2118       meta_topic (META_DEBUG_PREFS,
2119                   "Workspace name %d is too highly numbered, ignoring\n", i);
2120       return FALSE;
2121     }
2122 
2123   if (workspace_names[i] && value && strcmp (workspace_names[i], value) == 0)
2124     {
2125       meta_topic (META_DEBUG_PREFS,
2126                   "Workspace name %d is unchanged\n", i);
2127       return FALSE;
2128     }
2129 
2130   /* This is a bad hack. We have to treat empty string as
2131    * "unset" because the root window property can't contain
2132    * null. So it gets empty string instead and we don't want
2133    * that to result in setting the empty string as a value that
2134    * overrides "unset".
2135    */
2136   if (value != NULL && *value != '\0')
2137     {
2138       g_free (workspace_names[i]);
2139       workspace_names[i] = g_strdup (value);
2140     }
2141   else
2142     {
2143       /* use a default name */
2144       char *d;
2145 
2146       d = g_strdup_printf (_("Workspace %d"), i + 1);
2147       if (workspace_names[i] && strcmp (workspace_names[i], d) == 0)
2148         {
2149           g_free (d);
2150           return FALSE;
2151         }
2152       else
2153         {
2154           g_free (workspace_names[i]);
2155           workspace_names[i] = d;
2156         }
2157     }
2158 
2159   meta_topic (META_DEBUG_PREFS,
2160               "Updated workspace name %d to \"%s\"\n",
2161               i, workspace_names[i] ? workspace_names[i] : "none");
2162 
2163   return TRUE;
2164 }
2165 
2166 const char*
meta_prefs_get_workspace_name(int i)2167 meta_prefs_get_workspace_name (int i)
2168 {
2169   g_return_val_if_fail (i >= 0 && i < MAX_REASONABLE_WORKSPACES, NULL);
2170 
2171   g_assert (workspace_names[i] != NULL);
2172 
2173   meta_topic (META_DEBUG_PREFS,
2174               "Getting workspace name for %d: \"%s\"\n",
2175               i, workspace_names[i]);
2176 
2177   return workspace_names[i];
2178 }
2179 
2180 void
meta_prefs_change_workspace_name(int i,const char * name)2181 meta_prefs_change_workspace_name (int         i,
2182                                   const char *name)
2183 {
2184   char *key;
2185 
2186   g_return_if_fail (i >= 0 && i < MAX_REASONABLE_WORKSPACES);
2187 
2188   meta_topic (META_DEBUG_PREFS,
2189               "Changing name of workspace %d to %s\n",
2190               i, name ? name : "none");
2191 
2192   if (name && *name == '\0')
2193     name = NULL;
2194 
2195   if ((name == NULL && workspace_names[i] == NULL) ||
2196       (name && workspace_names[i] && strcmp (name, workspace_names[i]) == 0))
2197     {
2198       meta_topic (META_DEBUG_PREFS,
2199                   "Workspace %d already has name %s\n",
2200                   i, name ? name : "none");
2201       return;
2202     }
2203 
2204   key = settings_key_for_workspace_name (i);
2205 
2206   if (name != NULL)
2207     g_settings_set_string (settings_workspace_names,
2208                           key,
2209                           name);
2210   else
2211     g_settings_set_string (settings_workspace_names,
2212                           key,
2213                           "");
2214 
2215   g_free (key);
2216 }
2217 
2218 static char*
settings_key_for_workspace_name(int i)2219 settings_key_for_workspace_name (int i)
2220 {
2221   char *key;
2222 
2223   key = g_strdup_printf (KEY_WORKSPACE_NAME_PREFIX"%d", i + 1);
2224 
2225   return key;
2226 }
2227 
2228 void
meta_prefs_get_button_layout(MetaButtonLayout * button_layout_p)2229 meta_prefs_get_button_layout (MetaButtonLayout *button_layout_p)
2230 {
2231   *button_layout_p = button_layout;
2232 }
2233 
2234 gboolean
meta_prefs_get_visual_bell(void)2235 meta_prefs_get_visual_bell (void)
2236 {
2237   return provide_visual_bell;
2238 }
2239 
2240 gboolean
meta_prefs_bell_is_audible(void)2241 meta_prefs_bell_is_audible (void)
2242 {
2243   return bell_is_audible;
2244 }
2245 
2246 MetaVisualBellType
meta_prefs_get_visual_bell_type(void)2247 meta_prefs_get_visual_bell_type (void)
2248 {
2249   return visual_bell_type;
2250 }
2251 
2252 void
meta_prefs_get_key_bindings(const MetaKeyPref ** bindings,int * n_bindings)2253 meta_prefs_get_key_bindings (const MetaKeyPref **bindings,
2254                                 int                *n_bindings)
2255 {
2256 
2257   *bindings = key_bindings;
2258   *n_bindings = (int) G_N_ELEMENTS (key_bindings) - 1;
2259 }
2260 
2261 MetaActionTitlebar
meta_prefs_get_action_double_click_titlebar(void)2262 meta_prefs_get_action_double_click_titlebar (void)
2263 {
2264   return action_double_click_titlebar;
2265 }
2266 
2267 MetaActionTitlebar
meta_prefs_get_action_middle_click_titlebar(void)2268 meta_prefs_get_action_middle_click_titlebar (void)
2269 {
2270   return action_middle_click_titlebar;
2271 }
2272 
2273 MetaActionTitlebar
meta_prefs_get_action_right_click_titlebar(void)2274 meta_prefs_get_action_right_click_titlebar (void)
2275 {
2276   return action_right_click_titlebar;
2277 }
2278 
2279 gboolean
meta_prefs_get_auto_raise(void)2280 meta_prefs_get_auto_raise (void)
2281 {
2282   return auto_raise;
2283 }
2284 
2285 int
meta_prefs_get_auto_raise_delay(void)2286 meta_prefs_get_auto_raise_delay (void)
2287 {
2288   return auto_raise_delay;
2289 }
2290 
2291 gboolean
meta_prefs_get_reduced_resources(void)2292 meta_prefs_get_reduced_resources (void)
2293 {
2294   return reduced_resources;
2295 }
2296 
2297 gboolean
meta_prefs_get_mate_accessibility()2298 meta_prefs_get_mate_accessibility ()
2299 {
2300   return mate_accessibility;
2301 }
2302 
2303 gboolean
meta_prefs_get_mate_animations()2304 meta_prefs_get_mate_animations ()
2305 {
2306   return mate_animations;
2307 }
2308 
2309 MetaKeyBindingAction
meta_prefs_get_keybinding_action(const char * name)2310 meta_prefs_get_keybinding_action (const char *name)
2311 {
2312   int i;
2313 
2314   i = G_N_ELEMENTS (key_bindings) - 2; /* -2 for dummy entry at end */
2315   while (i >= 0)
2316     {
2317       if (strcmp (key_bindings[i].name, name) == 0)
2318         return (MetaKeyBindingAction) i;
2319 
2320       --i;
2321     }
2322 
2323   return META_KEYBINDING_ACTION_NONE;
2324 }
2325 
2326 /* This is used by the menu system to decide what key binding
2327  * to display next to an option. We return the first non-disabled
2328  * binding, if any.
2329  */
2330 void
meta_prefs_get_window_binding(const char * name,unsigned int * keysym,MetaVirtualModifier * modifiers)2331 meta_prefs_get_window_binding (const char          *name,
2332                                unsigned int        *keysym,
2333                                MetaVirtualModifier *modifiers)
2334 {
2335   int i;
2336 
2337   i = G_N_ELEMENTS (key_bindings) - 2; /* -2 for dummy entry at end */
2338   while (i >= 0)
2339     {
2340       if (key_bindings[i].per_window &&
2341           strcmp (key_bindings[i].name, name) == 0)
2342         {
2343           GSList *s = key_bindings[i].bindings;
2344 
2345           while (s)
2346             {
2347               MetaKeyCombo *c = s->data;
2348 
2349               if (c->keysym!=0 || c->modifiers!=0)
2350                 {
2351                   *keysym = c->keysym;
2352                   *modifiers = c->modifiers;
2353                   return;
2354                 }
2355 
2356               s = s->next;
2357             }
2358 
2359           /* Not found; return the disabled value */
2360           *keysym = *modifiers = 0;
2361           return;
2362         }
2363 
2364       --i;
2365     }
2366 
2367   g_assert_not_reached ();
2368 }
2369 
2370 gboolean
meta_prefs_get_compositing_manager(void)2371 meta_prefs_get_compositing_manager (void)
2372 {
2373   if (use_force_compositor_manager)
2374     return force_compositor_manager;
2375   return compositing_manager;
2376 }
2377 
2378 gboolean
meta_prefs_get_compositing_fast_alt_tab(void)2379 meta_prefs_get_compositing_fast_alt_tab (void)
2380 {
2381     return compositing_fast_alt_tab;
2382 }
2383 
2384 gboolean
meta_prefs_get_center_new_windows(void)2385 meta_prefs_get_center_new_windows (void)
2386 {
2387     return center_new_windows;
2388 }
2389 
2390 gboolean
meta_prefs_get_allow_tiling()2391 meta_prefs_get_allow_tiling ()
2392 {
2393   return allow_tiling;
2394 }
2395 
2396 gboolean
meta_prefs_get_allow_top_tiling()2397 meta_prefs_get_allow_top_tiling ()
2398 {
2399   return allow_top_tiling;
2400 }
2401 
2402 gboolean
meta_prefs_get_allow_tile_cycling()2403 meta_prefs_get_allow_tile_cycling ()
2404 {
2405   return allow_tile_cycling;
2406 }
2407 
2408 guint
meta_prefs_get_mouse_button_resize(void)2409 meta_prefs_get_mouse_button_resize (void)
2410 {
2411   return resize_with_right_button ? 3: 2;
2412 }
2413 
2414 guint
meta_prefs_get_mouse_button_menu(void)2415 meta_prefs_get_mouse_button_menu (void)
2416 {
2417   return resize_with_right_button ? 2: 3;
2418 }
2419 
2420 gboolean
meta_prefs_show_tab_border(void)2421 meta_prefs_show_tab_border(void)
2422 {
2423     return show_tab_border;
2424 }
2425 
2426 gboolean
meta_prefs_get_force_fullscreen(void)2427 meta_prefs_get_force_fullscreen (void)
2428 {
2429   return force_fullscreen;
2430 }
2431 
2432 MetaPlacementMode
meta_prefs_get_placement_mode(void)2433 meta_prefs_get_placement_mode (void)
2434 {
2435   return placement_mode;
2436 }
2437 
2438 void
meta_prefs_set_force_compositing_manager(gboolean whether)2439 meta_prefs_set_force_compositing_manager (gboolean whether)
2440 {
2441   use_force_compositor_manager = TRUE;
2442   force_compositor_manager = whether;
2443 }
2444 
2445 void
meta_prefs_set_compositing_fast_alt_tab(gboolean whether)2446 meta_prefs_set_compositing_fast_alt_tab (gboolean whether)
2447 {
2448     g_settings_set_boolean (settings_general,
2449                             KEY_GENERAL_COMPOSITOR_FAST_ALT_TAB,
2450                             whether);
2451 }
2452 
2453 void
meta_prefs_set_center_new_windows(gboolean whether)2454 meta_prefs_set_center_new_windows (gboolean whether)
2455 {
2456     g_settings_set_boolean (settings_general,
2457                             KEY_GENERAL_CENTER_NEW_WINDOWS,
2458                             whether);
2459 }
2460 
2461 void
meta_prefs_set_force_fullscreen(gboolean whether)2462 meta_prefs_set_force_fullscreen (gboolean whether)
2463 {
2464   force_fullscreen = whether;
2465 }
2466 
2467