[gnome-control-center] keyboard: Add common XKB options to the Typing shortcuts section



commit 978ab40f3eb6e4ec2a632941cd8f879919aa2e02
Author: Rui Matos <tiagomatos gmail com>
Date:   Tue Jul 31 18:27:17 2012 +0200

    keyboard: Add common XKB options to the Typing shortcuts section
    
    Both the compose key and the 3rd level chooser are common and useful
    enough to expose in the control center.
    
    Since these shortcuts are a small pre-defined set of only modifier
    keys we present them in combo cell renderers.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=682069

 configure.ac                         |    8 +-
 panels/keyboard/Makefile.am          |    2 +
 panels/keyboard/cc-keyboard-option.c |  449 ++++++++++++++++++++++++++++++++++
 panels/keyboard/cc-keyboard-option.h |   48 ++++
 panels/keyboard/keyboard-shortcuts.c |  291 ++++++++++++++++++-----
 5 files changed, 736 insertions(+), 62 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 80b70b9..0463769 100644
--- a/configure.ac
+++ b/configure.ac
@@ -103,8 +103,8 @@ POLKIT_REQUIRED_VERSION=0.103
 GSD_REQUIRED_VERSION=3.5.2
 NETWORK_MANAGER_REQUIRED_VERSION=0.8.992
 LIBNOTIFY_REQUIRED_VERSION=0.7.3
-GNOME_DESKTOP_REQUIRED_VERSION=3.5.3
-SCHEMAS_REQUIRED_VERSION=3.5.3
+GNOME_DESKTOP_REQUIRED_VERSION=3.5.6
+SCHEMAS_REQUIRED_VERSION=3.5.5
 LIBWACOM_REQUIRED_VERSION=0.5
 CLUTTER_REQUIRED_VERSION=1.11.3
 GOA_REQUIRED_VERSION=3.5.90
@@ -130,7 +130,9 @@ PKG_CHECK_MODULES(DATETIME_PANEL, $COMMON_MODULES
 PKG_CHECK_MODULES(DISPLAY_PANEL, $COMMON_MODULES gnome-desktop-3.0 >= 3.1.0)
 PKG_CHECK_MODULES(INFO_PANEL, $COMMON_MODULES libgtop-2.0
 		  polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION)
-PKG_CHECK_MODULES(KEYBOARD_PANEL, $COMMON_MODULES x11)
+PKG_CHECK_MODULES(KEYBOARD_PANEL, $COMMON_MODULES
+                  gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED_VERSION
+                  x11)
 PKG_CHECK_MODULES(MEDIA_PANEL, $COMMON_MODULES)
 PKG_CHECK_MODULES(MOUSE_PANEL, $COMMON_MODULES xi >= 1.2
                   gnome-settings-daemon >= $GSD_REQUIRED_VERSION x11)
diff --git a/panels/keyboard/Makefile.am b/panels/keyboard/Makefile.am
index 9623403..e3d7fc6 100644
--- a/panels/keyboard/Makefile.am
+++ b/panels/keyboard/Makefile.am
@@ -10,6 +10,8 @@ libkeyboard_la_SOURCES =   \
 	cc-keyboard-panel.h		\
 	cc-keyboard-item.c		\
 	cc-keyboard-item.h		\
+	cc-keyboard-option.c		\
+	cc-keyboard-option.h		\
 	wm-common.c			\
 	wm-common.h			\
 	keyboard-general.c		\
diff --git a/panels/keyboard/cc-keyboard-option.c b/panels/keyboard/cc-keyboard-option.c
new file mode 100644
index 0000000..229ceae
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-option.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <glib/gi18n.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-xkb-info.h>
+
+#include "cc-keyboard-option.h"
+
+#define CC_TYPE_KEYBOARD_OPTION            (cc_keyboard_option_get_type ())
+#define CC_KEYBOARD_OPTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_KEYBOARD_OPTION, CcKeyboardOption))
+#define CC_KEYBOARD_OPTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  CC_TYPE_KEYBOARD_OPTION, CcKeyboardOptionClass))
+#define CC_IS_KEYBOARD_OPTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_KEYBOARD_OPTION))
+#define CC_IS_KEYBOARD_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  CC_TYPE_KEYBOARD_OPTION))
+#define CC_KEYBOARD_OPTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  CC_TYPE_KEYBOARD_OPTION, CcKeyboardOptionClass))
+
+#define INPUT_SOURCES_SCHEMA "org.gnome.desktop.input-sources"
+#define XKB_OPTIONS_KEY "xkb-options"
+
+#define XKB_OPTION_GROUP_LVL3 "lv3"
+#define XKB_OPTION_GROUP_COMP "Compose key"
+
+enum
+{
+  PROP_0,
+  PROP_GROUP,
+  PROP_DESCRIPTION
+};
+
+enum
+{
+  CHANGED_SIGNAL,
+  LAST_SIGNAL
+};
+
+struct _CcKeyboardOption
+{
+  GObject parent_object;
+
+  gchar *group;
+  gchar *description;
+  gchar *current_value;
+  GtkListStore *store;
+
+  const gchar * const *whitelist;
+};
+
+typedef struct _CcKeyboardOptionClass CcKeyboardOptionClass;
+struct _CcKeyboardOptionClass
+{
+  GObjectClass parent_class;
+};
+
+static guint keyboard_option_signals[LAST_SIGNAL] = { 0 };
+
+static GnomeXkbInfo *xkb_info = NULL;
+static GSettings *input_sources_settings = NULL;
+static gchar **current_xkb_options = NULL;
+
+static const gchar *xkb_option_lvl3_whitelist[] = {
+  "lv3:switch",
+  "lv3:menu_switch",
+  "lv3:rwin_switch",
+  "lv3:lalt_switch",
+  "lv3:ralt_switch",
+  "lv3:caps_switch",
+  NULL
+};
+
+static const gchar *xkb_option_comp_whitelist[] = {
+  "compose:ralt",
+  "compose:rwin",
+  "compose:menu",
+  "compose:lctrl",
+  "compose:rctrl",
+  "compose:caps",
+  NULL
+};
+
+static GList *objects_list = NULL;
+
+GType cc_keyboard_option_get_type (void);
+
+G_DEFINE_TYPE (CcKeyboardOption, cc_keyboard_option, G_TYPE_OBJECT);
+
+static gboolean
+strv_contains (const gchar * const *strv,
+               const gchar         *str)
+{
+  const gchar * const *p = strv;
+  for (p = strv; *p; p++)
+    if (g_strcmp0 (*p, str) == 0)
+      return TRUE;
+
+  return FALSE;
+}
+
+static void
+reload_setting (CcKeyboardOption *self)
+{
+  gchar **iter;
+
+  for (iter = current_xkb_options; *iter; ++iter)
+    if (strv_contains (self->whitelist, *iter))
+      {
+        if (g_strcmp0 (self->current_value, *iter) != 0)
+          {
+            g_free (self->current_value);
+            self->current_value = g_strdup (*iter);
+            g_signal_emit (self, keyboard_option_signals[CHANGED_SIGNAL], 0);
+          }
+        break;
+      }
+
+  if (*iter == NULL && self->current_value != NULL)
+    {
+      g_clear_pointer (&self->current_value, g_free);
+      g_signal_emit (self, keyboard_option_signals[CHANGED_SIGNAL], 0);
+    }
+}
+
+static void
+xkb_options_changed (GSettings *settings,
+                     gchar     *key,
+                     gpointer   data)
+{
+  GList *l;
+
+  g_strfreev (current_xkb_options);
+  current_xkb_options = g_settings_get_strv (settings, key);
+
+  for (l = objects_list; l; l = l->next)
+    reload_setting (CC_KEYBOARD_OPTION (l->data));
+}
+
+static void
+cc_keyboard_option_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  CcKeyboardOption *self;
+
+  self = CC_KEYBOARD_OPTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_GROUP:
+      g_value_set_string (value, self->group);
+      break;
+    case PROP_DESCRIPTION:
+      g_value_set_string (value, self->description);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+cc_keyboard_option_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  CcKeyboardOption *self;
+
+  self = CC_KEYBOARD_OPTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_GROUP:
+      self->group = g_value_dup_string (value);
+      break;
+    case PROP_DESCRIPTION:
+      self->description = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+cc_keyboard_option_init (CcKeyboardOption *self)
+{
+}
+
+static void
+cc_keyboard_option_finalize (GObject *object)
+{
+  CcKeyboardOption *self = CC_KEYBOARD_OPTION (object);
+
+  g_clear_pointer (&self->group, g_free);
+  g_clear_pointer (&self->description, g_free);
+  g_clear_pointer (&self->current_value, g_free);
+  g_clear_object (&self->store);
+
+  G_OBJECT_CLASS (cc_keyboard_option_parent_class)->finalize (object);
+}
+
+static void
+cc_keyboard_option_constructed (GObject *object)
+{
+  GtkTreeIter iter;
+  GList *options, *l;
+  gchar *option_id;
+  CcKeyboardOption *self = CC_KEYBOARD_OPTION (object);
+
+  G_OBJECT_CLASS (cc_keyboard_option_parent_class)->constructed (object);
+
+  if (g_str_equal (self->group, XKB_OPTION_GROUP_LVL3))
+    self->whitelist = xkb_option_lvl3_whitelist;
+  else if (g_str_equal (self->group, XKB_OPTION_GROUP_COMP))
+    self->whitelist = xkb_option_comp_whitelist;
+  else
+    g_assert_not_reached ();
+
+  self->store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+  gtk_list_store_append (self->store, &iter);
+  gtk_list_store_set (self->store, &iter,
+                      XKB_OPTION_DESCRIPTION_COLUMN, _("Disabled"),
+                      XKB_OPTION_ID_COLUMN, NULL,
+                      -1);
+  options = gnome_xkb_info_get_options_for_group (xkb_info, self->group);
+  for (l = options; l; l = l->next)
+    {
+      option_id = l->data;
+      if (strv_contains (self->whitelist, option_id))
+        {
+          gtk_list_store_append (self->store, &iter);
+          gtk_list_store_set (self->store, &iter,
+                              XKB_OPTION_DESCRIPTION_COLUMN,
+                              gnome_xkb_info_description_for_option (xkb_info, self->group, option_id),
+                              XKB_OPTION_ID_COLUMN,
+                              option_id,
+                              -1);
+        }
+    }
+  g_list_free (options);
+
+  reload_setting (self);
+}
+
+static void
+cc_keyboard_option_class_init (CcKeyboardOptionClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->get_property = cc_keyboard_option_get_property;
+  gobject_class->set_property = cc_keyboard_option_set_property;
+  gobject_class->finalize = cc_keyboard_option_finalize;
+  gobject_class->constructed = cc_keyboard_option_constructed;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_GROUP,
+                                   g_param_spec_string ("group",
+                                                        "group",
+                                                        "xkb option group identifier",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+  g_object_class_install_property (gobject_class,
+                                   PROP_DESCRIPTION,
+                                   g_param_spec_string ("description",
+                                                        "description",
+                                                        "translated option description",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+  keyboard_option_signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                                          CC_TYPE_KEYBOARD_OPTION,
+                                                          G_SIGNAL_RUN_LAST,
+                                                          0, NULL, NULL, NULL,
+                                                          G_TYPE_NONE,
+                                                          0);
+}
+
+GList *
+cc_keyboard_option_get_all (void)
+{
+  if (objects_list)
+    return objects_list;
+
+  xkb_info = gnome_xkb_info_new ();
+
+  input_sources_settings = g_settings_new (INPUT_SOURCES_SCHEMA);
+
+  g_signal_connect (input_sources_settings, "changed::" XKB_OPTIONS_KEY,
+                    G_CALLBACK (xkb_options_changed), NULL);
+
+  xkb_options_changed (input_sources_settings, XKB_OPTIONS_KEY, NULL);
+
+  objects_list = g_list_prepend (objects_list,
+                                 g_object_new (CC_TYPE_KEYBOARD_OPTION,
+                                               "group", XKB_OPTION_GROUP_LVL3,
+                                               "description", _("Alternative Characters Key"),
+                                               NULL));
+  objects_list = g_list_prepend (objects_list,
+                                 g_object_new (CC_TYPE_KEYBOARD_OPTION,
+                                               "group", XKB_OPTION_GROUP_COMP,
+                                               "description", _("Compose Key"),
+                                               NULL));
+  return objects_list;
+}
+
+const gchar *
+cc_keyboard_option_get_description (CcKeyboardOption *self)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_OPTION (self), NULL);
+
+  return self->description;
+}
+
+GtkListStore *
+cc_keyboard_option_get_store (CcKeyboardOption *self)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_OPTION (self), NULL);
+
+  return self->store;
+}
+
+const gchar *
+cc_keyboard_option_get_current_value_description (CcKeyboardOption *self)
+{
+  g_return_val_if_fail (CC_IS_KEYBOARD_OPTION (self), NULL);
+
+  if (!self->current_value)
+    return _("Disabled");
+
+  return gnome_xkb_info_description_for_option (xkb_info, self->group, self->current_value);
+}
+
+static void
+remove_value (const gchar *value)
+{
+  gchar **p;
+
+  for (p = current_xkb_options; *p; ++p)
+    if (g_str_equal (*p, value))
+      {
+        g_free (*p);
+        break;
+      }
+
+  for (++p; *p; ++p)
+    *(p - 1) = *p;
+
+  *(p - 1) = NULL;
+}
+
+static void
+add_value (const gchar *value)
+{
+  gchar **new_xkb_options;
+  gchar **a, **b;
+
+  new_xkb_options = g_new0 (gchar *, g_strv_length (current_xkb_options) + 2);
+
+  a = new_xkb_options;
+  for (b = current_xkb_options; *b; ++a, ++b)
+    *a = g_strdup (*b);
+
+  *a = g_strdup (value);
+
+  g_strfreev (current_xkb_options);
+  current_xkb_options = new_xkb_options;
+}
+
+static void
+replace_value (const gchar *old,
+               const gchar *new)
+{
+  gchar **iter;
+
+  if (g_str_equal (old, new))
+    return;
+
+  for (iter = current_xkb_options; *iter; ++iter)
+    if (g_str_equal (*iter, old))
+      {
+        g_free (*iter);
+        *iter = g_strdup (new);
+        break;
+      }
+}
+
+void
+cc_keyboard_option_set_selection (CcKeyboardOption *self,
+                                  GtkTreeIter      *iter)
+{
+  gchar *new_value = NULL;
+
+  g_return_if_fail (CC_IS_KEYBOARD_OPTION (self));
+
+  gtk_tree_model_get (GTK_TREE_MODEL (self->store), iter,
+                      XKB_OPTION_ID_COLUMN, &new_value,
+                      -1);
+
+  if (!new_value)
+    {
+      if (self->current_value)
+        remove_value (self->current_value);
+    }
+  else
+    {
+      if (self->current_value)
+        replace_value (self->current_value, new_value);
+      else
+        add_value (new_value);
+    }
+
+  g_settings_set_strv (input_sources_settings, XKB_OPTIONS_KEY,
+                       (const gchar * const *) current_xkb_options);
+
+  g_free (new_value);
+}
+
+void
+cc_keyboard_option_clear_all (void)
+{
+  GList *l;
+
+  for (l = objects_list; l; l = l->next)
+    g_object_unref (l->data);
+
+  g_clear_pointer (&objects_list, g_list_free);
+  g_clear_pointer (&current_xkb_options, g_strfreev);
+  g_clear_object (&input_sources_settings);
+  g_clear_object (&xkb_info);
+}
diff --git a/panels/keyboard/cc-keyboard-option.h b/panels/keyboard/cc-keyboard-option.h
new file mode 100644
index 0000000..011481f
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-option.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __CC_KEYBOARD_OPTION_H__
+#define __CC_KEYBOARD_OPTION_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+enum
+{
+  XKB_OPTION_DESCRIPTION_COLUMN,
+  XKB_OPTION_ID_COLUMN,
+  XKB_OPTION_N_COLUMNS
+};
+
+typedef struct _CcKeyboardOption CcKeyboardOption;
+
+GList *         cc_keyboard_option_get_all              (void);
+const gchar *   cc_keyboard_option_get_description      (CcKeyboardOption *self);
+GtkListStore *  cc_keyboard_option_get_store            (CcKeyboardOption *self);
+const gchar *   cc_keyboard_option_get_current_value_description (CcKeyboardOption *self);
+void            cc_keyboard_option_set_selection        (CcKeyboardOption *self,
+                                                         GtkTreeIter      *iter);
+void            cc_keyboard_option_clear_all            (void);
+
+G_END_DECLS
+
+#endif  /* __CC_KEYBOARD_OPTION_H__ */
diff --git a/panels/keyboard/keyboard-shortcuts.c b/panels/keyboard/keyboard-shortcuts.c
index 093c713..8013670 100644
--- a/panels/keyboard/keyboard-shortcuts.c
+++ b/panels/keyboard/keyboard-shortcuts.c
@@ -22,8 +22,10 @@
 #include <config.h>
 
 #include <glib/gi18n.h>
+
 #include "keyboard-shortcuts.h"
 #include "cc-keyboard-item.h"
+#include "cc-keyboard-option.h"
 #include "wm-common.h"
 
 #define BINDINGS_SCHEMA "org.gnome.settings-daemon.plugins.media-keys"
@@ -53,10 +55,17 @@ typedef struct
   char *name; /* GSettings schema path, or GSettings key name depending on type */
 } KeyListEntry;
 
+typedef enum
+{
+  SHORTCUT_TYPE_KEY_ENTRY,
+  SHORTCUT_TYPE_XKB_OPTION,
+} ShortcutType;
+
 enum
 {
   DETAIL_DESCRIPTION_COLUMN,
   DETAIL_KEYENTRY_COLUMN,
+  DETAIL_TYPE_COLUMN,
   DETAIL_N_COLUMNS
 };
 
@@ -668,34 +677,53 @@ accel_set_func (GtkTreeViewColumn *tree_column,
                 GtkTreeIter       *iter,
                 gpointer           data)
 {
-  CcKeyboardItem *item;
+  gpointer entry;
+  ShortcutType type;
 
   gtk_tree_model_get (model, iter,
-                      DETAIL_KEYENTRY_COLUMN, &item,
+                      DETAIL_KEYENTRY_COLUMN, &entry,
+                      DETAIL_TYPE_COLUMN, &type,
                       -1);
 
-  if (item == NULL)
-    g_object_set (cell,
-                  "visible", FALSE,
-                  NULL);
-  else if (! item->editable)
-    g_object_set (cell,
-                  "visible", TRUE,
-                  "editable", FALSE,
-                  "accel-key", item->keyval,
-                  "accel-mods", item->mask,
-                  "keycode", item->keycode,
-                  "style", PANGO_STYLE_ITALIC,
-                  NULL);
-  else
-    g_object_set (cell,
-                  "visible", TRUE,
-                  "editable", TRUE,
-                  "accel-key", item->keyval,
-                  "accel-mods", item->mask,
-                  "keycode", item->keycode,
-                  "style", PANGO_STYLE_NORMAL,
-                  NULL);
+  gtk_cell_renderer_set_visible (cell, FALSE);
+
+  if (type == SHORTCUT_TYPE_XKB_OPTION &&
+      GTK_IS_CELL_RENDERER_COMBO (cell))
+    {
+      CcKeyboardOption *option = entry;
+
+      gtk_cell_renderer_set_visible (cell, TRUE);
+      g_object_set (cell,
+                    "model", cc_keyboard_option_get_store (option),
+                    "text", cc_keyboard_option_get_current_value_description (option),
+                    NULL);
+    }
+  else if (type == SHORTCUT_TYPE_KEY_ENTRY &&
+           GTK_IS_CELL_RENDERER_TEXT (cell) &&
+           !GTK_IS_CELL_RENDERER_COMBO (cell) &&
+           entry != NULL)
+    {
+      CcKeyboardItem *item = entry;
+
+      gtk_cell_renderer_set_visible (cell, TRUE);
+
+      if (item->editable)
+        g_object_set (cell,
+                      "editable", TRUE,
+                      "accel-key", item->keyval,
+                      "accel-mods", item->mask,
+                      "keycode", item->keycode,
+                      "style", PANGO_STYLE_NORMAL,
+                      NULL);
+      else
+        g_object_set (cell,
+                      "editable", FALSE,
+                      "accel-key", item->keyval,
+                      "accel-mods", item->mask,
+                      "keycode", item->keycode,
+                      "style", PANGO_STYLE_ITALIC,
+                      NULL);
+    }
 }
 
 static void
@@ -705,21 +733,34 @@ description_set_func (GtkTreeViewColumn *tree_column,
                       GtkTreeIter       *iter,
                       gpointer           data)
 {
+  gchar *description;
   CcKeyboardItem *item;
+  ShortcutType type;
 
   gtk_tree_model_get (model, iter,
+                      DETAIL_DESCRIPTION_COLUMN, &description,
                       DETAIL_KEYENTRY_COLUMN, &item,
+                      DETAIL_TYPE_COLUMN, &type,
                       -1);
 
-  if (item != NULL)
-    g_object_set (cell,
-                  "editable", FALSE,
-                  "text", item->description != NULL ?
-                          item->description : _("<Unknown Action>"),
-                  NULL);
+  if (type == SHORTCUT_TYPE_XKB_OPTION)
+    {
+      g_object_set (cell, "text", description, NULL);
+    }
   else
-    g_object_set (cell,
-                  "editable", FALSE, NULL);
+    {
+      if (item != NULL)
+        g_object_set (cell,
+                      "editable", FALSE,
+                      "text", item->description != NULL ?
+                      item->description : _("<Unknown Action>"),
+                      NULL);
+      else
+        g_object_set (cell,
+                      "editable", FALSE, NULL);
+    }
+
+  g_free (description);
 }
 
 static void
@@ -730,12 +771,17 @@ shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
   GtkTreeIter iter;
   CcKeyboardItem *item;
   gboolean can_remove;
+  ShortcutType type;
 
   can_remove = FALSE;
   if (gtk_tree_selection_get_selected (selection, &model, &iter))
     {
-      gtk_tree_model_get (model, &iter, DETAIL_KEYENTRY_COLUMN, &item, -1);
-      if (item && item->command != NULL && item->editable)
+      gtk_tree_model_get (model, &iter,
+                          DETAIL_KEYENTRY_COLUMN, &item,
+                          DETAIL_TYPE_COLUMN, &type,
+                          -1);
+      if (type == SHORTCUT_TYPE_KEY_ENTRY &&
+          item && item->command != NULL && item->editable)
         can_remove = TRUE;
     }
 
@@ -743,6 +789,25 @@ shortcut_selection_changed (GtkTreeSelection *selection, gpointer data)
 }
 
 static void
+fill_xkb_options_shortcuts (GtkTreeModel *model)
+{
+  GList *l;
+  GtkTreeIter iter;
+
+  for (l = cc_keyboard_option_get_all (); l; l = l->next)
+    {
+      CcKeyboardOption *option = l->data;
+
+      gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+      gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                          DETAIL_DESCRIPTION_COLUMN, cc_keyboard_option_get_description (option),
+                          DETAIL_KEYENTRY_COLUMN, option,
+                          DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_XKB_OPTION,
+                          -1);
+    }
+}
+
+static void
 section_selection_changed (GtkTreeSelection *selection, gpointer data)
 {
   GtkTreeIter iter;
@@ -769,7 +834,6 @@ section_selection_changed (GtkTreeSelection *selection, gpointer data)
           g_free (description);
           return;
         }
-      g_free (description);
 
       gtk_widget_set_sensitive (WID (builder, "remove-toolbutton"), FALSE);
 
@@ -787,8 +851,14 @@ section_selection_changed (GtkTreeSelection *selection, gpointer data)
           gtk_list_store_set (GTK_LIST_STORE (shortcut_model), &new_row,
                               DETAIL_DESCRIPTION_COLUMN, item->description,
                               DETAIL_KEYENTRY_COLUMN, item,
+                              DETAIL_TYPE_COLUMN, SHORTCUT_TYPE_KEY_ENTRY,
                               -1);
         }
+
+      if (g_str_equal (description, _("Typing")))
+        fill_xkb_options_shortcuts (shortcut_model);
+
+      g_free (description);
     }
 }
 
@@ -907,6 +977,7 @@ start_editing_cb (GtkTreeView    *tree_view,
 {
   GtkTreePath *path;
   GtkTreeViewColumn *column;
+  GtkCellRenderer *cell = user_data;
 
   if (event->window != gtk_tree_view_get_bin_window (tree_view))
     return FALSE;
@@ -920,32 +991,41 @@ start_editing_cb (GtkTreeView    *tree_view,
       GtkTreeModel *model;
       GtkTreeIter iter;
       CcKeyboardItem *item;
+      ShortcutType type;
 
       model = gtk_tree_view_get_model (tree_view);
       gtk_tree_model_get_iter (model, &iter, path);
       gtk_tree_model_get (model, &iter,
                           DETAIL_KEYENTRY_COLUMN, &item,
+                          DETAIL_TYPE_COLUMN, &type,
                          -1);
 
+      if (type == SHORTCUT_TYPE_XKB_OPTION)
+        {
+          gtk_tree_path_free (path);
+          return FALSE;
+        }
+
       /* if only the accel can be edited on the selected row
        * always select the accel column */
       if (item->desc_editable &&
           column == gtk_tree_view_get_column (tree_view, 0))
         {
           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
-          gtk_tree_view_set_cursor (tree_view, path,
-                                    gtk_tree_view_get_column (tree_view, 0),
+          gtk_tree_view_set_cursor (tree_view,
+                                    path,
+                                    column,
                                     FALSE);
           update_custom_shortcut (model, &iter);
         }
       else
         {
           gtk_widget_grab_focus (GTK_WIDGET (tree_view));
-          gtk_tree_view_set_cursor (tree_view,
-                                    path,
-                                    item->desc_editable ? column :
-                                    gtk_tree_view_get_column (tree_view, 1),
-                                    TRUE);
+          gtk_tree_view_set_cursor_on_cell (tree_view,
+                                            path,
+                                            gtk_tree_view_get_column (tree_view, 1),
+                                            cell,
+                                            TRUE);
         }
       g_signal_stop_emission_by_name (tree_view, "button_press_event");
       gtk_tree_path_free (path);
@@ -954,39 +1034,47 @@ start_editing_cb (GtkTreeView    *tree_view,
 }
 
 static void
-start_editing_kb_cb (GtkTreeView *treeview,
-                          GtkTreePath *path,
-                          GtkTreeViewColumn *column,
-                          gpointer user_data)
+start_editing_kb_cb (GtkTreeView       *treeview,
+                     GtkTreePath       *path,
+                     GtkTreeViewColumn *column,
+                     gpointer           user_data)
 {
   GtkTreeModel *model;
   GtkTreeIter iter;
   CcKeyboardItem *item;
+  ShortcutType type;
+  GtkCellRenderer *cell = user_data;
 
   model = gtk_tree_view_get_model (treeview);
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_model_get (model, &iter,
                       DETAIL_KEYENTRY_COLUMN, &item,
+                      DETAIL_TYPE_COLUMN, &type,
                       -1);
 
+  if (type == SHORTCUT_TYPE_XKB_OPTION)
+    return;
+
   /* if only the accel can be edited on the selected row
    * always select the accel column */
   if (item->desc_editable &&
       column == gtk_tree_view_get_column (treeview, 0))
     {
       gtk_widget_grab_focus (GTK_WIDGET (treeview));
-      gtk_tree_view_set_cursor (treeview, path,
-                                gtk_tree_view_get_column (treeview, 0),
+      gtk_tree_view_set_cursor (treeview,
+                                path,
+                                column,
                                 FALSE);
       update_custom_shortcut (model, &iter);
     }
   else
     {
        gtk_widget_grab_focus (GTK_WIDGET (treeview));
-       gtk_tree_view_set_cursor (treeview,
-                                 path,
-                                 gtk_tree_view_get_column (treeview, 1),
-                                 TRUE);
+       gtk_tree_view_set_cursor_on_cell (treeview,
+                                         path,
+                                         gtk_tree_view_get_column (treeview, 1),
+                                         cell,
+                                         TRUE);
     }
 }
 
@@ -1466,6 +1554,74 @@ sections_separator_func (GtkTreeModel *model,
 }
 
 static void
+xkb_options_combo_changed (GtkCellRendererCombo *combo,
+                           gchar                *model_path,
+                           GtkTreeIter          *model_iter,
+                           gpointer              data)
+{
+  GtkTreeView *shortcut_treeview;
+  GtkTreeModel *shortcut_model;
+  GtkTreeIter shortcut_iter;
+  GtkTreeSelection *selection;
+  CcKeyboardOption *option;
+  ShortcutType type;
+  GtkBuilder *builder = data;
+
+  shortcut_treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview"));
+  selection = gtk_tree_view_get_selection (shortcut_treeview);
+  if (!gtk_tree_selection_get_selected (selection, &shortcut_model, &shortcut_iter))
+    return;
+
+  gtk_tree_model_get (shortcut_model, &shortcut_iter,
+                      DETAIL_KEYENTRY_COLUMN, &option,
+                      DETAIL_TYPE_COLUMN, &type,
+                      -1);
+
+  if (type != SHORTCUT_TYPE_XKB_OPTION)
+    return;
+
+  cc_keyboard_option_set_selection (option, model_iter);
+}
+
+static gboolean
+poke_xkb_option_row (GtkTreeModel *model,
+                     GtkTreePath  *path,
+                     GtkTreeIter  *iter,
+                     gpointer      option)
+{
+  gpointer item;
+
+  gtk_tree_model_get (model, iter,
+                      DETAIL_KEYENTRY_COLUMN, &item,
+                      -1);
+
+  if (item != option)
+    return FALSE;
+
+  gtk_tree_model_row_changed (model, path, iter);
+  return TRUE;
+}
+
+static void
+xkb_option_changed (CcKeyboardOption *option,
+                    gpointer          data)
+{
+  GtkTreeModel *model = data;
+
+  gtk_tree_model_foreach (model, poke_xkb_option_row, option);
+}
+
+static void
+setup_keyboard_options (GtkListStore *store)
+{
+  GList *l;
+
+  for (l = cc_keyboard_option_get_all (); l; l = l->next)
+    g_signal_connect (l->data, "changed",
+                      G_CALLBACK (xkb_option_changed), store);
+}
+
+static void
 setup_dialog (CcPanel *panel, GtkBuilder *builder)
 {
   GtkCellRenderer *renderer;
@@ -1529,11 +1685,6 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
 
   binding_settings = g_settings_new (BINDINGS_SCHEMA);
 
-  g_signal_connect (treeview, "button_press_event",
-                    G_CALLBACK (start_editing_cb), builder);
-  g_signal_connect (treeview, "row-activated",
-                    G_CALLBACK (start_editing_kb_cb), NULL);
-
   renderer = gtk_cell_renderer_text_new ();
   g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
 
@@ -1548,10 +1699,14 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
                                                "accel-mode", GTK_CELL_RENDERER_ACCEL_MODE_OTHER,
                                                NULL);
 
+  g_signal_connect (treeview, "button_press_event",
+                    G_CALLBACK (start_editing_cb), renderer);
+  g_signal_connect (treeview, "row-activated",
+                    G_CALLBACK (start_editing_kb_cb), renderer);
+
   g_signal_connect (renderer, "accel_edited",
                     G_CALLBACK (accel_edited_callback),
                     treeview);
-
   g_signal_connect (renderer, "accel_cleared",
                     G_CALLBACK (accel_cleared_callback),
                     treeview);
@@ -1561,12 +1716,28 @@ setup_dialog (CcPanel *panel, GtkBuilder *builder)
   gtk_tree_view_column_set_resizable (column, FALSE);
   gtk_tree_view_column_set_expand (column, FALSE);
 
+  renderer = (GtkCellRenderer *) g_object_new (GTK_TYPE_CELL_RENDERER_COMBO,
+                                               "has-entry", FALSE,
+                                               "text-column", XKB_OPTION_DESCRIPTION_COLUMN,
+                                               "editable", TRUE,
+                                               "ellipsize", PANGO_ELLIPSIZE_END,
+                                               "width-chars", 25,
+                                               NULL);
+  g_signal_connect (renderer, "changed",
+                    G_CALLBACK (xkb_options_combo_changed), builder);
+
+  gtk_tree_view_column_pack_end (column, renderer, FALSE);
+
+  gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
+
   gtk_tree_view_append_column (treeview, column);
 
-  model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
+  model = gtk_list_store_new (DETAIL_N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_INT);
   gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
   g_object_unref (model);
 
+  setup_keyboard_options (model);
+
   widget = GTK_WIDGET (gtk_builder_get_object (builder, "actions_swindow"));
   context = gtk_widget_get_style_context (widget);
   gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
@@ -1639,4 +1810,6 @@ keyboard_shortcuts_dispose (CcPanel *panel)
     }
 
   g_clear_object (&binding_settings);
+
+  cc_keyboard_option_clear_all ();
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]