gnome-control-center/panels/keyboard/cc-keyboard-option.c
Rui Matos 978ab40f3e 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
2012-08-20 18:51:39 +02:00

449 lines
12 KiB
C

/*
* 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);
}