summaryrefslogtreecommitdiff
path: root/src/core/prefs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/prefs.c')
-rw-r--r--src/core/prefs.c2794
1 files changed, 2794 insertions, 0 deletions
diff --git a/src/core/prefs.c b/src/core/prefs.c
new file mode 100644
index 00000000..494d3da1
--- /dev/null
+++ b/src/core/prefs.c
@@ -0,0 +1,2794 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/* Marco preferences */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc.
+ * Copyright (C) 2006 Elijah Newren
+ * Copyright (C) 2008 Thomas Thurman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "prefs.h"
+#include "ui.h"
+#include "util.h"
+#ifdef HAVE_MATECONF
+#include <mateconf/mateconf-client.h>
+#endif
+#include <string.h>
+#include <stdlib.h>
+
+#define MAX_REASONABLE_WORKSPACES 36
+
+#define MAX_COMMANDS (32 + NUM_EXTRA_COMMANDS)
+#define NUM_EXTRA_COMMANDS 2
+#define SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 2)
+#define WIN_SCREENSHOT_COMMAND_IDX (MAX_COMMANDS - 1)
+
+/* If you add a key, it needs updating in init() and in the mateconf
+ * notify listener and of course in the .schemas file.
+ *
+ * Keys which are handled by one of the unified handlers below are
+ * not given a name here, because the purpose of the unified handlers
+ * is that keys should be referred to exactly once.
+ */
+#define KEY_TITLEBAR_FONT "/apps/marco/general/titlebar_font"
+#define KEY_NUM_WORKSPACES "/apps/marco/general/num_workspaces"
+#define KEY_COMPOSITOR "/apps/marco/general/compositing_manager"
+#define KEY_MATE_ACCESSIBILITY "/desktop/mate/interface/accessibility"
+
+#define KEY_COMMAND_DIRECTORY "/apps/marco/keybinding_commands"
+#define KEY_COMMAND_PREFIX "/apps/marco/keybinding_commands/command_"
+
+#define KEY_TERMINAL_DIR "/desktop/mate/applications/terminal"
+#define KEY_TERMINAL_COMMAND KEY_TERMINAL_DIR "/exec"
+
+#define KEY_SCREEN_BINDINGS_PREFIX "/apps/marco/global_keybindings"
+#define KEY_WINDOW_BINDINGS_PREFIX "/apps/marco/window_keybindings"
+#define KEY_LIST_BINDINGS_SUFFIX "_list"
+
+#define KEY_WORKSPACE_NAME_DIRECTORY "/apps/marco/workspace_names"
+#define KEY_WORKSPACE_NAME_PREFIX "/apps/marco/workspace_names/name_"
+
+
+#ifdef HAVE_MATECONF
+static MateConfClient *default_client = NULL;
+static GList *changes = NULL;
+static guint changed_idle;
+static GList *listeners = NULL;
+#endif
+
+static gboolean use_system_font = FALSE;
+static PangoFontDescription *titlebar_font = NULL;
+static MetaVirtualModifier mouse_button_mods = Mod1Mask;
+static MetaFocusMode focus_mode = META_FOCUS_MODE_CLICK;
+static MetaFocusNewWindows focus_new_windows = META_FOCUS_NEW_WINDOWS_SMART;
+static gboolean raise_on_click = TRUE;
+static char* current_theme = NULL;
+static int num_workspaces = 4;
+static MetaActionTitlebar action_double_click_titlebar = META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE;
+static MetaActionTitlebar action_middle_click_titlebar = META_ACTION_TITLEBAR_LOWER;
+static MetaActionTitlebar action_right_click_titlebar = META_ACTION_TITLEBAR_MENU;
+static gboolean application_based = FALSE;
+static gboolean disable_workarounds = FALSE;
+static gboolean auto_raise = FALSE;
+static gboolean auto_raise_delay = 500;
+static gboolean provide_visual_bell = FALSE;
+static gboolean bell_is_audible = TRUE;
+static gboolean reduced_resources = FALSE;
+static gboolean mate_accessibility = FALSE;
+static gboolean mate_animations = TRUE;
+static char *cursor_theme = NULL;
+static int cursor_size = 24;
+static gboolean compositing_manager = FALSE;
+static gboolean resize_with_right_button = FALSE;
+static gboolean force_fullscreen = TRUE;
+
+static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;
+static MetaButtonLayout button_layout;
+
+/* The screenshot commands are at the end */
+static char *commands[MAX_COMMANDS] = { NULL, };
+
+static char *terminal_command = NULL;
+
+static char *workspace_names[MAX_REASONABLE_WORKSPACES] = { NULL, };
+
+#ifdef HAVE_MATECONF
+static gboolean handle_preference_update_enum (const gchar *key, MateConfValue *value);
+
+static gboolean update_key_binding (const char *name,
+ const char *value);
+static gboolean find_and_update_list_binding (MetaKeyPref *bindings,
+ const char *name,
+ GSList *value);
+static gboolean update_key_list_binding (const char *name,
+ GSList *value);
+static gboolean update_command (const char *name,
+ const char *value);
+static gboolean update_workspace_name (const char *name,
+ const char *value);
+
+static void change_notify (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data);
+
+static char* mateconf_key_for_workspace_name (int i);
+
+static void queue_changed (MetaPreference pref);
+
+typedef enum
+ {
+ META_LIST_OF_STRINGS,
+ META_LIST_OF_MATECONFVALUE_STRINGS
+ } MetaStringListType;
+
+static gboolean update_list_binding (MetaKeyPref *binding,
+ GSList *value,
+ MetaStringListType type_of_value);
+
+static void cleanup_error (GError **error);
+static gboolean get_bool (const char *key, gboolean *val);
+static void maybe_give_disable_workarounds_warning (void);
+
+static void titlebar_handler (MetaPreference, const gchar*, gboolean*);
+static void theme_name_handler (MetaPreference, const gchar*, gboolean*);
+static void mouse_button_mods_handler (MetaPreference, const gchar*, gboolean*);
+static void button_layout_handler (MetaPreference, const gchar*, gboolean*);
+
+#endif /* HAVE_MATECONF */
+
+static gboolean update_binding (MetaKeyPref *binding,
+ const char *value);
+
+static void init_bindings (void);
+static void init_commands (void);
+static void init_workspace_names (void);
+
+#ifndef HAVE_MATECONF
+static void init_button_layout (void);
+#endif /* !HAVE_MATECONF */
+
+#ifdef HAVE_MATECONF
+
+typedef struct
+{
+ MetaPrefsChangedFunc func;
+ gpointer data;
+} MetaPrefsListener;
+
+static MateConfEnumStringPair symtab_focus_mode[] =
+ {
+ { META_FOCUS_MODE_CLICK, "click" },
+ { META_FOCUS_MODE_SLOPPY, "sloppy" },
+ { META_FOCUS_MODE_MOUSE, "mouse" },
+ { 0, NULL },
+ };
+
+static MateConfEnumStringPair symtab_focus_new_windows[] =
+ {
+ { META_FOCUS_NEW_WINDOWS_SMART, "smart" },
+ { META_FOCUS_NEW_WINDOWS_STRICT, "strict" },
+ { 0, NULL },
+ };
+
+static MateConfEnumStringPair symtab_visual_bell_type[] =
+ {
+ /* Note to the reader: 0 is an invalid value; these start at 1. */
+ { META_VISUAL_BELL_FULLSCREEN_FLASH, "fullscreen" },
+ { META_VISUAL_BELL_FRAME_FLASH, "frame_flash" },
+ { 0, NULL },
+ };
+
+static MateConfEnumStringPair symtab_titlebar_action[] =
+ {
+ { META_ACTION_TITLEBAR_TOGGLE_SHADE, "toggle_shade" },
+ { META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE, "toggle_maximize" },
+ { META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE_HORIZONTALLY,
+ "toggle_maximize_horizontally" },
+ { META_ACTION_TITLEBAR_TOGGLE_MAXIMIZE_VERTICALLY,
+ "toggle_maximize_vertically" },
+ { META_ACTION_TITLEBAR_MINIMIZE, "minimize" },
+ { META_ACTION_TITLEBAR_NONE, "none" },
+ { META_ACTION_TITLEBAR_LOWER, "lower" },
+ { META_ACTION_TITLEBAR_MENU, "menu" },
+ { META_ACTION_TITLEBAR_TOGGLE_SHADE, "toggle_shade" },
+ { 0, NULL },
+ };
+
+/**
+ * The details of one preference which is constrained to be
+ * one of a small number of string values-- in other words,
+ * an enumeration.
+ *
+ * We could have done this other ways. One particularly attractive
+ * possibility would have been to represent the entire symbol table
+ * as a space-separated string literal in the list of symtabs, so
+ * the focus mode enums could have been represented simply by
+ * "click sloppy mouse". However, the simplicity gained would have
+ * been outweighed by the bugs caused when the ordering of the enum
+ * strings got out of sync with the actual enum statement. Also,
+ * there is existing library code to use this kind of symbol tables.
+ *
+ * Other things we might consider doing to clean this up in the
+ * future include:
+ *
+ * - most of the keys begin with the same prefix, and perhaps we
+ * could assume it if they don't start with a slash
+ *
+ * - there are several cases where a single identifier could be used
+ * to generate an entire entry, and perhaps this could be done
+ * with a macro. (This would reduce clarity, however, and is
+ * probably a bad thing.)
+ *
+ * - these types all begin with a gchar* (and contain a MetaPreference)
+ * and we can factor out the repeated code in the handlers by taking
+ * advantage of this using some kind of union arrangement.
+ */
+typedef struct
+{
+ gchar *key;
+ MetaPreference pref;
+ MateConfEnumStringPair *symtab;
+ gpointer target;
+} MetaEnumPreference;
+
+typedef struct
+{
+ gchar *key;
+ MetaPreference pref;
+ gboolean *target;
+ gboolean becomes_true_on_destruction;
+} MetaBoolPreference;
+
+typedef struct
+{
+ gchar *key;
+ MetaPreference pref;
+
+ /**
+ * A handler. Many of the string preferences aren't stored as
+ * strings and need parsing; others of them have default values
+ * which can't be solved in the general case. If you include a
+ * function pointer here, it will be called before the string
+ * value is written out to the target variable.
+ *
+ * The function is passed two arguments: the preference, and
+ * the new string as a gchar*. It returns a gboolean;
+ * only if this is true, the listeners will be informed that
+ * the preference has changed.
+ *
+ * This may be NULL. If it is, see "target", below.
+ */
+ void (*handler) (MetaPreference pref,
+ const gchar *string_value,
+ gboolean *inform_listeners);
+
+ /**
+ * Where to write the incoming string.
+ *
+ * This must be NULL if the handler is non-NULL.
+ * If the incoming string is NULL, no change will be made.
+ */
+ gchar **target;
+
+} MetaStringPreference;
+
+#define METAINTPREFERENCE_NO_CHANGE_ON_DESTROY G_MININT
+
+typedef struct
+{
+ gchar *key;
+ MetaPreference pref;
+ gint *target;
+ /**
+ * Minimum and maximum values of the integer.
+ * If the new value is out of bounds, it will be discarded with a warning.
+ */
+ gint minimum, maximum;
+ /**
+ * Value to use if the key is destroyed.
+ * If this is METAINTPREFERENCE_NO_CHANGE_ON_DESTROY, it will
+ * not be changed when the key is destroyed.
+ */
+ gint value_if_destroyed;
+} MetaIntPreference;
+
+/* FIXMEs: */
+/* @@@ Don't use NULL lines at the end; glib can tell you how big it is */
+/* @@@ /apps/marco/general should be assumed if first char is not / */
+/* @@@ Will it ever be possible to merge init and update? If not, why not? */
+
+static MetaEnumPreference preferences_enum[] =
+ {
+ { "/apps/marco/general/focus_new_windows",
+ META_PREF_FOCUS_NEW_WINDOWS,
+ symtab_focus_new_windows,
+ &focus_new_windows,
+ },
+ { "/apps/marco/general/focus_mode",
+ META_PREF_FOCUS_MODE,
+ symtab_focus_mode,
+ &focus_mode,
+ },
+ { "/apps/marco/general/visual_bell_type",
+ META_PREF_VISUAL_BELL_TYPE,
+ symtab_visual_bell_type,
+ &visual_bell_type,
+ },
+ { "/apps/marco/general/action_double_click_titlebar",
+ META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR,
+ symtab_titlebar_action,
+ &action_double_click_titlebar,
+ },
+ { "/apps/marco/general/action_middle_click_titlebar",
+ META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR,
+ symtab_titlebar_action,
+ &action_middle_click_titlebar,
+ },
+ { "/apps/marco/general/action_right_click_titlebar",
+ META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
+ symtab_titlebar_action,
+ &action_right_click_titlebar,
+ },
+ { NULL, 0, NULL, NULL },
+ };
+
+static MetaBoolPreference preferences_bool[] =
+ {
+ { "/apps/marco/general/raise_on_click",
+ META_PREF_RAISE_ON_CLICK,
+ &raise_on_click,
+ TRUE,
+ },
+ { "/apps/marco/general/titlebar_uses_system_font",
+ META_PREF_TITLEBAR_FONT, /* note! shares a pref */
+ &use_system_font,
+ TRUE,
+ },
+ { "/apps/marco/general/application_based",
+ META_PREF_APPLICATION_BASED,
+ NULL, /* feature is known but disabled */
+ FALSE,
+ },
+ { "/apps/marco/general/disable_workarounds",
+ META_PREF_DISABLE_WORKAROUNDS,
+ &disable_workarounds,
+ FALSE,
+ },
+ { "/apps/marco/general/auto_raise",
+ META_PREF_AUTO_RAISE,
+ &auto_raise,
+ FALSE,
+ },
+ { "/apps/marco/general/visual_bell",
+ META_PREF_VISUAL_BELL,
+ &provide_visual_bell, /* FIXME: change the name: it's confusing */
+ FALSE,
+ },
+ { "/apps/marco/general/audible_bell",
+ META_PREF_AUDIBLE_BELL,
+ &bell_is_audible, /* FIXME: change the name: it's confusing */
+ FALSE,
+ },
+ { "/apps/marco/general/reduced_resources",
+ META_PREF_REDUCED_RESOURCES,
+ &reduced_resources,
+ FALSE,
+ },
+ { "/desktop/mate/interface/accessibility",
+ META_PREF_MATE_ACCESSIBILITY,
+ &mate_accessibility,
+ FALSE,
+ },
+ { "/desktop/mate/interface/enable_animations",
+ META_PREF_MATE_ANIMATIONS,
+ &mate_animations,
+ TRUE,
+ },
+ { "/apps/marco/general/compositing_manager",
+ META_PREF_COMPOSITING_MANAGER,
+ &compositing_manager,
+ FALSE,
+ },
+ { "/apps/marco/general/resize_with_right_button",
+ META_PREF_RESIZE_WITH_RIGHT_BUTTON,
+ &resize_with_right_button,
+ FALSE,
+ },
+ { NULL, 0, NULL, FALSE },
+ };
+
+static MetaStringPreference preferences_string[] =
+ {
+ { "/apps/marco/general/mouse_button_modifier",
+ META_PREF_MOUSE_BUTTON_MODS,
+ mouse_button_mods_handler,
+ NULL,
+ },
+ { "/apps/marco/general/theme",
+ META_PREF_THEME,
+ theme_name_handler,
+ NULL,
+ },
+ { KEY_TITLEBAR_FONT,
+ META_PREF_TITLEBAR_FONT,
+ titlebar_handler,
+ NULL,
+ },
+ { KEY_TERMINAL_COMMAND,
+ META_PREF_TERMINAL_COMMAND,
+ NULL,
+ &terminal_command,
+ },
+ { "/apps/marco/general/button_layout",
+ META_PREF_BUTTON_LAYOUT,
+ button_layout_handler,
+ NULL,
+ },
+ { "/desktop/mate/peripherals/mouse/cursor_theme",
+ META_PREF_CURSOR_THEME,
+ NULL,
+ &cursor_theme,
+ },
+ { NULL, 0, NULL, NULL },
+ };
+
+static MetaIntPreference preferences_int[] =
+ {
+ { "/apps/marco/general/num_workspaces",
+ META_PREF_NUM_WORKSPACES,
+ &num_workspaces,
+ /* I would actually recommend we change the destroy value to 4
+ * and get rid of METAINTPREFERENCE_NO_CHANGE_ON_DESTROY entirely.
+ * -- tthurman
+ */
+ 1, MAX_REASONABLE_WORKSPACES, METAINTPREFERENCE_NO_CHANGE_ON_DESTROY,
+ },
+ { "/apps/marco/general/auto_raise_delay",
+ META_PREF_AUTO_RAISE_DELAY,
+ &auto_raise_delay,
+ 0, 10000, 0,
+ /* @@@ Get rid of MAX_REASONABLE_AUTO_RAISE_DELAY */
+ },
+ { "/desktop/mate/peripherals/mouse/cursor_size",
+ META_PREF_CURSOR_SIZE,
+ &cursor_size,
+ 1, 128, 24,
+ },
+ { NULL, 0, NULL, 0, 0, 0, },
+ };
+
+static void
+handle_preference_init_enum (void)
+{
+ MetaEnumPreference *cursor = preferences_enum;
+
+ while (cursor->key!=NULL)
+ {
+ char *value;
+ GError *error = NULL;
+
+ if (cursor->target==NULL)
+ {
+ ++cursor;
+ continue;
+ }
+
+ value = mateconf_client_get_string (default_client,
+ cursor->key,
+ &error);
+ cleanup_error (&error);
+
+ if (value==NULL)
+ {
+ ++cursor;
+ continue;
+ }
+
+ if (!mateconf_string_to_enum (cursor->symtab,
+ value,
+ (gint *) cursor->target))
+ meta_warning (_("MateConf key '%s' is set to an invalid value\n"),
+ cursor->key);
+
+ g_free (value);
+
+ ++cursor;
+ }
+}
+
+static void
+handle_preference_init_bool (void)
+{
+ MetaBoolPreference *cursor = preferences_bool;
+
+ while (cursor->key!=NULL)
+ {
+ if (cursor->target!=NULL)
+ get_bool (cursor->key, cursor->target);
+
+ ++cursor;
+ }
+
+ maybe_give_disable_workarounds_warning ();
+}
+
+static void
+handle_preference_init_string (void)
+{
+ MetaStringPreference *cursor = preferences_string;
+
+ while (cursor->key!=NULL)
+ {
+ char *value;
+ GError *error = NULL;
+ gboolean dummy = TRUE;
+
+ /* the string "value" will be newly allocated */
+ value = mateconf_client_get_string (default_client,
+ cursor->key,
+ &error);
+ cleanup_error (&error);
+
+ if (cursor->handler)
+ {
+ if (cursor->target)
+ meta_bug ("%s has both a target and a handler\n", cursor->key);
+
+ cursor->handler (cursor->pref, value, &dummy);
+
+ g_free (value);
+ }
+ else if (cursor->target)
+ {
+ if (*(cursor->target))
+ g_free (*(cursor->target));
+
+ *(cursor->target) = value;
+ }
+
+ ++cursor;
+ }
+}
+
+static void
+handle_preference_init_int (void)
+{
+ MetaIntPreference *cursor = preferences_int;
+
+
+ while (cursor->key!=NULL)
+ {
+ gint value;
+ GError *error = NULL;
+
+ value = mateconf_client_get_int (default_client,
+ cursor->key,
+ &error);
+ cleanup_error (&error);
+
+ if (value < cursor->minimum || value > cursor->maximum)
+ {
+ meta_warning (_("%d stored in MateConf key %s is out of range %d to %d\n"),
+ value, cursor->key, cursor->minimum, cursor->maximum);
+ /* Former behaviour for out-of-range values was:
+ * - number of workspaces was clamped;
+ * - auto raise delay was always reset to zero even if too high!;
+ * - cursor size was ignored.
+ *
+ * These seem to be meaningless variations. If they did
+ * have meaning we could have put them into MetaIntPreference.
+ * The last of these is the closest to how we behave for
+ * other types, so I think we should standardise on that.
+ */
+ }
+ else if (cursor->target)
+ *cursor->target = value;
+
+ ++cursor;
+ }
+}
+
+static gboolean
+handle_preference_update_enum (const gchar *key, MateConfValue *value)
+{
+ MetaEnumPreference *cursor = preferences_enum;
+ gint old_value;
+
+ while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
+ ++cursor;
+
+ if (cursor->key==NULL)
+ /* Didn't recognise that key. */
+ return FALSE;
+
+ /* Setting it to null (that is, removing it) always means
+ * "don't change".
+ */
+
+ if (value==NULL)
+ return TRUE;
+
+ /* Check the type. Enums are always strings. */
+
+ if (value->type != MATECONF_VALUE_STRING)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ /* But we did recognise it. */
+ return TRUE;
+ }
+
+ /* We need to know whether the value changes, so
+ * store the current value away.
+ */
+
+ old_value = * ((gint *) cursor->target);
+
+ /* Now look it up... */
+
+ if (!mateconf_string_to_enum (cursor->symtab,
+ mateconf_value_get_string (value),
+ (gint *) cursor->target))
+ {
+ /*
+ * We found it, but it was invalid. Complain.
+ *
+ * FIXME: This replicates the original behaviour, but in the future
+ * we might consider reverting invalid keys to their original values.
+ * (We know the old value, so we can look up a suitable string in
+ * the symtab.)
+ *
+ * (Empty comment follows so the translators don't see this.)
+ */
+
+ /* */
+ meta_warning (_("MateConf key '%s' is set to an invalid value\n"),
+ key);
+ return TRUE;
+ }
+
+ /* Did it change? If so, tell the listeners about it. */
+
+ if (old_value != *((gint *) cursor->target))
+ queue_changed (cursor->pref);
+
+ return TRUE;
+}
+
+static gboolean
+handle_preference_update_bool (const gchar *key, MateConfValue *value)
+{
+ MetaBoolPreference *cursor = preferences_bool;
+ gboolean old_value;
+
+ while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
+ ++cursor;
+
+ if (cursor->key==NULL)
+ /* Didn't recognise that key. */
+ return FALSE;
+
+ if (cursor->target==NULL)
+ /* No work for us to do. */
+ return TRUE;
+
+ if (value==NULL)
+ {
+ /* Value was destroyed; let's get out of here. */
+
+ if (cursor->becomes_true_on_destruction)
+ /* This preserves the behaviour of the old system, but
+ * for all I know that might have been an oversight.
+ */
+ *((gboolean *)cursor->target) = TRUE;
+
+ return TRUE;
+ }
+
+ /* Check the type. */
+
+ if (value->type != MATECONF_VALUE_BOOL)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ /* But we did recognise it. */
+ return TRUE;
+ }
+
+ /* We need to know whether the value changes, so
+ * store the current value away.
+ */
+
+ old_value = * ((gboolean *) cursor->target);
+
+ /* Now look it up... */
+
+ *((gboolean *) cursor->target) = mateconf_value_get_bool (value);
+
+ /* Did it change? If so, tell the listeners about it. */
+
+ if (old_value != *((gboolean *) cursor->target))
+ queue_changed (cursor->pref);
+
+ if (cursor->pref==META_PREF_DISABLE_WORKAROUNDS)
+ maybe_give_disable_workarounds_warning ();
+
+ return TRUE;
+}
+
+static gboolean
+handle_preference_update_string (const gchar *key, MateConfValue *value)
+{
+ MetaStringPreference *cursor = preferences_string;
+ const gchar *value_as_string;
+ gboolean inform_listeners = TRUE;
+
+ while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
+ ++cursor;
+
+ if (cursor->key==NULL)
+ /* Didn't recognise that key. */
+ return FALSE;
+
+ if (value==NULL)
+ return TRUE;
+
+ /* Check the type. */
+
+ if (value->type != MATECONF_VALUE_STRING)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ /* But we did recognise it. */
+ return TRUE;
+ }
+
+ /* Docs: "The returned string is not a copy, don't try to free it." */
+ value_as_string = mateconf_value_get_string (value);
+
+ if (cursor->handler)
+ cursor->handler (cursor->pref, value_as_string, &inform_listeners);
+ else if (cursor->target)
+ {
+ if (*(cursor->target))
+ g_free(*(cursor->target));
+
+ if (value_as_string!=NULL)
+ *(cursor->target) = g_strdup (value_as_string);
+ else
+ *(cursor->target) = NULL;
+
+ inform_listeners =
+ (value_as_string==NULL && *(cursor->target)==NULL) ||
+ (value_as_string!=NULL && *(cursor->target)!=NULL &&
+ strcmp (value_as_string, *(cursor->target))==0);
+ }
+
+ if (inform_listeners)
+ queue_changed (cursor->pref);
+
+ return TRUE;
+}
+
+static gboolean
+handle_preference_update_int (const gchar *key, MateConfValue *value)
+{
+ MetaIntPreference *cursor = preferences_int;
+ gint new_value;
+
+ while (cursor->key!=NULL && strcmp (key, cursor->key)!=0)
+ ++cursor;
+
+ if (cursor->key==NULL)
+ /* Didn't recognise that key. */
+ return FALSE;
+
+ if (cursor->target==NULL)
+ /* No work for us to do. */
+ return TRUE;
+
+ if (value==NULL)
+ {
+ /* Value was destroyed. */
+
+ if (cursor->value_if_destroyed != METAINTPREFERENCE_NO_CHANGE_ON_DESTROY)
+ *((gint *)cursor->target) = cursor->value_if_destroyed;
+
+ return TRUE;
+ }
+
+ /* Check the type. */
+
+ if (value->type != MATECONF_VALUE_INT)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ /* But we did recognise it. */
+ return TRUE;
+ }
+
+ new_value = mateconf_value_get_int (value);
+
+ if (new_value < cursor->minimum || new_value > cursor->maximum)
+ {
+ meta_warning (_("%d stored in MateConf key %s is out of range %d to %d\n"),
+ new_value, cursor->key,
+ cursor->minimum, cursor->maximum);
+ return TRUE;
+ }
+
+ /* Did it change? If so, tell the listeners about it. */
+
+ if (*cursor->target != new_value)
+ {
+ *cursor->target = new_value;
+ queue_changed (cursor->pref);
+ }
+
+ return TRUE;
+
+}
+
+
+/****************************************************************************/
+/* Listeners. */
+/****************************************************************************/
+
+void
+meta_prefs_add_listener (MetaPrefsChangedFunc func,
+ gpointer data)
+{
+ MetaPrefsListener *l;
+
+ l = g_new (MetaPrefsListener, 1);
+ l->func = func;
+ l->data = data;
+
+ listeners = g_list_prepend (listeners, l);
+}
+
+void
+meta_prefs_remove_listener (MetaPrefsChangedFunc func,
+ gpointer data)
+{
+ GList *tmp;
+
+ tmp = listeners;
+ while (tmp != NULL)
+ {
+ MetaPrefsListener *l = tmp->data;
+
+ if (l->func == func &&
+ l->data == data)
+ {
+ g_free (l);
+ listeners = g_list_delete_link (listeners, tmp);
+
+ return;
+ }
+
+ tmp = tmp->next;
+ }
+
+ meta_bug ("Did not find listener to remove\n");
+}
+
+static void
+emit_changed (MetaPreference pref)
+{
+ GList *tmp;
+ GList *copy;
+
+ meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed\n",
+ meta_preference_to_string (pref));
+
+ copy = g_list_copy (listeners);
+
+ tmp = copy;
+
+ while (tmp != NULL)
+ {
+ MetaPrefsListener *l = tmp->data;
+
+ (* l->func) (pref, l->data);
+
+ tmp = tmp->next;
+ }
+
+ g_list_free (copy);
+}
+
+static gboolean
+changed_idle_handler (gpointer data)
+{
+ GList *tmp;
+ GList *copy;
+
+ changed_idle = 0;
+
+ copy = g_list_copy (changes); /* reentrancy paranoia */
+
+ g_list_free (changes);
+ changes = NULL;
+
+ tmp = copy;
+ while (tmp != NULL)
+ {
+ MetaPreference pref = GPOINTER_TO_INT (tmp->data);
+
+ emit_changed (pref);
+
+ tmp = tmp->next;
+ }
+
+ g_list_free (copy);
+
+ return FALSE;
+}
+
+static void
+queue_changed (MetaPreference pref)
+{
+ meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s\n",
+ meta_preference_to_string (pref));
+
+ if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL)
+ changes = g_list_prepend (changes, GINT_TO_POINTER (pref));
+ else
+ meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending\n",
+ meta_preference_to_string (pref));
+
+ /* add idle at priority below the mateconf notify idle */
+ if (changed_idle == 0)
+ changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY,
+ changed_idle_handler, NULL, NULL);
+}
+
+#else /* HAVE_MATECONF */
+
+void
+meta_prefs_add_listener (MetaPrefsChangedFunc func,
+ gpointer data)
+{
+ /* Nothing, because they have mateconf turned off */
+}
+
+void
+meta_prefs_remove_listener (MetaPrefsChangedFunc func,
+ gpointer data)
+{
+ /* Nothing, because they have mateconf turned off */
+}
+
+#endif /* HAVE_MATECONF */
+
+
+/****************************************************************************/
+/* Initialisation. */
+/****************************************************************************/
+
+#ifdef HAVE_MATECONF
+/* @@@ again, use glib's ability to tell you the size of the array */
+static gchar *mateconf_dirs_we_are_interested_in[] = {
+ "/apps/marco",
+ KEY_TERMINAL_DIR,
+ KEY_MATE_ACCESSIBILITY,
+ "/desktop/mate/peripherals/mouse",
+ "/desktop/mate/interface",
+ NULL,
+};
+#endif
+
+void
+meta_prefs_init (void)
+{
+#ifdef HAVE_MATECONF
+ GError *err = NULL;
+ gchar **mateconf_dir_cursor;
+
+ if (default_client != NULL)
+ return;
+
+ /* returns a reference which we hold forever */
+ default_client = mateconf_client_get_default ();
+
+ for (mateconf_dir_cursor=mateconf_dirs_we_are_interested_in;
+ *mateconf_dir_cursor!=NULL;
+ mateconf_dir_cursor++)
+ {
+ mateconf_client_add_dir (default_client,
+ *mateconf_dir_cursor,
+ MATECONF_CLIENT_PRELOAD_RECURSIVE,
+ &err);
+ cleanup_error (&err);
+ }
+
+ /* Pick up initial values. */
+
+ handle_preference_init_enum ();
+ handle_preference_init_bool ();
+ handle_preference_init_string ();
+ handle_preference_init_int ();
+
+ /* @@@ Is there any reason we don't do the add_dir here? */
+ for (mateconf_dir_cursor=mateconf_dirs_we_are_interested_in;
+ *mateconf_dir_cursor!=NULL;
+ mateconf_dir_cursor++)
+ {
+ mateconf_client_notify_add (default_client,
+ *mateconf_dir_cursor,
+ change_notify,
+ NULL,
+ NULL,
+ &err);
+ cleanup_error (&err);
+ }
+
+#else /* HAVE_MATECONF */
+
+ /* Set defaults for some values that can't be set at initialization time of
+ * the static globals. In the case of the theme, note that there is code
+ * elsewhere that will do everything possible to fallback to an existing theme
+ * if the one here does not exist.
+ */
+ titlebar_font = pango_font_description_from_string ("Sans Bold 10");
+ current_theme = g_strdup ("ClearlooksRe");
+
+ init_button_layout();
+#endif /* HAVE_MATECONF */
+
+ init_bindings ();
+ init_commands ();
+ init_workspace_names ();
+}
+
+
+/****************************************************************************/
+/* Updates. */
+/****************************************************************************/
+
+#ifdef HAVE_MATECONF
+
+gboolean (*preference_update_handler[]) (const gchar*, MateConfValue*) = {
+ handle_preference_update_enum,
+ handle_preference_update_bool,
+ handle_preference_update_string,
+ handle_preference_update_int,
+ NULL
+};
+
+static void
+change_notify (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer user_data)
+{
+ const char *key;
+ MateConfValue *value;
+ gint i=0;
+
+ key = mateconf_entry_get_key (entry);
+ value = mateconf_entry_get_value (entry);
+
+ /* First, search for a handler that might know what to do. */
+
+ /* FIXME: When this is all working, since the first item in every
+ * array is the gchar* of the key, there's no reason we can't
+ * find the correct record for that key here and save code duplication.
+ */
+
+ while (preference_update_handler[i]!=NULL)
+ {
+ if (preference_update_handler[i] (key, value))
+ goto out; /* Get rid of this eventually */
+
+ i++;
+ }
+
+ if (g_str_has_prefix (key, KEY_WINDOW_BINDINGS_PREFIX) ||
+ g_str_has_prefix (key, KEY_SCREEN_BINDINGS_PREFIX))
+ {
+ if (g_str_has_suffix (key, KEY_LIST_BINDINGS_SUFFIX))
+ {
+ GSList *list;
+
+ if (value && value->type != MATECONF_VALUE_LIST)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ goto out;
+ }
+
+ list = value ? mateconf_value_get_list (value) : NULL;
+
+ if (update_key_list_binding (key, list))
+ queue_changed (META_PREF_KEYBINDINGS);
+ }
+ else
+ {
+ const char *str;
+
+ if (value && value->type != MATECONF_VALUE_STRING)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ goto out;
+ }
+
+ str = value ? mateconf_value_get_string (value) : NULL;
+
+ if (update_key_binding (key, str))
+ queue_changed (META_PREF_KEYBINDINGS);
+ }
+ }
+ else if (g_str_has_prefix (key, KEY_COMMAND_PREFIX))
+ {
+ const char *str;
+
+ if (value && value->type != MATECONF_VALUE_STRING)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ goto out;
+ }
+
+ str = value ? mateconf_value_get_string (value) : NULL;
+
+ if (update_command (key, str))
+ queue_changed (META_PREF_COMMANDS);
+ }
+ else if (g_str_has_prefix (key, KEY_WORKSPACE_NAME_PREFIX))
+ {
+ const char *str;
+
+ if (value && value->type != MATECONF_VALUE_STRING)
+ {
+ meta_warning (_("MateConf key \"%s\" is set to an invalid type\n"),
+ key);
+ goto out;
+ }
+
+ str = value ? mateconf_value_get_string (value) : NULL;
+
+ if (update_workspace_name (key, str))
+ queue_changed (META_PREF_WORKSPACE_NAMES);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_PREFS, "Key %s doesn't mean anything to Marco\n",
+ key);
+ }
+
+ out:
+ /* nothing */
+ return; /* AIX compiler wants something after a label like out: */
+}
+
+static void
+cleanup_error (GError **error)
+{
+ if (*error)
+ {
+ meta_warning ("%s\n", (*error)->message);
+
+ g_error_free (*error);
+ *error = NULL;
+ }
+}
+
+/* get_bool returns TRUE if *val is filled in, FALSE otherwise */
+/* @@@ probably worth moving this inline; only used once */
+static gboolean
+get_bool (const char *key, gboolean *val)
+{
+ GError *err = NULL;
+ MateConfValue *value;
+ gboolean filled_in = FALSE;
+
+ value = mateconf_client_get (default_client, key, &err);
+ cleanup_error (&err);
+ if (value)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ *val = mateconf_value_get_bool (value);
+ filled_in = TRUE;
+ }
+ mateconf_value_free (value);
+ }
+
+ return filled_in;
+}
+
+/**
+ * Special case: give a warning the first time disable_workarounds
+ * is turned on.
+ */
+static void
+maybe_give_disable_workarounds_warning (void)
+{
+ static gboolean first_disable = TRUE;
+
+ if (first_disable && disable_workarounds)
+ {
+ first_disable = FALSE;
+
+ meta_warning (_("Workarounds for broken applications disabled. "
+ "Some applications may not behave properly.\n"));
+ }
+}
+
+#endif /* HAVE_MATECONF */
+
+MetaVirtualModifier
+meta_prefs_get_mouse_button_mods (void)
+{
+ return mouse_button_mods;
+}
+
+MetaFocusMode
+meta_prefs_get_focus_mode (void)
+{
+ return focus_mode;
+}
+
+MetaFocusNewWindows
+meta_prefs_get_focus_new_windows (void)
+{
+ return focus_new_windows;
+}
+
+gboolean
+meta_prefs_get_raise_on_click (void)
+{
+ /* Force raise_on_click on for click-to-focus, as requested by Havoc
+ * in #326156.
+ */
+ return raise_on_click || focus_mode == META_FOCUS_MODE_CLICK;
+}
+
+const char*
+meta_prefs_get_theme (void)
+{
+ return current_theme;
+}
+
+const char*
+meta_prefs_get_cursor_theme (void)
+{
+ return cursor_theme;
+}
+
+int
+meta_prefs_get_cursor_size (void)
+{
+ return cursor_size;
+}
+
+
+/****************************************************************************/
+/* Handlers for string preferences. */
+/****************************************************************************/
+
+#ifdef HAVE_MATECONF
+
+static void
+titlebar_handler (MetaPreference pref,
+ const gchar *string_value,
+ gboolean *inform_listeners)
+{
+ PangoFontDescription *new_desc = NULL;
+
+ if (string_value)
+ new_desc = pango_font_description_from_string (string_value);
+
+ if (new_desc == NULL)
+ {
+ meta_warning (_("Could not parse font description "
+ "\"%s\" from MateConf key %s\n"),
+ string_value ? string_value : "(null)",
+ KEY_TITLEBAR_FONT);
+
+ *inform_listeners = FALSE;
+
+ return;
+ }
+
+ /* Is the new description the same as the old? */
+
+ if (titlebar_font &&
+ pango_font_description_equal (new_desc, titlebar_font))
+ {
+ pango_font_description_free (new_desc);
+ *inform_listeners = FALSE;
+ return;
+ }
+
+ /* No, so free the old one and put ours in instead. */
+
+ if (titlebar_font)
+ pango_font_description_free (titlebar_font);
+
+ titlebar_font = new_desc;
+
+}
+
+static void
+theme_name_handler (MetaPreference pref,
+ const gchar *string_value,
+ gboolean *inform_listeners)
+{
+ g_free (current_theme);
+
+ /* Fallback crackrock */
+ if (string_value == NULL)
+ current_theme = g_strdup ("ClearlooksRe");
+ else
+ current_theme = g_strdup (string_value);
+}
+
+static void
+mouse_button_mods_handler (MetaPreference pref,
+ const gchar *string_value,
+ gboolean *inform_listeners)
+{
+ MetaVirtualModifier mods;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Mouse button modifier has new mateconf value \"%s\"\n",
+ string_value);
+ if (string_value && meta_ui_parse_modifier (string_value, &mods))
+ {
+ mouse_button_mods = mods;
+ }
+ else
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Failed to parse new mateconf value\n");
+
+ meta_warning (_("\"%s\" found in configuration database is "
+ "not a valid value for mouse button modifier\n"),
+ string_value);
+
+ *inform_listeners = FALSE;
+ }
+}
+
+static gboolean
+button_layout_equal (const MetaButtonLayout *a,
+ const MetaButtonLayout *b)
+{
+ int i;
+
+ i = 0;
+ while (i < MAX_BUTTONS_PER_CORNER)
+ {
+ if (a->left_buttons[i] != b->left_buttons[i])
+ return FALSE;
+ if (a->right_buttons[i] != b->right_buttons[i])
+ return FALSE;
+ if (a->left_buttons_has_spacer[i] != b->left_buttons_has_spacer[i])
+ return FALSE;
+ if (a->right_buttons_has_spacer[i] != b->right_buttons_has_spacer[i])
+ return FALSE;
+ ++i;
+ }
+
+ return TRUE;
+}
+
+static MetaButtonFunction
+button_function_from_string (const char *str)
+{
+ /* FIXME: mateconf_string_to_enum is the obvious way to do this */
+
+ if (strcmp (str, "menu") == 0)
+ return META_BUTTON_FUNCTION_MENU;
+ else if (strcmp (str, "minimize") == 0)
+ return META_BUTTON_FUNCTION_MINIMIZE;
+ else if (strcmp (str, "maximize") == 0)
+ return META_BUTTON_FUNCTION_MAXIMIZE;
+ else if (strcmp (str, "close") == 0)
+ return META_BUTTON_FUNCTION_CLOSE;
+ else if (strcmp (str, "shade") == 0)
+ return META_BUTTON_FUNCTION_SHADE;
+ else if (strcmp (str, "above") == 0)
+ return META_BUTTON_FUNCTION_ABOVE;
+ else if (strcmp (str, "stick") == 0)
+ return META_BUTTON_FUNCTION_STICK;
+ else
+ /* don't know; give up */
+ return META_BUTTON_FUNCTION_LAST;
+}
+
+static MetaButtonFunction
+button_opposite_function (MetaButtonFunction ofwhat)
+{
+ switch (ofwhat)
+ {
+ case META_BUTTON_FUNCTION_SHADE:
+ return META_BUTTON_FUNCTION_UNSHADE;
+ case META_BUTTON_FUNCTION_UNSHADE:
+ return META_BUTTON_FUNCTION_SHADE;
+
+ case META_BUTTON_FUNCTION_ABOVE:
+ return META_BUTTON_FUNCTION_UNABOVE;
+ case META_BUTTON_FUNCTION_UNABOVE:
+ return META_BUTTON_FUNCTION_ABOVE;
+
+ case META_BUTTON_FUNCTION_STICK:
+ return META_BUTTON_FUNCTION_UNSTICK;
+ case META_BUTTON_FUNCTION_UNSTICK:
+ return META_BUTTON_FUNCTION_STICK;
+
+ default:
+ return META_BUTTON_FUNCTION_LAST;
+ }
+}
+
+static void
+button_layout_handler (MetaPreference pref,
+ const gchar *string_value,
+ gboolean *inform_listeners)
+{
+ MetaButtonLayout new_layout;
+ char **sides = NULL;
+ int i;
+
+ /* We need to ignore unknown button functions, for
+ * compat with future versions
+ */
+
+ if (string_value)
+ sides = g_strsplit (string_value, ":", 2);
+
+ if (sides != NULL && sides[0] != NULL)
+ {
+ char **buttons;
+ int b;
+ gboolean used[META_BUTTON_FUNCTION_LAST];
+
+ i = 0;
+ while (i < META_BUTTON_FUNCTION_LAST)
+ {
+ used[i] = FALSE;
+ new_layout.left_buttons_has_spacer[i] = FALSE;
+ ++i;
+ }
+
+ buttons = g_strsplit (sides[0], ",", -1);
+ i = 0;
+ b = 0;
+ while (buttons[b] != NULL)
+ {
+ MetaButtonFunction f = button_function_from_string (buttons[b]);
+ if (i > 0 && strcmp("spacer", buttons[b]) == 0)
+ {
+ new_layout.left_buttons_has_spacer[i-1] = TRUE;
+ f = button_opposite_function (f);
+
+ if (f != META_BUTTON_FUNCTION_LAST)
+ {
+ new_layout.left_buttons_has_spacer[i-2] = TRUE;
+ }
+ }
+ else
+ {
+ if (f != META_BUTTON_FUNCTION_LAST && !used[f])
+ {
+ new_layout.left_buttons[i] = f;
+ used[f] = TRUE;
+ ++i;
+
+ f = button_opposite_function (f);
+
+ if (f != META_BUTTON_FUNCTION_LAST)
+ new_layout.left_buttons[i++] = f;
+
+ }
+ else
+ {
+ meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n",
+ buttons[b]);
+ }
+ }
+
+ ++b;
+ }
+
+ new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
+ new_layout.left_buttons_has_spacer[i] = FALSE;
+
+ g_strfreev (buttons);
+ }
+
+ if (sides != NULL && sides[0] != NULL && sides[1] != NULL)
+ {
+ char **buttons;
+ int b;
+ gboolean used[META_BUTTON_FUNCTION_LAST];
+
+ i = 0;
+ while (i < META_BUTTON_FUNCTION_LAST)
+ {
+ used[i] = FALSE;
+ new_layout.right_buttons_has_spacer[i] = FALSE;
+ ++i;
+ }
+
+ buttons = g_strsplit (sides[1], ",", -1);
+ i = 0;
+ b = 0;
+ while (buttons[b] != NULL)
+ {
+ MetaButtonFunction f = button_function_from_string (buttons[b]);
+ if (i > 0 && strcmp("spacer", buttons[b]) == 0)
+ {
+ new_layout.right_buttons_has_spacer[i-1] = TRUE;
+ f = button_opposite_function (f);
+ if (f != META_BUTTON_FUNCTION_LAST)
+ {
+ new_layout.right_buttons_has_spacer[i-2] = TRUE;
+ }
+ }
+ else
+ {
+ if (f != META_BUTTON_FUNCTION_LAST && !used[f])
+ {
+ new_layout.right_buttons[i] = f;
+ used[f] = TRUE;
+ ++i;
+
+ f = button_opposite_function (f);
+
+ if (f != META_BUTTON_FUNCTION_LAST)
+ new_layout.right_buttons[i++] = f;
+
+ }
+ else
+ {
+ meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n",
+ buttons[b]);
+ }
+ }
+
+ ++b;
+ }
+
+ new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
+ new_layout.right_buttons_has_spacer[i] = FALSE;
+
+ g_strfreev (buttons);
+ }
+
+ g_strfreev (sides);
+
+ /* Invert the button layout for RTL languages */
+ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
+ {
+ MetaButtonLayout rtl_layout;
+ int j;
+
+ for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
+ for (j = 0; j < i; j++)
+ {
+ rtl_layout.right_buttons[j] = new_layout.left_buttons[i - j - 1];
+ if (j == 0)
+ rtl_layout.right_buttons_has_spacer[i - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
+ else
+ rtl_layout.right_buttons_has_spacer[j - 1] = new_layout.left_buttons_has_spacer[i - j - 1];
+ }
+ rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST;
+ rtl_layout.right_buttons_has_spacer[j] = FALSE;
+
+ for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++);
+ for (j = 0; j < i; j++)
+ {
+ rtl_layout.left_buttons[j] = new_layout.right_buttons[i - j - 1];
+ if (j == 0)
+ rtl_layout.left_buttons_has_spacer[i - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
+ else
+ rtl_layout.left_buttons_has_spacer[j - 1] = new_layout.right_buttons_has_spacer[i - j - 1];
+ }
+ rtl_layout.left_buttons[j] = META_BUTTON_FUNCTION_LAST;
+ rtl_layout.left_buttons_has_spacer[j] = FALSE;
+
+ new_layout = rtl_layout;
+ }
+
+ if (button_layout_equal (&button_layout, &new_layout))
+ {
+ /* Same as before, so duck out */
+ *inform_listeners = FALSE;
+ }
+ else
+ {
+ button_layout = new_layout;
+ }
+}
+
+#endif /* HAVE_MATECONF */
+
+const PangoFontDescription*
+meta_prefs_get_titlebar_font (void)
+{
+ if (use_system_font)
+ return NULL;
+ else
+ return titlebar_font;
+}
+
+int
+meta_prefs_get_num_workspaces (void)
+{
+ return num_workspaces;
+}
+
+gboolean
+meta_prefs_get_application_based (void)
+{
+ return FALSE; /* For now, we never want this to do anything */
+
+ return application_based;
+}
+
+gboolean
+meta_prefs_get_disable_workarounds (void)
+{
+ return disable_workarounds;
+}
+
+#ifdef HAVE_MATECONF
+#define MAX_REASONABLE_AUTO_RAISE_DELAY 10000
+
+#endif /* HAVE_MATECONF */
+
+#ifdef WITH_VERBOSE_MODE
+const char*
+meta_preference_to_string (MetaPreference pref)
+{
+ /* FIXME: another case for mateconf_string_to_enum */
+ switch (pref)
+ {
+ case META_PREF_MOUSE_BUTTON_MODS:
+ return "MOUSE_BUTTON_MODS";
+
+ case META_PREF_FOCUS_MODE:
+ return "FOCUS_MODE";
+
+ case META_PREF_FOCUS_NEW_WINDOWS:
+ return "FOCUS_NEW_WINDOWS";
+
+ case META_PREF_RAISE_ON_CLICK:
+ return "RAISE_ON_CLICK";
+
+ case META_PREF_THEME:
+ return "THEME";
+
+ case META_PREF_TITLEBAR_FONT:
+ return "TITLEBAR_FONT";
+
+ case META_PREF_NUM_WORKSPACES:
+ return "NUM_WORKSPACES";
+
+ case META_PREF_APPLICATION_BASED:
+ return "APPLICATION_BASED";
+
+ case META_PREF_KEYBINDINGS:
+ return "KEYBINDINGS";
+
+ case META_PREF_DISABLE_WORKAROUNDS:
+ return "DISABLE_WORKAROUNDS";
+
+ case META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR:
+ return "ACTION_DOUBLE_CLICK_TITLEBAR";
+
+ case META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR:
+ return "ACTION_MIDDLE_CLICK_TITLEBAR";
+
+ case META_PREF_ACTION_RIGHT_CLICK_TITLEBAR:
+ return "ACTION_RIGHT_CLICK_TITLEBAR";
+
+ case META_PREF_AUTO_RAISE:
+ return "AUTO_RAISE";
+
+ case META_PREF_AUTO_RAISE_DELAY:
+ return "AUTO_RAISE_DELAY";
+
+ case META_PREF_COMMANDS:
+ return "COMMANDS";
+
+ case META_PREF_TERMINAL_COMMAND:
+ return "TERMINAL_COMMAND";
+
+ case META_PREF_BUTTON_LAYOUT:
+ return "BUTTON_LAYOUT";
+
+ case META_PREF_WORKSPACE_NAMES:
+ return "WORKSPACE_NAMES";
+
+ case META_PREF_VISUAL_BELL:
+ return "VISUAL_BELL";
+
+ case META_PREF_AUDIBLE_BELL:
+ return "AUDIBLE_BELL";
+
+ case META_PREF_VISUAL_BELL_TYPE:
+ return "VISUAL_BELL_TYPE";
+
+ case META_PREF_REDUCED_RESOURCES:
+ return "REDUCED_RESOURCES";
+
+ case META_PREF_MATE_ACCESSIBILITY:
+ return "MATE_ACCESSIBILTY";
+
+ case META_PREF_MATE_ANIMATIONS:
+ return "MATE_ANIMATIONS";
+
+ case META_PREF_CURSOR_THEME:
+ return "CURSOR_THEME";
+
+ case META_PREF_CURSOR_SIZE:
+ return "CURSOR_SIZE";
+
+ case META_PREF_COMPOSITING_MANAGER:
+ return "COMPOSITING_MANAGER";
+
+ case META_PREF_RESIZE_WITH_RIGHT_BUTTON:
+ return "RESIZE_WITH_RIGHT_BUTTON";
+
+ case META_PREF_FORCE_FULLSCREEN:
+ return "FORCE_FULLSCREEN";
+ }
+
+ return "(unknown)";
+}
+#endif /* WITH_VERBOSE_MODE */
+
+void
+meta_prefs_set_num_workspaces (int n_workspaces)
+{
+#ifdef HAVE_MATECONF
+ GError *err;
+
+ if (default_client == NULL)
+ return;
+
+ if (n_workspaces < 1)
+ n_workspaces = 1;
+ if (n_workspaces > MAX_REASONABLE_WORKSPACES)
+ n_workspaces = MAX_REASONABLE_WORKSPACES;
+
+ err = NULL;
+ mateconf_client_set_int (default_client,
+ KEY_NUM_WORKSPACES,
+ n_workspaces,
+ &err);
+
+ if (err)
+ {
+ meta_warning (_("Error setting number of workspaces to %d: %s\n"),
+ num_workspaces,
+ err->message);
+ g_error_free (err);
+ }
+#endif /* HAVE_MATECONF */
+}
+
+#define keybind(name, handler, param, flags, stroke, description) \
+ { #name, NULL, !!(flags & BINDING_REVERSES), !!(flags & BINDING_PER_WINDOW) },
+static MetaKeyPref key_bindings[] = {
+#include "all-keybindings.h"
+ { NULL, NULL, FALSE }
+};
+#undef keybind
+
+#ifndef HAVE_MATECONF
+
+/**
+ * A type to map names of keybindings (such as "switch_windows")
+ * to the binding strings themselves (such as "<Alt>Tab").
+ * It exists only when MateConf is turned off in ./configure and
+ * functions as a sort of ersatz MateConf.
+ */
+typedef struct
+{
+ const char *name;
+ const char *keybinding;
+} MetaSimpleKeyMapping;
+
+/* FIXME: This would be neater if the array only contained entries whose
+ * default keystroke was non-null. You COULD do this by defining
+ * ONLY_BOUND_BY_DEFAULT around various blocks at the cost of making
+ * the bindings file way more complicated. However, we could stop this being
+ * data and move it into code. Then the compiler would optimise away
+ * the problem lines.
+ */
+
+#define keybind(name, handler, param, flags, stroke, description) \
+ { #name, stroke },
+
+static MetaSimpleKeyMapping key_string_bindings[] = {
+#include "all-keybindings.h"
+ { NULL, NULL }
+};
+#undef keybind
+
+#endif /* NOT HAVE_MATECONF */
+
+static void
+init_bindings (void)
+{
+#ifdef HAVE_MATECONF
+ const char *prefix[] = {
+ KEY_WINDOW_BINDINGS_PREFIX,
+ KEY_SCREEN_BINDINGS_PREFIX,
+ NULL
+ };
+ int i;
+ GSList *list, *l, *list_val;
+ const char *str_val;
+ const char *key;
+ MateConfEntry *entry;
+ MateConfValue *value;
+
+ for (i = 0; prefix[i]; i++)
+ {
+ list = mateconf_client_all_entries (default_client, prefix[i], NULL);
+ for (l = list; l; l = l->next)
+ {
+ entry = l->data;
+ key = mateconf_entry_get_key (entry);
+ value = mateconf_entry_get_value (entry);
+ if (g_str_has_suffix (key, KEY_LIST_BINDINGS_SUFFIX))
+ {
+ list_val = mateconf_client_get_list (default_client, key, MATECONF_VALUE_STRING, NULL);
+
+ update_key_list_binding (key, list_val);
+ g_slist_foreach (list_val, (GFunc)g_free, NULL);
+ g_slist_free (list_val);
+ }
+ else
+ {
+ str_val = mateconf_value_get_string (value);
+ update_key_binding (key, str_val);
+ }
+ mateconf_entry_free (entry);
+ }
+ g_slist_free (list);
+ }
+#else /* HAVE_MATECONF */
+ int i = 0;
+ int which = 0;
+ while (key_string_bindings[i].name)
+ {
+ if (key_string_bindings[i].keybinding == NULL) {
+ ++i;
+ continue;
+ }
+
+ while (strcmp(key_bindings[which].name,
+ key_string_bindings[i].name) != 0)
+ which++;
+
+ /* Set the binding */
+ update_binding (&key_bindings[which],
+ key_string_bindings[i].keybinding);
+
+ ++i;
+ }
+#endif /* HAVE_MATECONF */
+}
+
+static void
+init_commands (void)
+{
+#ifdef HAVE_MATECONF
+ GSList *list, *l;
+ const char *str_val;
+ const char *key;
+ MateConfEntry *entry;
+ MateConfValue *value;
+
+ list = mateconf_client_all_entries (default_client, KEY_COMMAND_DIRECTORY, NULL);
+ for (l = list; l; l = l->next)
+ {
+ entry = l->data;
+ key = mateconf_entry_get_key (entry);
+ value = mateconf_entry_get_value (entry);
+ str_val = mateconf_value_get_string (value);
+ update_command (key, str_val);
+ mateconf_entry_free (entry);
+ }
+ g_slist_free (list);
+#else
+ int i;
+ for (i = 0; i < MAX_COMMANDS; i++)
+ commands[i] = NULL;
+#endif /* HAVE_MATECONF */
+}
+
+static void
+init_workspace_names (void)
+{
+#ifdef HAVE_MATECONF
+ GSList *list, *l;
+ const char *str_val;
+ const char *key;
+ MateConfEntry *entry;
+ MateConfValue *value;
+
+ list = mateconf_client_all_entries (default_client, KEY_WORKSPACE_NAME_DIRECTORY, NULL);
+ for (l = list; l; l = l->next)
+ {
+ entry = l->data;
+ key = mateconf_entry_get_key (entry);
+ value = mateconf_entry_get_value (entry);
+ str_val = mateconf_value_get_string (value);
+ update_workspace_name (key, str_val);
+ mateconf_entry_free (entry);
+ }
+ g_slist_free (list);
+#else
+ int i;
+ for (i = 0; i < MAX_REASONABLE_WORKSPACES; i++)
+ workspace_names[i] = g_strdup_printf (_("Workspace %d"), i + 1);
+
+ meta_topic (META_DEBUG_PREFS,
+ "Initialized workspace names\n");
+#endif /* HAVE_MATECONF */
+}
+
+static gboolean
+update_binding (MetaKeyPref *binding,
+ const char *value)
+{
+ unsigned int keysym;
+ unsigned int keycode;
+ MetaVirtualModifier mods;
+ MetaKeyCombo *combo;
+ gboolean changed;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Binding \"%s\" has new mateconf value \"%s\"\n",
+ binding->name, value ? value : "none");
+
+ keysym = 0;
+ keycode = 0;
+ mods = 0;
+ if (value)
+ {
+ if (!meta_ui_parse_accelerator (value, &keysym, &keycode, &mods))
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Failed to parse new mateconf value\n");
+ meta_warning (_("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"\n"),
+ value, binding->name);
+
+ return FALSE;
+ }
+ }
+
+ /* If there isn't already a first element, make one. */
+ if (!binding->bindings)
+ {
+ MetaKeyCombo *blank = g_malloc0 (sizeof (MetaKeyCombo));
+ binding->bindings = g_slist_alloc();
+ binding->bindings->data = blank;
+ }
+
+ combo = binding->bindings->data;
+
+#ifdef HAVE_MATECONF
+ /* Bug 329676: Bindings which can be shifted must not have no modifiers,
+ * nor only SHIFT as a modifier.
+ */
+
+ if (binding->add_shift &&
+ 0 != keysym &&
+ (META_VIRTUAL_SHIFT_MASK == mods || 0 == mods))
+ {
+ gchar *old_setting;
+ gchar *key;
+ GError *err = NULL;
+
+ meta_warning ("Cannot bind \"%s\" to %s: it needs a modifier "
+ "such as Ctrl or Alt.\n",
+ binding->name,
+ value);
+
+ old_setting = meta_ui_accelerator_name (combo->keysym,
+ combo->modifiers);
+
+ if (!strcmp(old_setting, value))
+ {
+ /* We were about to set it to the same value
+ * that it had originally! This must be caused
+ * by getting an invalid string back from
+ * meta_ui_accelerator_name. Bail out now
+ * so we don't get into an infinite loop.
+ */
+ g_free (old_setting);
+ return TRUE;
+ }
+
+ meta_warning ("Reverting \"%s\" to %s.\n",
+ binding->name,
+ old_setting);
+
+ /* FIXME: add_shift is currently screen_bindings only, but
+ * there's no really good reason it should always be.
+ * So we shouldn't blindly add KEY_SCREEN_BINDINGS_PREFIX
+ * onto here.
+ */
+ key = g_strconcat (KEY_SCREEN_BINDINGS_PREFIX, "/",
+ binding->name, NULL);
+
+ mateconf_client_set_string (mateconf_client_get_default (),
+ key, old_setting, &err);
+
+ if (err)
+ {
+ meta_warning ("Error while reverting keybinding: %s\n",
+ err->message);
+ g_error_free (err);
+ err = NULL;
+ }
+
+ g_free (old_setting);
+ g_free (key);
+
+ /* The call to mateconf_client_set_string() will cause this function
+ * to be called again with the new value, so there's no need to
+ * carry on.
+ */
+ return TRUE;
+ }
+#endif
+
+ changed = FALSE;
+ if (keysym != combo->keysym ||
+ keycode != combo->keycode ||
+ mods != combo->modifiers)
+ {
+ changed = TRUE;
+
+ combo->keysym = keysym;
+ combo->keycode = keycode;
+ combo->modifiers = mods;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "New keybinding for \"%s\" is keysym = 0x%x keycode = 0x%x mods = 0x%x\n",
+ binding->name, combo->keysym, combo->keycode,
+ combo->modifiers);
+ }
+ else
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Keybinding for \"%s\" is unchanged\n", binding->name);
+ }
+
+ return changed;
+}
+
+#ifdef HAVE_MATECONF
+static gboolean
+update_list_binding (MetaKeyPref *binding,
+ GSList *value,
+ MetaStringListType type_of_value)
+{
+ unsigned int keysym;
+ unsigned int keycode;
+ MetaVirtualModifier mods;
+ gboolean changed = FALSE;
+ const gchar *pref_string;
+ GSList *pref_iterator = value, *tmp;
+ MetaKeyCombo *combo;
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Binding \"%s\" has new mateconf value\n",
+ binding->name);
+
+ if (binding->bindings == NULL)
+ {
+ /* We need to insert a dummy element into the list, because the first
+ * element is the one governed by update_binding. We only handle the
+ * subsequent elements.
+ */
+ MetaKeyCombo *blank = g_malloc0 (sizeof (MetaKeyCombo));
+ binding->bindings = g_slist_alloc();
+ binding->bindings->data = blank;
+ }
+
+ /* Okay, so, we're about to provide a new list of key combos for this
+ * action. Delete any pre-existing list.
+ */
+ tmp = binding->bindings->next;
+ while (tmp)
+ {
+ g_free (tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free (binding->bindings->next);
+ binding->bindings->next = NULL;
+
+ while (pref_iterator)
+ {
+ keysym = 0;
+ keycode = 0;
+ mods = 0;
+
+ if (!pref_iterator->data)
+ {
+ pref_iterator = pref_iterator->next;
+ continue;
+ }
+
+ switch (type_of_value)
+ {
+ case META_LIST_OF_STRINGS:
+ pref_string = pref_iterator->data;
+ break;
+ case META_LIST_OF_MATECONFVALUE_STRINGS:
+ pref_string = mateconf_value_get_string (pref_iterator->data);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (!meta_ui_parse_accelerator (pref_string, &keysym, &keycode, &mods))
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Failed to parse new mateconf value\n");
+ meta_warning (_("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"\n"),
+ pref_string, binding->name);
+
+ /* Should we remove this value from the list in mateconf? */
+ pref_iterator = pref_iterator->next;
+ continue;
+ }
+
+ /* Bug 329676: Bindings which can be shifted must not have no modifiers,
+ * nor only SHIFT as a modifier.
+ */
+
+ if (binding->add_shift &&
+ 0 != keysym &&
+ (META_VIRTUAL_SHIFT_MASK == mods || 0 == mods))
+ {
+ meta_warning ("Cannot bind \"%s\" to %s: it needs a modifier "
+ "such as Ctrl or Alt.\n",
+ binding->name,
+ pref_string);
+
+ /* Should we remove this value from the list in mateconf? */
+
+ pref_iterator = pref_iterator->next;
+ continue;
+ }
+
+ changed = TRUE;
+
+ combo = g_malloc0 (sizeof (MetaKeyCombo));
+ combo->keysym = keysym;
+ combo->keycode = keycode;
+ combo->modifiers = mods;
+ binding->bindings->next = g_slist_prepend (binding->bindings->next, combo);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "New keybinding for \"%s\" is keysym = 0x%x keycode = 0x%x mods = 0x%x\n",
+ binding->name, keysym, keycode, mods);
+
+ pref_iterator = pref_iterator->next;
+ }
+ return changed;
+}
+
+static const gchar*
+relative_key (const gchar* key)
+{
+ const gchar* end;
+
+ end = strrchr (key, '/');
+
+ ++end;
+
+ return end;
+}
+
+/* Return value is TRUE if a preference changed and we need to
+ * notify
+ */
+static gboolean
+find_and_update_binding (MetaKeyPref *bindings,
+ const char *name,
+ const char *value)
+{
+ const char *key;
+ int i;
+
+ if (*name == '/')
+ key = relative_key (name);
+ else
+ key = name;
+
+ i = 0;
+ while (bindings[i].name &&
+ strcmp (key, bindings[i].name) != 0)
+ ++i;
+
+ if (bindings[i].name)
+ return update_binding (&bindings[i], value);
+ else
+ return FALSE;
+}
+
+static gboolean
+update_key_binding (const char *name,
+ const char *value)
+{
+ return find_and_update_binding (key_bindings, name, value);
+}
+
+static gboolean
+find_and_update_list_binding (MetaKeyPref *bindings,
+ const char *name,
+ GSList *value)
+{
+ const char *key;
+ int i;
+ gchar *name_without_suffix = g_strdup(name);
+
+ name_without_suffix[strlen(name_without_suffix) - strlen(KEY_LIST_BINDINGS_SUFFIX)] = 0;
+
+ if (*name_without_suffix == '/')
+ key = relative_key (name_without_suffix);
+ else
+ key = name_without_suffix;
+
+ i = 0;
+ while (bindings[i].name &&
+ strcmp (key, bindings[i].name) != 0)
+ ++i;
+
+ g_free (name_without_suffix);
+
+ if (bindings[i].name)
+ return update_list_binding (&bindings[i], value, META_LIST_OF_MATECONFVALUE_STRINGS);
+ else
+ return FALSE;
+}
+
+static gboolean
+update_key_list_binding (const char *name,
+ GSList *value)
+{
+ return find_and_update_list_binding (key_bindings, name, value);
+}
+
+static gboolean
+update_command (const char *name,
+ const char *value)
+{
+ char *p;
+ int i;
+
+ p = strrchr (name, '_');
+ if (p == NULL)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Command %s has no underscore?\n", name);
+ return FALSE;
+ }
+
+ ++p;
+
+ if (g_ascii_isdigit (*p))
+ {
+ i = atoi (p);
+ i -= 1; /* count from 0 not 1 */
+ }
+ else
+ {
+ p = strrchr (name, '/');
+ ++p;
+
+ if (strcmp (p, "command_screenshot") == 0)
+ {
+ i = SCREENSHOT_COMMAND_IDX;
+ }
+ else if (strcmp (p, "command_window_screenshot") == 0)
+ {
+ i = WIN_SCREENSHOT_COMMAND_IDX;
+ }
+ else
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Command %s doesn't end in number?\n", name);
+ return FALSE;
+ }
+ }
+
+ if (i >= MAX_COMMANDS)
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Command %d is too highly numbered, ignoring\n", i);
+ return FALSE;
+ }
+
+ if ((commands[i] == NULL && value == NULL) ||
+ (commands[i] && value && strcmp (commands[i], value) == 0))
+ {
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Command %d is unchanged\n", i);
+ return FALSE;
+ }
+
+ g_free (commands[i]);
+ commands[i] = g_strdup (value);
+
+ meta_topic (META_DEBUG_KEYBINDINGS,
+ "Updated command %d to \"%s\"\n",
+ i, commands[i] ? commands[i] : "none");
+
+ return TRUE;
+}
+
+#endif /* HAVE_MATECONF */
+
+const char*
+meta_prefs_get_command (int i)
+{
+ g_return_val_if_fail (i >= 0 && i < MAX_COMMANDS, NULL);
+
+ return commands[i];
+}
+
+char*
+meta_prefs_get_mateconf_key_for_command (int i)
+{
+ char *key;
+
+ switch (i)
+ {
+ case SCREENSHOT_COMMAND_IDX:
+ key = g_strdup (KEY_COMMAND_PREFIX "screenshot");
+ break;
+ case WIN_SCREENSHOT_COMMAND_IDX:
+ key = g_strdup (KEY_COMMAND_PREFIX "window_screenshot");
+ break;
+ default:
+ key = g_strdup_printf (KEY_COMMAND_PREFIX"%d", i + 1);
+ break;
+ }
+
+ return key;
+}
+
+const char*
+meta_prefs_get_terminal_command (void)
+{
+ return terminal_command;
+}
+
+const char*
+meta_prefs_get_mateconf_key_for_terminal_command (void)
+{
+ return KEY_TERMINAL_COMMAND;
+}
+
+#ifdef HAVE_MATECONF
+static gboolean
+update_workspace_name (const char *name,
+ const char *value)
+{
+ char *p;
+ int i;
+
+ p = strrchr (name, '_');
+ if (p == NULL)
+ {
+ meta_topic (META_DEBUG_PREFS,
+ "Workspace name %s has no underscore?\n", name);
+ return FALSE;
+ }
+
+ ++p;
+
+ if (!g_ascii_isdigit (*p))
+ {
+ meta_topic (META_DEBUG_PREFS,
+ "Workspace name %s doesn't end in number?\n", name);
+ return FALSE;
+ }
+
+ i = atoi (p);
+ i -= 1; /* count from 0 not 1 */
+
+ if (i >= MAX_REASONABLE_WORKSPACES)
+ {
+ meta_topic (META_DEBUG_PREFS,
+ "Workspace name %d is too highly numbered, ignoring\n", i);
+ return FALSE;
+ }
+
+ if (workspace_names[i] && value && strcmp (workspace_names[i], value) == 0)
+ {
+ meta_topic (META_DEBUG_PREFS,
+ "Workspace name %d is unchanged\n", i);
+ return FALSE;
+ }
+
+ /* This is a bad hack. We have to treat empty string as
+ * "unset" because the root window property can't contain
+ * null. So it gets empty string instead and we don't want
+ * that to result in setting the empty string as a value that
+ * overrides "unset".
+ */
+ if (value != NULL && *value != '\0')
+ {
+ g_free (workspace_names[i]);
+ workspace_names[i] = g_strdup (value);
+ }
+ else
+ {
+ /* use a default name */
+ char *d;
+
+ d = g_strdup_printf (_("Workspace %d"), i + 1);
+ if (workspace_names[i] && strcmp (workspace_names[i], d) == 0)
+ {
+ g_free (d);
+ return FALSE;
+ }
+ else
+ {
+ g_free (workspace_names[i]);
+ workspace_names[i] = d;
+ }
+ }
+
+ meta_topic (META_DEBUG_PREFS,
+ "Updated workspace name %d to \"%s\"\n",
+ i, workspace_names[i] ? workspace_names[i] : "none");
+
+ return TRUE;
+}
+#endif /* HAVE_MATECONF */
+
+const char*
+meta_prefs_get_workspace_name (int i)
+{
+ g_return_val_if_fail (i >= 0 && i < MAX_REASONABLE_WORKSPACES, NULL);
+
+ g_assert (workspace_names[i] != NULL);
+
+ meta_topic (META_DEBUG_PREFS,
+ "Getting workspace name for %d: \"%s\"\n",
+ i, workspace_names[i]);
+
+ return workspace_names[i];
+}
+
+void
+meta_prefs_change_workspace_name (int i,
+ const char *name)
+{
+#ifdef HAVE_MATECONF
+ char *key;
+ GError *err;
+
+ g_return_if_fail (i >= 0 && i < MAX_REASONABLE_WORKSPACES);
+
+ meta_topic (META_DEBUG_PREFS,
+ "Changing name of workspace %d to %s\n",
+ i, name ? name : "none");
+
+ /* This is a bad hack. We have to treat empty string as
+ * "unset" because the root window property can't contain
+ * null. So it gets empty string instead and we don't want
+ * that to result in setting the empty string as a value that
+ * overrides "unset".
+ */
+ if (name && *name == '\0')
+ name = NULL;
+
+ if ((name == NULL && workspace_names[i] == NULL) ||
+ (name && workspace_names[i] && strcmp (name, workspace_names[i]) == 0))
+ {
+ meta_topic (META_DEBUG_PREFS,
+ "Workspace %d already has name %s\n",
+ i, name ? name : "none");
+ return;
+ }
+
+ key = mateconf_key_for_workspace_name (i);
+
+ err = NULL;
+ if (name != NULL)
+ mateconf_client_set_string (default_client,
+ key, name,
+ &err);
+ else
+ mateconf_client_unset (default_client,
+ key, &err);
+
+
+ if (err)
+ {
+ meta_warning (_("Error setting name for workspace %d to \"%s\": %s\n"),
+ i, name ? name : "none",
+ err->message);
+ g_error_free (err);
+ }
+
+ g_free (key);
+#else
+ g_free (workspace_names[i]);
+ workspace_names[i] = g_strdup (name);
+#endif /* HAVE_MATECONF */
+}
+
+#ifdef HAVE_MATECONF
+static char*
+mateconf_key_for_workspace_name (int i)
+{
+ char *key;
+
+ key = g_strdup_printf (KEY_WORKSPACE_NAME_PREFIX"%d", i + 1);
+
+ return key;
+}
+#endif /* HAVE_MATECONF */
+
+void
+meta_prefs_get_button_layout (MetaButtonLayout *button_layout_p)
+{
+ *button_layout_p = button_layout;
+}
+
+gboolean
+meta_prefs_get_visual_bell (void)
+{
+ return provide_visual_bell;
+}
+
+gboolean
+meta_prefs_bell_is_audible (void)
+{
+ return bell_is_audible;
+}
+
+MetaVisualBellType
+meta_prefs_get_visual_bell_type (void)
+{
+ return visual_bell_type;
+}
+
+void
+meta_prefs_get_key_bindings (const MetaKeyPref **bindings,
+ int *n_bindings)
+{
+
+ *bindings = key_bindings;
+ *n_bindings = (int) G_N_ELEMENTS (key_bindings) - 1;
+}
+
+MetaActionTitlebar
+meta_prefs_get_action_double_click_titlebar (void)
+{
+ return action_double_click_titlebar;
+}
+
+MetaActionTitlebar
+meta_prefs_get_action_middle_click_titlebar (void)
+{
+ return action_middle_click_titlebar;
+}
+
+MetaActionTitlebar
+meta_prefs_get_action_right_click_titlebar (void)
+{
+ return action_right_click_titlebar;
+}
+
+gboolean
+meta_prefs_get_auto_raise (void)
+{
+ return auto_raise;
+}
+
+int
+meta_prefs_get_auto_raise_delay (void)
+{
+ return auto_raise_delay;
+}
+
+gboolean
+meta_prefs_get_reduced_resources (void)
+{
+ return reduced_resources;
+}
+
+gboolean
+meta_prefs_get_mate_accessibility ()
+{
+ return mate_accessibility;
+}
+
+gboolean
+meta_prefs_get_mate_animations ()
+{
+ return mate_animations;
+}
+
+MetaKeyBindingAction
+meta_prefs_get_keybinding_action (const char *name)
+{
+ int i;
+
+ i = G_N_ELEMENTS (key_bindings) - 2; /* -2 for dummy entry at end */
+ while (i >= 0)
+ {
+ if (strcmp (key_bindings[i].name, name) == 0)
+ return (MetaKeyBindingAction) i;
+
+ --i;
+ }
+
+ return META_KEYBINDING_ACTION_NONE;
+}
+
+/* This is used by the menu system to decide what key binding
+ * to display next to an option. We return the first non-disabled
+ * binding, if any.
+ */
+void
+meta_prefs_get_window_binding (const char *name,
+ unsigned int *keysym,
+ MetaVirtualModifier *modifiers)
+{
+ int i;
+
+ i = G_N_ELEMENTS (key_bindings) - 2; /* -2 for dummy entry at end */
+ while (i >= 0)
+ {
+ if (key_bindings[i].per_window &&
+ strcmp (key_bindings[i].name, name) == 0)
+ {
+ GSList *s = key_bindings[i].bindings;
+
+ while (s)
+ {
+ MetaKeyCombo *c = s->data;
+
+ if (c->keysym!=0 || c->modifiers!=0)
+ {
+ *keysym = c->keysym;
+ *modifiers = c->modifiers;
+ return;
+ }
+
+ s = s->next;
+ }
+
+ /* Not found; return the disabled value */
+ *keysym = *modifiers = 0;
+ return;
+ }
+
+ --i;
+ }
+
+ g_assert_not_reached ();
+}
+
+gboolean
+meta_prefs_get_compositing_manager (void)
+{
+ return compositing_manager;
+}
+
+guint
+meta_prefs_get_mouse_button_resize (void)
+{
+ return resize_with_right_button ? 3: 2;
+}
+
+guint
+meta_prefs_get_mouse_button_menu (void)
+{
+ return resize_with_right_button ? 2: 3;
+}
+
+gboolean
+meta_prefs_get_force_fullscreen (void)
+{
+ return force_fullscreen;
+}
+
+void
+meta_prefs_set_compositing_manager (gboolean whether)
+{
+#ifdef HAVE_MATECONF
+ GError *err = NULL;
+
+ mateconf_client_set_bool (default_client,
+ KEY_COMPOSITOR,
+ whether,
+ &err);
+
+ if (err)
+ {
+ meta_warning (_("Error setting compositor status: %s\n"),
+ err->message);
+ g_error_free (err);
+ }
+#else
+ compositing_manager = whether;
+#endif
+}
+
+#ifndef HAVE_MATECONF
+static void
+init_button_layout(void)
+{
+ MetaButtonLayout button_layout_ltr = {
+ {
+ /* buttons in the group on the left side */
+ META_BUTTON_FUNCTION_MENU,
+ META_BUTTON_FUNCTION_LAST
+ },
+ {
+ /* buttons in the group on the right side */
+ META_BUTTON_FUNCTION_MINIMIZE,
+ META_BUTTON_FUNCTION_MAXIMIZE,
+ META_BUTTON_FUNCTION_CLOSE,
+ META_BUTTON_FUNCTION_LAST
+ }
+ };
+ MetaButtonLayout button_layout_rtl = {
+ {
+ /* buttons in the group on the left side */
+ META_BUTTON_FUNCTION_CLOSE,
+ META_BUTTON_FUNCTION_MAXIMIZE,
+ META_BUTTON_FUNCTION_MINIMIZE,
+ META_BUTTON_FUNCTION_LAST
+ },
+ {
+ /* buttons in the group on the right side */
+ META_BUTTON_FUNCTION_MENU,
+ META_BUTTON_FUNCTION_LAST
+ }
+ };
+
+ button_layout = meta_ui_get_direction() == META_UI_DIRECTION_LTR ?
+ button_layout_ltr : button_layout_rtl;
+};
+
+#endif
+
+void
+meta_prefs_set_force_fullscreen (gboolean whether)
+{
+ force_fullscreen = whether;
+}
+