prefs.c 44.2 KB
Newer Older
1 2
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

3 4
/* Metacity preferences */

5
/*
6
 * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc.
7
 * Copyright (C) 2006 Elijah Newren
Thomas Thurman's avatar
Thomas Thurman committed
8
 * Copyright (C) 2008 Thomas Thurman
9 10
 * Copyright (C) 2010 Milan Bouchet-Valat, Copyright (C) 2011 Red Hat Inc.
 *
11 12 13 14 15 16 17 18 19
 * 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.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
23 24 25 26
 */

#include <config.h>
#include "prefs.h"
27
#include "ui.h"
28
#include "util.h"
29 30
#include <glib.h>
#include <gio/gio.h>
31
#include <string.h>
32
#include <stdlib.h>
33
#include "keybindings.h"
34

35
/* If you add a key, it needs updating in init() and in the gsettings
36 37 38 39 40
 * 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.
41
 */
42 43 44 45
#define KEY_TITLEBAR_FONT "titlebar-font"
#define KEY_NUM_WORKSPACES "num-workspaces"
#define KEY_WORKSPACE_NAMES "workspace-names"
#define KEY_COMPOSITOR "compositing-manager"
46
#define KEY_PLACEMENT_MODE "placement-mode"
47 48 49 50 51 52 53 54 55 56 57

/* Keys from "foreign" schemas */
#define KEY_GNOME_ACCESSIBILITY "toolkit-accessibility"
#define KEY_GNOME_ANIMATIONS "enable-animations"
#define KEY_GNOME_CURSOR_THEME "cursor-theme"
#define KEY_GNOME_CURSOR_SIZE "cursor-size"

/* These are the different schemas we are keeping
 * a GSettings instance for */
#define SCHEMA_GENERAL         "org.gnome.desktop.wm.preferences"
#define SCHEMA_METACITY        "org.gnome.metacity"
58
#define SCHEMA_METACITY_THEME  "org.gnome.metacity.theme"
59 60 61
#define SCHEMA_INTERFACE       "org.gnome.desktop.interface"

#define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
62

63 64
static GList *changes = NULL;
static guint changed_idle;
65
static GList *listeners = NULL;
66
static GHashTable *settings_schemas;
67

68
static gboolean use_system_font = FALSE;
69
static PangoFontDescription *titlebar_font = NULL;
70
static MetaVirtualModifier mouse_button_mods = Mod1Mask;
71 72
static GDesktopFocusMode focus_mode = G_DESKTOP_FOCUS_MODE_CLICK;
static GDesktopFocusNewWindows focus_new_windows = G_DESKTOP_FOCUS_NEW_WINDOWS_SMART;
73
static gboolean raise_on_click = TRUE;
74
static gboolean attach_modal_dialogs = FALSE;
75 76
static gchar *current_theme_name = NULL;
static MetaThemeType current_theme_type = META_THEME_TYPE_GTK;
77
static int num_workspaces = 4;
78 79 80
static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE;
static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER;
static GDesktopTitlebarAction action_right_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_MENU;
81
static gboolean disable_workarounds = FALSE;
Havoc Pennington's avatar
Havoc Pennington committed
82 83
static gboolean auto_raise = FALSE;
static gboolean auto_raise_delay = 500;
84
static gboolean bell_is_visible = FALSE;
85
static gboolean bell_is_audible = TRUE;
86
static gboolean reduced_resources = FALSE;
87
static gboolean gnome_accessibility = FALSE;
88
static gboolean gnome_animations = TRUE;
89 90
static char *cursor_theme = NULL;
static int   cursor_size = 24;
91
static gboolean compositing_manager = TRUE;
92
static gboolean resize_with_right_button = FALSE;
Alberts Muktupāvels's avatar
Alberts Muktupāvels committed
93
static gboolean edge_tiling = FALSE;
94
static gboolean force_fullscreen = TRUE;
95
static gboolean alt_tab_thumbnails = FALSE;
96

97
static GDesktopVisualBellType visual_bell_type = G_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH;
98
static MetaButtonLayout button_layout;
99

100 101
static MetaPlacementMode placement_mode = META_PLACEMENT_MODE_SMART;

102 103
/* NULL-terminated array */
static char **workspace_names = NULL;
104

105 106
static void handle_preference_update_enum (GSettings *settings,
                                           gchar     *key);
107

108 109 110 111 112
static gboolean update_binding         (MetaKeyPref *binding,
                                        gchar      **strokes);
static gboolean update_key_binding     (const char  *key,
                                        gchar      **strokes);
static gboolean update_workspace_names (void);
113

114 115 116 117 118 119
static void settings_changed (GSettings      *settings,
                              gchar          *key,
                              gpointer        data);
static void bindings_changed (GSettings      *settings,
                              gchar          *key,
                              gpointer        data);
120 121


122 123
static void queue_changed (MetaPreference  pref);

124 125
static void maybe_give_disable_workarounds_warning (void);

126 127 128 129
static gboolean titlebar_handler (GVariant*, gpointer*, gpointer);
static gboolean theme_name_handler (GVariant*, gpointer*, gpointer);
static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
130

131 132
static void     init_bindings             (void);
static void     init_workspace_names      (void);
133

134
static void update_button_layout (const gchar *string_value);
135

136 137 138 139 140 141
typedef struct
{
  MetaPrefsChangedFunc func;
  gpointer data;
} MetaPrefsListener;

142 143
typedef struct
{
144 145
  const gchar *key;
  const gchar *schema;
Thomas Thurman's avatar
Thomas Thurman committed
146
  MetaPreference pref;
147 148 149 150 151
} MetaBasePreference;

typedef struct
{
  MetaBasePreference base;
152 153 154
  gpointer target;
} MetaEnumPreference;

155 156
typedef struct
{
157
  MetaBasePreference base;
Thomas Thurman's avatar
Thomas Thurman committed
158
  gboolean *target;
159 160 161
  gboolean becomes_true_on_destruction;
} MetaBoolPreference;

162 163
typedef struct
{
164
  MetaBasePreference base;
165 166 167 168 169

  /**
   * 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
170 171
   * function pointer here, it will be called instead of writing
   * the string value out to the target variable.
172
   *
173 174 175 176 177 178
   * The function will be passed to g_settings_get_mapped() and should
   * return %TRUE if the mapping was successful and %FALSE otherwise.
   * In the former case the function is expected to handle the result
   * of the conversion itself and call queue_changed() appropriately;
   * in particular the @result (out) parameter as returned by
   * g_settings_get_mapped() will be ignored in all cases.
179 180 181
   *
   * This may be NULL.  If it is, see "target", below.
   */
182
  GSettingsGetMapping handler;
183 184 185 186

  /**
   * Where to write the incoming string.
   *
Thomas Thurman's avatar
Thomas Thurman committed
187
   * This must be NULL if the handler is non-NULL.
188 189 190 191 192 193
   * If the incoming string is NULL, no change will be made.
   */
  gchar **target;

} MetaStringPreference;

Thomas Thurman's avatar
Thomas Thurman committed
194 195
typedef struct
{
196
  MetaBasePreference base;
Thomas Thurman's avatar
Thomas Thurman committed
197 198 199
  gint *target;
} MetaIntPreference;

200 201 202 203 204

/* All preferences that are not keybindings must be listed here,
 * plus in the GSettings schemas and the MetaPreference enum.
 */

Thomas Thurman's avatar
Thomas Thurman committed
205 206
/* FIXMEs: */
/* @@@ Don't use NULL lines at the end; glib can tell you how big it is */
207 208
static MetaEnumPreference preferences_enum[] =
  {
209 210 211 212 213
    {
      { "focus-new-windows",
        SCHEMA_GENERAL,
        META_PREF_FOCUS_NEW_WINDOWS,
      },
214 215
      &focus_new_windows,
    },
216 217 218 219 220
    {
      { "focus-mode",
        SCHEMA_GENERAL,
        META_PREF_FOCUS_MODE,
      },
221 222
      &focus_mode,
    },
223 224 225 226 227
    {
      { "visual-bell-type",
        SCHEMA_GENERAL,
        META_PREF_VISUAL_BELL_TYPE,
      },
228 229
      &visual_bell_type,
    },
230 231 232 233 234
    {
      { "action-double-click-titlebar",
        SCHEMA_GENERAL,
        META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR,
      },
235 236
      &action_double_click_titlebar,
    },
237 238 239 240 241
    {
      { "action-middle-click-titlebar",
        SCHEMA_GENERAL,
        META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR,
      },
242 243
      &action_middle_click_titlebar,
    },
244 245 246 247 248
    {
      { "action-right-click-titlebar",
        SCHEMA_GENERAL,
        META_PREF_ACTION_RIGHT_CLICK_TITLEBAR,
      },
249 250
      &action_right_click_titlebar,
    },
251 252 253 254 255 256 257
    {
      { "placement-mode",
        SCHEMA_METACITY,
        META_PREF_PLACEMENT_MODE,
      },
      &placement_mode,
    },
258 259 260 261 262 263 264
    {
      { "type",
        SCHEMA_METACITY_THEME,
        META_PREF_THEME_TYPE,
      },
      &current_theme_type,
    },
265
    { { NULL, 0, 0 }, NULL },
266 267
  };

268 269
static MetaBoolPreference preferences_bool[] =
  {
270 271 272 273 274
    {
      { "raise-on-click",
        SCHEMA_GENERAL,
        META_PREF_RAISE_ON_CLICK,
      },
Thomas Thurman's avatar
Thomas Thurman committed
275
      &raise_on_click,
276 277
      TRUE,
    },
278 279 280 281 282
    {
      { "titlebar-uses-system-font",
        SCHEMA_GENERAL,
        META_PREF_TITLEBAR_FONT, /* note! shares a pref */
      },
Thomas Thurman's avatar
Thomas Thurman committed
283
      &use_system_font,
284 285
      TRUE,
    },
286 287 288 289 290
    {
      { "disable-workarounds",
        SCHEMA_GENERAL,
        META_PREF_DISABLE_WORKAROUNDS,
      },
Thomas Thurman's avatar
Thomas Thurman committed
291
      &disable_workarounds,
292 293
      FALSE,
    },
294 295 296 297 298
    {
      { "auto-raise",
        SCHEMA_GENERAL,
        META_PREF_AUTO_RAISE,
      },
Thomas Thurman's avatar
Thomas Thurman committed
299
      &auto_raise,
300 301
      FALSE,
    },
302 303 304 305 306 307 308 309
    {
      { "visual-bell",
        SCHEMA_GENERAL,
        META_PREF_VISUAL_BELL,
      },
      &bell_is_visible, /* FIXME: change the name: it's confusing */
      FALSE,
    },
310 311 312 313 314
    {
      { "audible-bell",
        SCHEMA_GENERAL,
        META_PREF_AUDIBLE_BELL,
      },
Thomas Thurman's avatar
Thomas Thurman committed
315
      &bell_is_audible, /* FIXME: change the name: it's confusing */
316 317
      FALSE,
    },
318 319 320 321 322
    {
      { "reduced-resources",
        SCHEMA_METACITY,
        META_PREF_REDUCED_RESOURCES,
      },
Thomas Thurman's avatar
Thomas Thurman committed
323
      &reduced_resources,
324 325
      FALSE,
    },
326 327 328 329 330
    {
      { KEY_GNOME_ACCESSIBILITY,
        SCHEMA_INTERFACE,
        META_PREF_GNOME_ACCESSIBILITY,
      },
Thomas Thurman's avatar
Thomas Thurman committed
331
      &gnome_accessibility,
332 333
      FALSE,
    },
334 335 336 337 338
    {
      { KEY_GNOME_ANIMATIONS,
        SCHEMA_INTERFACE,
        META_PREF_GNOME_ANIMATIONS,
      },
339 340 341
      &gnome_animations,
      TRUE,
    },
342 343 344 345 346
    {
      { "compositing-manager",
        SCHEMA_METACITY,
        META_PREF_COMPOSITING_MANAGER,
      },
Thomas Thurman's avatar
Thomas Thurman committed
347
      &compositing_manager,
348 349
      FALSE,
    },
350 351 352 353 354
    {
      { "resize-with-right-button",
        SCHEMA_GENERAL,
        META_PREF_RESIZE_WITH_RIGHT_BUTTON,
      },
355 356 357
      &resize_with_right_button,
      FALSE,
    },
Alberts Muktupāvels's avatar
Alberts Muktupāvels committed
358 359 360 361 362 363 364 365
    {
      { "edge-tiling",
        SCHEMA_METACITY,
        META_PREF_EDGE_TILING,
      },
      &edge_tiling,
      FALSE,
    },
366 367 368 369 370 371 372 373
    {
      { "alt-tab-thumbnails",
        SCHEMA_METACITY,
        META_PREF_ALT_TAB_THUMBNAILS,
      },
      &alt_tab_thumbnails,
      FALSE,
    },
374
    { { NULL, 0, 0 }, NULL, FALSE },
375
  };
376

377 378
static MetaStringPreference preferences_string[] =
  {
379 380 381 382 383
    {
      { "mouse-button-modifier",
        SCHEMA_GENERAL,
        META_PREF_MOUSE_BUTTON_MODS,
      },
384 385 386
      mouse_button_mods_handler,
      NULL,
    },
387 388 389 390 391
    {
      { KEY_TITLEBAR_FONT,
        SCHEMA_GENERAL,
        META_PREF_TITLEBAR_FONT,
      },
392 393 394
      titlebar_handler,
      NULL,
    },
395 396 397 398 399
    {
      { "button-layout",
        SCHEMA_GENERAL,
        META_PREF_BUTTON_LAYOUT,
      },
400 401 402
      button_layout_handler,
      NULL,
    },
403 404 405 406 407
    {
      { KEY_GNOME_CURSOR_THEME,
        SCHEMA_INTERFACE,
        META_PREF_CURSOR_THEME,
      },
408 409 410
      NULL,
      &cursor_theme,
    },
411 412 413 414 415 416 417 418
    {
      { "name",
        SCHEMA_METACITY_THEME,
        META_PREF_THEME_NAME,
      },
      theme_name_handler,
      NULL,
    },
419
    { { NULL, 0, 0 }, NULL },
420 421
  };

Thomas Thurman's avatar
Thomas Thurman committed
422 423
static MetaIntPreference preferences_int[] =
  {
424 425 426 427 428 429
    {
      { "num-workspaces",
        SCHEMA_GENERAL,
        META_PREF_NUM_WORKSPACES,
      },
      &num_workspaces
Thomas Thurman's avatar
Thomas Thurman committed
430
    },
431 432 433 434 435 436
    {
      { "auto-raise-delay",
        SCHEMA_GENERAL,
        META_PREF_AUTO_RAISE_DELAY,
      },
      &auto_raise_delay
Thomas Thurman's avatar
Thomas Thurman committed
437
    },
438 439 440 441 442 443
    {
      { KEY_GNOME_CURSOR_SIZE,
        SCHEMA_INTERFACE,
        META_PREF_CURSOR_SIZE,
      },
      &cursor_size
Thomas Thurman's avatar
Thomas Thurman committed
444
    },
445
    { { NULL, 0, 0 }, NULL },
Thomas Thurman's avatar
Thomas Thurman committed
446 447
  };

448 449 450 451 452
static void
handle_preference_init_enum (void)
{
  MetaEnumPreference *cursor = preferences_enum;

453
  while (cursor->base.key != NULL)
454
    {
455 456
      if (cursor->target==NULL)
          continue;
457

458 459
      *((gint *) cursor->target) =
        g_settings_get_enum (SETTINGS (cursor->base.schema), cursor->base.key);
460 461 462 463 464

      ++cursor;
    }
}

465 466 467 468 469
static void
handle_preference_init_bool (void)
{
  MetaBoolPreference *cursor = preferences_bool;

470
  while (cursor->base.key != NULL)
471 472
    {
      if (cursor->target!=NULL)
473 474 475
        *cursor->target =
          g_settings_get_boolean (SETTINGS (cursor->base.schema),
                                  cursor->base.key);
476 477 478 479 480 481 482

      ++cursor;
    }

  maybe_give_disable_workarounds_warning ();
}

483 484 485 486 487
static void
handle_preference_init_string (void)
{
  MetaStringPreference *cursor = preferences_string;

488
  while (cursor->base.key != NULL)
489 490 491
    {
      char *value;

492
      /* Complex keys have a mapping function to check validity */
493 494 495
      if (cursor->handler)
        {
          if (cursor->target)
496
            meta_bug ("%s has both a target and a handler\n", cursor->base.key);
497

498 499
          g_settings_get_mapped (SETTINGS (cursor->base.schema),
                                 cursor->base.key, cursor->handler, NULL);
500
        }
501
      else
502
        {
503 504 505
          if (!cursor->target)
            meta_bug ("%s must have handler or target\n", cursor->base.key);

506 507 508
          if (*(cursor->target))
            g_free (*(cursor->target));

509 510 511
          value = g_settings_get_string (SETTINGS (cursor->base.schema),
                                         cursor->base.key);

512 513 514 515 516 517 518
          *(cursor->target) = value;
        }

      ++cursor;
    }
}

Thomas Thurman's avatar
Thomas Thurman committed
519 520 521 522 523
static void
handle_preference_init_int (void)
{
  MetaIntPreference *cursor = preferences_int;

524

525
  while (cursor->base.key != NULL)
Thomas Thurman's avatar
Thomas Thurman committed
526
    {
527 528 529
      if (cursor->target)
        *cursor->target = g_settings_get_int (SETTINGS (cursor->base.schema),
                                              cursor->base.key);
Thomas Thurman's avatar
Thomas Thurman committed
530 531 532 533 534

      ++cursor;
    }
}

535 536 537
static void
handle_preference_update_enum (GSettings *settings,
                               gchar *key)
538 539 540 541
{
  MetaEnumPreference *cursor = preferences_enum;
  gint old_value;

542
  while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
543 544
    ++cursor;

545
  if (cursor->base.key==NULL)
546
    /* Didn't recognise that key. */
547
    return;
548 549

  /* We need to know whether the value changes, so
550
   * store the current value away. */
551
  old_value = * ((gint *) cursor->target);
552

553 554
  *((gint *) cursor->target) =
    g_settings_get_enum (SETTINGS (cursor->base.schema), key);
555 556 557

  /* Did it change?  If so, tell the listeners about it. */
  if (old_value != *((gint *) cursor->target))
558
    queue_changed (cursor->base.pref);
559 560
}

561 562 563
static void
handle_preference_update_bool (GSettings *settings,
                               gchar *key)
564 565 566 567
{
  MetaBoolPreference *cursor = preferences_bool;
  gboolean old_value;

568
  while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
569 570
    ++cursor;

571 572 573
  if (cursor->base.key==NULL || cursor->target==NULL)
    /* Unknown key or no work for us to do. */
    return;
574 575

  /* We need to know whether the value changes, so
576 577
   * store the current value away. */
  old_value = *((gboolean *) cursor->target);
578

579 580
  *((gboolean *) cursor->target) =
    g_settings_get_boolean (SETTINGS (cursor->base.schema), key);
581 582 583

  /* Did it change?  If so, tell the listeners about it. */
  if (old_value != *((gboolean *) cursor->target))
584
    queue_changed (cursor->base.pref);
585

586
  if (cursor->base.pref==META_PREF_DISABLE_WORKAROUNDS)
587 588 589
    maybe_give_disable_workarounds_warning ();
}

590 591 592
static void
handle_preference_update_string (GSettings *settings,
                                 gchar *key)
593 594
{
  MetaStringPreference *cursor = preferences_string;
595 596
  char *value;
  gboolean inform_listeners = FALSE;
597

598
  while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
599 600
    ++cursor;

601
  if (cursor->base.key==NULL)
602
    /* Didn't recognise that key. */
603
    return;
604

605 606
  /* Complex keys have a mapping function to check validity */
  if (cursor->handler)
607
    {
608 609 610 611 612
      if (cursor->target)
        meta_bug ("%s has both a target and a handler\n", cursor->base.key);

      g_settings_get_mapped (SETTINGS (cursor->base.schema),
                             cursor->base.key, cursor->handler, NULL);
613
    }
614 615 616 617
  else
    {
      if (!cursor->target)
        meta_bug ("%s must have handler or target\n", cursor->base.key);
618

619 620 621
      value = g_settings_get_string (SETTINGS (cursor->base.schema),
                                     cursor->base.key);
      inform_listeners = (g_strcmp0 (value, *(cursor->target)) != 0);
622 623

      if (*(cursor->target))
624
        g_free (*(cursor->target));
625

626
      *(cursor->target) = value;
627 628 629
    }

  if (inform_listeners)
630
    queue_changed (cursor->base.pref);
631 632
}

633 634 635
static void
handle_preference_update_int (GSettings *settings,
                              gchar *key)
Thomas Thurman's avatar
Thomas Thurman committed
636 637 638 639
{
  MetaIntPreference *cursor = preferences_int;
  gint new_value;

640
  while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
Thomas Thurman's avatar
Thomas Thurman committed
641 642
    ++cursor;

643 644 645
  if (cursor->base.key==NULL || cursor->target==NULL)
    /* Unknown key or no work for us to do. */
    return;
Thomas Thurman's avatar
Thomas Thurman committed
646

647
  new_value = g_settings_get_int (SETTINGS (cursor->base.schema), key);
Thomas Thurman's avatar
Thomas Thurman committed
648 649 650 651 652

  /* Did it change?  If so, tell the listeners about it. */
  if (*cursor->target != new_value)
    {
      *cursor->target = new_value;
653
      queue_changed (cursor->base.pref);
Thomas Thurman's avatar
Thomas Thurman committed
654 655 656 657 658 659 660
    }
}

/****************************************************************************/
/* Listeners.                                                               */
/****************************************************************************/

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
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;
        }
693

694 695 696 697 698 699 700 701 702 703 704 705
      tmp = tmp->next;
    }

  meta_bug ("Did not find listener to remove\n");
}

static void
emit_changed (MetaPreference pref)
{
  GList *tmp;
  GList *copy;

706 707
  meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed\n",
              meta_preference_to_string (pref));
708

709
  copy = g_list_copy (listeners);
710

711
  tmp = copy;
712

713 714 715 716 717
  while (tmp != NULL)
    {
      MetaPrefsListener *l = tmp->data;

      (* l->func) (pref, l->data);
718

719 720 721 722 723 724 725 726 727 728 729 730 731
      tmp = tmp->next;
    }

  g_list_free (copy);
}

static gboolean
changed_idle_handler (gpointer data)
{
  GList *tmp;
  GList *copy;

  changed_idle = 0;
732

733 734 735 736
  copy = g_list_copy (changes); /* reentrancy paranoia */

  g_list_free (changes);
  changes = NULL;
737

738 739 740 741 742 743
  tmp = copy;
  while (tmp != NULL)
    {
      MetaPreference pref = GPOINTER_TO_INT (tmp->data);

      emit_changed (pref);
744

745 746 747 748
      tmp = tmp->next;
    }

  g_list_free (copy);
749

750 751 752 753 754 755
  return FALSE;
}

static void
queue_changed (MetaPreference pref)
{
756
  meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s\n",
757
              meta_preference_to_string (pref));
758

759 760 761
  if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL)
    changes = g_list_prepend (changes, GINT_TO_POINTER (pref));
  else
762 763
    meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending\n",
                meta_preference_to_string (pref));
764

765
  if (changed_idle == 0)
766
    changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY,
767
                                    changed_idle_handler, NULL, NULL);
768
}
769

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
static gboolean
in_desktop (const gchar *name)
{
  const gchar *xdg_current_desktop;
  gboolean result;
  gchar **desktops;
  gint i;

  xdg_current_desktop = g_getenv ("XDG_CURRENT_DESKTOP");
  if (xdg_current_desktop == NULL)
    return FALSE;

  result = FALSE;
  desktops = g_strsplit (xdg_current_desktop, ":", -1);

  for (i = 0; desktops[i] != NULL; i++)
    {
      if (g_strcmp0 (desktops[i], name) == 0)
        {
          result = TRUE;
          break;
        }
    }

  g_strfreev (desktops);

  return result;
}

799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
static void
gtk_decoration_layout_changed (GtkSettings *settings,
                               GParamSpec  *pspec,
                               gpointer     user_data)
{
  gchar *layout;
  gchar **sides;
  gint i;
  gint j;

  g_object_get (settings, "gtk-decoration-layout", &layout, NULL);

  sides = g_strsplit (layout, ":", -1);
  g_free (layout);

  for (i = 0; sides[i]; i++)
    {
      gchar **buttons;

      buttons = g_strsplit (sides[i], ",", -1);

      for (j = 0; buttons[j]; j++)
        {
          const gchar *button;

          if (g_strcmp0 (buttons[j], "icon") == 0)
            button = "menu";
          else if (g_strcmp0 (buttons[j], "menu") == 0)
            button = "appmenu";
          else
            button = NULL;

          if (button)
            {
              g_free (buttons[j]);
              buttons[j] = g_strdup (button);
            }
        }

      g_free (sides[i]);
      sides[i] = g_strjoinv (",", buttons);

      g_strfreev (buttons);
    }

  layout = g_strjoinv (":", sides);
  g_strfreev (sides);

  update_button_layout (layout);
  g_free (layout);
}

851

852 853 854 855 856
static void
init_gtk_decoration_layout (void)
{
  GtkSettings *settings;

857
  if (!in_desktop ("GNOME-Flashback"))
858 859
    return;

860 861 862 863 864 865 866 867
  settings = gtk_settings_get_default ();

  g_signal_connect (settings, "notify::gtk-decoration-layout",
                    G_CALLBACK (gtk_decoration_layout_changed), NULL);

  gtk_decoration_layout_changed (settings, NULL, NULL);
}

868 869 870 871 872
static void
gtk_theme_name_changed (GtkSettings *settings,
                        GParamSpec  *pspec,
                        gpointer     user_data)
{
873 874
  if (current_theme_type == META_THEME_TYPE_GTK)
    queue_changed (META_PREF_THEME_NAME);
875 876 877 878 879 880 881 882 883 884 885 886 887
}

static void
init_gtk_theme_name (void)
{
  GtkSettings *settings;

  settings = gtk_settings_get_default ();

  g_signal_connect (settings, "notify::gtk-theme-name",
                    G_CALLBACK (gtk_theme_name_changed), NULL);
}

Thomas Thurman's avatar
Thomas Thurman committed
888 889 890
/****************************************************************************/
/* Initialisation.                                                          */
/****************************************************************************/
891

892 893 894
void
meta_prefs_init (void)
{
895 896 897
  GSettings *settings;

  settings_schemas = g_hash_table_new_full (g_str_hash, g_str_equal,
898
                                            g_free, g_object_unref);
899 900 901

  settings = g_settings_new (SCHEMA_GENERAL);
  g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
902
  g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_GENERAL), settings);
903 904 905

  settings = g_settings_new (SCHEMA_METACITY);
  g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
906
  g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_METACITY), settings);
907

908 909 910 911
  settings = g_settings_new (SCHEMA_METACITY_THEME);
  g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL);
  g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_METACITY_THEME), settings);

912 913 914 915 916 917 918 919 920 921
  /* Individual keys we watch outside of our schemas */
  settings = g_settings_new (SCHEMA_INTERFACE);
  g_signal_connect (settings, "changed::" KEY_GNOME_ACCESSIBILITY,
                    G_CALLBACK (settings_changed), NULL);
  g_signal_connect (settings, "changed::" KEY_GNOME_ANIMATIONS,
                    G_CALLBACK (settings_changed), NULL);
  g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_THEME,
                    G_CALLBACK (settings_changed), NULL);
  g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_SIZE,
                    G_CALLBACK (settings_changed), NULL);
922
  g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings);
923

924
  /* Pick up initial values. */
925
  handle_preference_init_enum ();
926
  handle_preference_init_bool ();
927
  handle_preference_init_string ();
Thomas Thurman's avatar
Thomas Thurman committed
928
  handle_preference_init_int ();
929

930
  init_bindings ();
931
  init_workspace_names ();
932 933

  init_gtk_decoration_layout ();
934
  init_gtk_theme_name ();
935 936
}

Thomas Thurman's avatar
Thomas Thurman committed
937 938 939 940
/****************************************************************************/
/* Updates.                                                                 */
/****************************************************************************/

941
static void
942 943 944 945 946 947 948 949
settings_changed (GSettings *settings,
                  gchar *key,
                  gpointer data)
{
  GVariant *value;
  const GVariantType *type;
  MetaEnumPreference *cursor;
  gboolean found_enum;
950

951 952
  /* String array, handled separately */
  if (strcmp (key, KEY_WORKSPACE_NAMES) == 0)
953
    {
954
      if (update_workspace_names ())
955
        queue_changed (META_PREF_WORKSPACE_NAMES);
956

957
      return;
958
    }
959

960 961
  value = g_settings_get_value (settings, key);
  type = g_variant_get_type (value);
962

963 964 965 966 967
  if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN))
    handle_preference_update_bool (settings, key);
  else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
    handle_preference_update_int (settings, key);
  else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
968
    {
969 970
      cursor = preferences_enum;
      found_enum = FALSE;
971

972
      while (cursor->base.key != NULL)
973
        {
974 975
          if (strcmp (key, cursor->base.key) == 0)
            found_enum = TRUE;
976

977
          cursor++;
978 979
        }

980 981 982 983
      if (found_enum)
        handle_preference_update_enum (settings, key);
      else
        handle_preference_update_string (settings, key);
984
    }
985
  else
986 987 988 989
    /* Someone added a preference of an unhandled type */
    g_assert_not_reached ();

  g_variant_unref (value);
990
}
991 992

static void
993 994 995
bindings_changed (GSettings *settings,
                  gchar *key,
                  gpointer data)
996
{
997
  gchar **strokes;
998

999
  strokes = g_settings_get_strv (settings, key);
1000