keyboard: Display sections in a separate treeview
This commit is contained in:
parent
974acd358e
commit
ae26a638bc
3 changed files with 773 additions and 736 deletions
|
@ -7,85 +7,21 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gconf/gconf-client.h>
|
||||
#include <gdk/gdkx.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
#include <libgnome-control-center/cc-shell.h>
|
||||
|
||||
#include "wm-common.h"
|
||||
#include "gnome-keyboard-panel.h"
|
||||
#include "eggcellrendererkeys.h"
|
||||
|
||||
#define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
|
||||
#define MAX_ELEMENTS_BEFORE_SCROLLING 10
|
||||
#define MAX_CUSTOM_SHORTCUTS 1000
|
||||
#define RESPONSE_ADD 0
|
||||
#define RESPONSE_REMOVE 1
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
/* The gettext package to use to translate the section title */
|
||||
char *package;
|
||||
/* Name of the window manager the keys would apply to */
|
||||
char *wm_name;
|
||||
/* an array of KeyListEntry */
|
||||
GArray *entries;
|
||||
} KeyList;
|
||||
|
||||
typedef enum {
|
||||
COMPARISON_NONE = 0,
|
||||
COMPARISON_GT,
|
||||
COMPARISON_LT,
|
||||
COMPARISON_EQ
|
||||
} Comparison;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int value;
|
||||
char *key;
|
||||
char *description_name;
|
||||
char *cmd_name;
|
||||
Comparison comparison;
|
||||
} KeyListEntry;
|
||||
|
||||
enum
|
||||
{
|
||||
DESCRIPTION_COLUMN,
|
||||
KEYENTRY_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *gconf_key;
|
||||
guint keyval;
|
||||
guint keycode;
|
||||
EggVirtualModifierType mask;
|
||||
gboolean editable;
|
||||
GtkTreeModel *model;
|
||||
char *description;
|
||||
char *desc_gconf_key;
|
||||
gboolean desc_editable;
|
||||
char *command;
|
||||
char *cmd_gconf_key;
|
||||
gboolean cmd_editable;
|
||||
guint gconf_cnxn;
|
||||
guint gconf_cnxn_desc;
|
||||
guint gconf_cnxn_cmd;
|
||||
} KeyEntry;
|
||||
|
||||
static guint maybe_block_accels_id = 0;
|
||||
static gboolean block_accels = FALSE;
|
||||
static GtkWidget *custom_shortcut_dialog = NULL;
|
||||
static GtkWidget *custom_shortcut_name_entry = NULL;
|
||||
static GtkWidget *custom_shortcut_command_entry = NULL;
|
||||
static GHashTable *keyb_sections = NULL;
|
||||
|
||||
#define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
|
||||
|
||||
static char*
|
||||
binding_name (guint keyval,
|
||||
guint keycode,
|
||||
|
@ -100,91 +36,6 @@ binding_name (guint keyval,
|
|||
return g_strdup (translate ? _("Disabled") : "");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
binding_from_string (const char *str,
|
||||
guint *accelerator_key,
|
||||
guint *keycode,
|
||||
EggVirtualModifierType *accelerator_mods)
|
||||
{
|
||||
g_return_val_if_fail (accelerator_key != NULL, FALSE);
|
||||
|
||||
if (str == NULL || strcmp (str, "disabled") == 0)
|
||||
{
|
||||
*accelerator_key = 0;
|
||||
*keycode = 0;
|
||||
*accelerator_mods = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
egg_accelerator_parse_virtual (str, accelerator_key, keycode, accelerator_mods);
|
||||
|
||||
if (*accelerator_key == 0)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
accel_set_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
KeyEntry *key_entry;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
KEYENTRY_COLUMN, &key_entry,
|
||||
-1);
|
||||
|
||||
if (key_entry == NULL)
|
||||
g_object_set (cell,
|
||||
"visible", FALSE,
|
||||
NULL);
|
||||
else if (! key_entry->editable)
|
||||
g_object_set (cell,
|
||||
"visible", TRUE,
|
||||
"editable", FALSE,
|
||||
"accel_key", key_entry->keyval,
|
||||
"accel_mask", key_entry->mask,
|
||||
"keycode", key_entry->keycode,
|
||||
"style", PANGO_STYLE_ITALIC,
|
||||
NULL);
|
||||
else
|
||||
g_object_set (cell,
|
||||
"visible", TRUE,
|
||||
"editable", TRUE,
|
||||
"accel_key", key_entry->keyval,
|
||||
"accel_mask", key_entry->mask,
|
||||
"keycode", key_entry->keycode,
|
||||
"style", PANGO_STYLE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
description_set_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
KeyEntry *key_entry;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
KEYENTRY_COLUMN, &key_entry,
|
||||
-1);
|
||||
|
||||
if (key_entry != NULL)
|
||||
g_object_set (cell,
|
||||
"editable", FALSE,
|
||||
"text", key_entry->description != NULL ?
|
||||
key_entry->description : _("<Unknown Action>"),
|
||||
NULL);
|
||||
else
|
||||
g_object_set (cell,
|
||||
"editable", FALSE, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
keybinding_key_changed_foreach (GtkTreeModel *model,
|
||||
GtkTreePath *path,
|
||||
|
@ -433,43 +284,6 @@ key_is_already_shown (GtkTreeModel *model, const KeyListEntry *entry)
|
|||
return data.found;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
should_show_key (const KeyListEntry *entry)
|
||||
{
|
||||
int value;
|
||||
GConfClient *client;
|
||||
|
||||
if (entry->comparison == COMPARISON_NONE)
|
||||
return TRUE;
|
||||
|
||||
g_return_val_if_fail (entry->key != NULL, FALSE);
|
||||
|
||||
client = gconf_client_get_default();
|
||||
value = gconf_client_get_int (client, entry->key, NULL);
|
||||
g_object_unref (client);
|
||||
|
||||
switch (entry->comparison) {
|
||||
case COMPARISON_NONE:
|
||||
/* For compiler warnings */
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
case COMPARISON_GT:
|
||||
if (value > entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
case COMPARISON_LT:
|
||||
if (value < entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
case COMPARISON_EQ:
|
||||
if (value == entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
count_rows_foreach (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
||||
{
|
||||
|
@ -530,14 +344,10 @@ append_keys_to_tree (GtkBuilder *builder,
|
|||
const gchar *title,
|
||||
const KeyListEntry *keys_list)
|
||||
{
|
||||
GConfClient *client;
|
||||
GtkTreeIter parent_iter, iter;
|
||||
GtkTreeModel *model;
|
||||
gint i, j;
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
|
||||
|
||||
/* Try to find a section parent iter, if it already exists */
|
||||
find_section (model, &iter, title);
|
||||
parent_iter = iter;
|
||||
|
@ -549,121 +359,7 @@ append_keys_to_tree (GtkBuilder *builder,
|
|||
* then we need to scroll now */
|
||||
ensure_scrollbar (builder, i - 1);
|
||||
|
||||
for (j = 0; keys_list[j].name != NULL; j++)
|
||||
{
|
||||
GConfEntry *entry;
|
||||
KeyEntry *key_entry;
|
||||
const gchar *key_string;
|
||||
gchar *key_value;
|
||||
gchar *description;
|
||||
gchar *command;
|
||||
|
||||
if (!should_show_key (&keys_list[j]))
|
||||
continue;
|
||||
|
||||
if (key_is_already_shown (model, &keys_list[j]))
|
||||
continue;
|
||||
|
||||
key_string = keys_list[j].name;
|
||||
|
||||
entry = gconf_client_get_entry (client,
|
||||
key_string,
|
||||
NULL,
|
||||
TRUE,
|
||||
NULL);
|
||||
if (entry == NULL)
|
||||
{
|
||||
/* We don't actually want to popup a dialog - just skip this one */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys_list[j].description_name != NULL)
|
||||
{
|
||||
description = gconf_client_get_string (client, keys_list[j].description_name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
description = NULL;
|
||||
|
||||
if (gconf_entry_get_schema_name (entry))
|
||||
{
|
||||
GConfSchema *schema;
|
||||
|
||||
schema = gconf_client_get_schema (client,
|
||||
gconf_entry_get_schema_name (entry),
|
||||
NULL);
|
||||
if (schema != NULL)
|
||||
{
|
||||
description = g_strdup (gconf_schema_get_short_desc (schema));
|
||||
gconf_schema_free (schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (description == NULL)
|
||||
{
|
||||
/* Only print a warning for keys that should have a schema */
|
||||
if (keys_list[j].description_name == NULL)
|
||||
g_warning ("No description for key '%s'", key_string);
|
||||
}
|
||||
|
||||
if (keys_list[j].cmd_name != NULL)
|
||||
{
|
||||
command = gconf_client_get_string (client, keys_list[j].cmd_name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
command = NULL;
|
||||
}
|
||||
|
||||
key_entry = g_new0 (KeyEntry, 1);
|
||||
key_entry->gconf_key = g_strdup (key_string);
|
||||
key_entry->editable = gconf_entry_get_is_writable (entry);
|
||||
key_entry->model = model;
|
||||
key_entry->description = description;
|
||||
key_entry->command = command;
|
||||
if (keys_list[j].description_name != NULL)
|
||||
{
|
||||
key_entry->desc_gconf_key = g_strdup (keys_list[j].description_name);
|
||||
key_entry->desc_editable = gconf_client_key_is_writable (client, key_entry->desc_gconf_key, NULL);
|
||||
key_entry->gconf_cnxn_desc = gconf_client_notify_add (client,
|
||||
key_entry->desc_gconf_key,
|
||||
(GConfClientNotifyFunc) &keybinding_description_changed,
|
||||
key_entry, NULL, NULL);
|
||||
}
|
||||
if (keys_list[j].cmd_name != NULL)
|
||||
{
|
||||
key_entry->cmd_gconf_key = g_strdup (keys_list[j].cmd_name);
|
||||
key_entry->cmd_editable = gconf_client_key_is_writable (client, key_entry->cmd_gconf_key, NULL);
|
||||
key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client,
|
||||
key_entry->cmd_gconf_key,
|
||||
(GConfClientNotifyFunc) &keybinding_command_changed,
|
||||
key_entry, NULL, NULL);
|
||||
}
|
||||
|
||||
gconf_client_add_dir (client, key_string, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
key_entry->gconf_cnxn = gconf_client_notify_add (client,
|
||||
key_string,
|
||||
(GConfClientNotifyFunc) &keybinding_key_changed,
|
||||
key_entry, NULL, NULL);
|
||||
|
||||
key_value = gconf_client_get_string (client, key_string, NULL);
|
||||
binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
|
||||
g_free (key_value);
|
||||
|
||||
gconf_entry_free (entry);
|
||||
ensure_scrollbar (builder, i);
|
||||
|
||||
++i;
|
||||
gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent_iter);
|
||||
/* we use the DESCRIPTION_COLUMN only for the section headers */
|
||||
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
|
||||
KEYENTRY_COLUMN, key_entry,
|
||||
-1);
|
||||
gtk_tree_view_expand_all (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
|
||||
}
|
||||
|
||||
g_object_unref (client);
|
||||
===============================================================
|
||||
|
||||
/* Don't show an empty section */
|
||||
if (gtk_tree_model_iter_n_children (model, &parent_iter) == 0)
|
||||
|
@ -675,318 +371,6 @@ append_keys_to_tree (GtkBuilder *builder,
|
|||
gtk_widget_show (WID (builder, "shortcuts_vbox"));
|
||||
}
|
||||
|
||||
static void
|
||||
parse_start_tag (GMarkupParseContext *ctx,
|
||||
const gchar *element_name,
|
||||
const gchar **attr_names,
|
||||
const gchar **attr_values,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
KeyList *keylist = (KeyList *) user_data;
|
||||
KeyListEntry key;
|
||||
const char *name, *gconf_key;
|
||||
int value;
|
||||
Comparison comparison;
|
||||
|
||||
name = NULL;
|
||||
|
||||
/* The top-level element, names the section in the tree */
|
||||
if (g_str_equal (element_name, "KeyListEntries"))
|
||||
{
|
||||
const char *wm_name = NULL;
|
||||
const char *package = NULL;
|
||||
|
||||
while (*attr_names && *attr_values)
|
||||
{
|
||||
if (g_str_equal (*attr_names, "name"))
|
||||
{
|
||||
if (**attr_values)
|
||||
name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "wm_name")) {
|
||||
if (**attr_values)
|
||||
wm_name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "package")) {
|
||||
if (**attr_values)
|
||||
package = *attr_values;
|
||||
}
|
||||
++attr_names;
|
||||
++attr_values;
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
if (keylist->name)
|
||||
g_warning ("Duplicate section name");
|
||||
g_free (keylist->name);
|
||||
keylist->name = g_strdup (name);
|
||||
}
|
||||
if (wm_name)
|
||||
{
|
||||
if (keylist->wm_name)
|
||||
g_warning ("Duplicate window manager name");
|
||||
g_free (keylist->wm_name);
|
||||
keylist->wm_name = g_strdup (wm_name);
|
||||
}
|
||||
if (package)
|
||||
{
|
||||
if (keylist->package)
|
||||
g_warning ("Duplicate gettext package name");
|
||||
g_free (keylist->package);
|
||||
keylist->package = g_strdup (package);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_str_equal (element_name, "KeyListEntry")
|
||||
|| attr_names == NULL
|
||||
|| attr_values == NULL)
|
||||
return;
|
||||
|
||||
value = 0;
|
||||
comparison = COMPARISON_NONE;
|
||||
gconf_key = NULL;
|
||||
|
||||
while (*attr_names && *attr_values)
|
||||
{
|
||||
if (g_str_equal (*attr_names, "name"))
|
||||
{
|
||||
/* skip if empty */
|
||||
if (**attr_values)
|
||||
name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "value")) {
|
||||
if (**attr_values) {
|
||||
value = (int) g_ascii_strtoull (*attr_values, NULL, 0);
|
||||
}
|
||||
} else if (g_str_equal (*attr_names, "key")) {
|
||||
if (**attr_values) {
|
||||
gconf_key = *attr_values;
|
||||
}
|
||||
} else if (g_str_equal (*attr_names, "comparison")) {
|
||||
if (**attr_values) {
|
||||
if (g_str_equal (*attr_values, "gt")) {
|
||||
comparison = COMPARISON_GT;
|
||||
} else if (g_str_equal (*attr_values, "lt")) {
|
||||
comparison = COMPARISON_LT;
|
||||
} else if (g_str_equal (*attr_values, "eq")) {
|
||||
comparison = COMPARISON_EQ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++attr_names;
|
||||
++attr_values;
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
return;
|
||||
|
||||
key.name = g_strdup (name);
|
||||
key.description_name = NULL;
|
||||
key.value = value;
|
||||
if (gconf_key)
|
||||
key.key = g_strdup (gconf_key);
|
||||
else
|
||||
key.key = NULL;
|
||||
key.comparison = comparison;
|
||||
key.cmd_name = NULL;
|
||||
g_array_append_val (keylist->entries, key);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
strv_contains (char **strv,
|
||||
char *str)
|
||||
{
|
||||
char **p = strv;
|
||||
for (p = strv; *p; p++)
|
||||
if (strcmp (*p, str) == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
append_keys_to_tree_from_file (GtkBuilder *builder,
|
||||
const char *filename,
|
||||
char **wm_keybindings)
|
||||
{
|
||||
GMarkupParseContext *ctx;
|
||||
GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
|
||||
KeyList *keylist;
|
||||
KeyListEntry key, *keys;
|
||||
GError *err = NULL;
|
||||
char *buf;
|
||||
const char *title;
|
||||
gsize buf_len;
|
||||
guint i;
|
||||
|
||||
if (!g_file_get_contents (filename, &buf, &buf_len, &err))
|
||||
return;
|
||||
|
||||
keylist = g_new0 (KeyList, 1);
|
||||
keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
|
||||
ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
|
||||
|
||||
if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
|
||||
{
|
||||
g_warning ("Failed to parse '%s': '%s'", filename, err->message);
|
||||
g_error_free (err);
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
g_free (keylist->wm_name);
|
||||
for (i = 0; i < keylist->entries->len; i++)
|
||||
g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
|
||||
g_array_free (keylist->entries, TRUE);
|
||||
g_free (keylist);
|
||||
keylist = NULL;
|
||||
}
|
||||
g_markup_parse_context_free (ctx);
|
||||
g_free (buf);
|
||||
|
||||
if (keylist == NULL)
|
||||
return;
|
||||
|
||||
/* If there's no keys to add, or the settings apply to a window manager
|
||||
* that's not the one we're running */
|
||||
if (keylist->entries->len == 0
|
||||
|| (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name))
|
||||
|| keylist->name == NULL)
|
||||
{
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
g_free (keylist->wm_name);
|
||||
g_array_free (keylist->entries, TRUE);
|
||||
g_free (keylist);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Empty KeyListEntry to end the array */
|
||||
key.name = NULL;
|
||||
key.description_name = NULL;
|
||||
key.key = NULL;
|
||||
key.value = 0;
|
||||
key.comparison = COMPARISON_NONE;
|
||||
g_array_append_val (keylist->entries, key);
|
||||
|
||||
keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
|
||||
if (keylist->package)
|
||||
{
|
||||
bind_textdomain_codeset (keylist->package, "UTF-8");
|
||||
title = dgettext (keylist->package, keylist->name);
|
||||
} else {
|
||||
title = _(keylist->name);
|
||||
}
|
||||
|
||||
append_keys_to_tree (builder, title, keys);
|
||||
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
for (i = 0; keys[i].name != NULL; i++)
|
||||
g_free (keys[i].name);
|
||||
g_free (keylist);
|
||||
}
|
||||
|
||||
static void
|
||||
append_keys_to_tree_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
|
||||
{
|
||||
GConfClient *client;
|
||||
GSList *custom_list, *l;
|
||||
GArray *entries;
|
||||
KeyListEntry key;
|
||||
|
||||
/* load custom shortcuts from GConf */
|
||||
entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
|
||||
|
||||
key.key = NULL;
|
||||
key.value = 0;
|
||||
key.comparison = COMPARISON_NONE;
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
custom_list = gconf_client_all_dirs (client, gconf_path, NULL);
|
||||
|
||||
for (l = custom_list; l != NULL; l = l->next)
|
||||
{
|
||||
key.name = g_strconcat (l->data, "/binding", NULL);
|
||||
key.cmd_name = g_strconcat (l->data, "/action", NULL);
|
||||
key.description_name = g_strconcat (l->data, "/name", NULL);
|
||||
g_array_append_val (entries, key);
|
||||
|
||||
g_free (l->data);
|
||||
}
|
||||
|
||||
g_slist_free (custom_list);
|
||||
g_object_unref (client);
|
||||
|
||||
if (entries->len > 0)
|
||||
{
|
||||
KeyListEntry *keys;
|
||||
int i;
|
||||
|
||||
/* Empty KeyListEntry to end the array */
|
||||
key.name = NULL;
|
||||
key.description_name = NULL;
|
||||
g_array_append_val (entries, key);
|
||||
|
||||
keys = (KeyListEntry *) entries->data;
|
||||
append_keys_to_tree (builder, _("Custom Shortcuts"), keys);
|
||||
for (i = 0; i < entries->len; ++i)
|
||||
{
|
||||
g_free (keys[i].name);
|
||||
g_free (keys[i].description_name);
|
||||
}
|
||||
}
|
||||
|
||||
g_array_free (entries, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
reload_key_entries (GtkBuilder *builder)
|
||||
{
|
||||
gchar **wm_keybindings;
|
||||
GDir *dir;
|
||||
const char *name;
|
||||
GList *list, *l;
|
||||
|
||||
wm_keybindings = wm_common_get_current_keybindings();
|
||||
|
||||
clear_old_model (builder);
|
||||
|
||||
dir = g_dir_open (GNOMECC_KEYBINDINGS_DIR, 0, NULL);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
list = NULL;
|
||||
for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
|
||||
{
|
||||
if (g_str_has_suffix (name, ".xml"))
|
||||
{
|
||||
list = g_list_insert_sorted (list, g_strdup (name),
|
||||
(GCompareFunc) g_ascii_strcasecmp);
|
||||
}
|
||||
}
|
||||
g_dir_close (dir);
|
||||
|
||||
for (l = list; l != NULL; l = l->next)
|
||||
{
|
||||
gchar *path;
|
||||
|
||||
path = g_build_filename (GNOMECC_KEYBINDINGS_DIR, l->data, NULL);
|
||||
append_keys_to_tree_from_file (builder, path, wm_keybindings);
|
||||
g_free (l->data);
|
||||
g_free (path);
|
||||
}
|
||||
g_list_free (list);
|
||||
|
||||
/* Load custom shortcuts _after_ system-provided ones,
|
||||
* since some of the custom shortcuts may also be listed
|
||||
* in a file. Loading the custom shortcuts last makes
|
||||
* such keys not show up in the custom section.
|
||||
*/
|
||||
append_keys_to_tree_from_gconf (builder, GCONF_BINDING_DIR);
|
||||
|
||||
g_strfreev (wm_keybindings);
|
||||
}
|
||||
|
||||
static void
|
||||
key_entry_controlling_key_changed (GConfClient *client,
|
||||
guint cnxn_id,
|
||||
|
@ -1819,117 +1203,6 @@ remove_button_clicked (GtkWidget *button,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dialog (CcPanel *panel, GtkBuilder *builder)
|
||||
{
|
||||
GConfClient *client;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkWidget *widget;
|
||||
GtkTreeView *treeview;
|
||||
GtkTreeSelection *selection;
|
||||
GSList *allowed_keys;
|
||||
CcShell *shell;
|
||||
|
||||
treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
|
||||
"shortcut_treeview"));
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
|
||||
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_signal_connect (renderer, "edited",
|
||||
G_CALLBACK (description_edited_callback),
|
||||
treeview);
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Action"),
|
||||
renderer,
|
||||
"text", DESCRIPTION_COLUMN,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_resizable (column, FALSE);
|
||||
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
gtk_tree_view_column_set_sort_column_id (column, DESCRIPTION_COLUMN);
|
||||
|
||||
renderer = (GtkCellRenderer *) g_object_new (EGG_TYPE_CELL_RENDERER_KEYS,
|
||||
"accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X,
|
||||
NULL);
|
||||
|
||||
g_signal_connect (renderer, "accel_edited",
|
||||
G_CALLBACK (accel_edited_callback),
|
||||
treeview);
|
||||
|
||||
g_signal_connect (renderer, "accel_cleared",
|
||||
G_CALLBACK (accel_cleared_callback),
|
||||
treeview);
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_resizable (column, FALSE);
|
||||
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
gtk_tree_view_column_set_sort_column_id (column, KEYENTRY_COLUMN);
|
||||
|
||||
gconf_client_add_dir (client, GCONF_BINDING_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
gconf_client_add_dir (client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
gconf_client_notify_add (client,
|
||||
"/apps/metacity/general/num_workspaces",
|
||||
(GConfClientNotifyFunc) key_entry_controlling_key_changed,
|
||||
builder, NULL, NULL);
|
||||
|
||||
/* set up the dialog */
|
||||
reload_key_entries (builder);
|
||||
|
||||
shell = cc_panel_get_shell (CC_PANEL (panel));
|
||||
widget = cc_shell_get_toplevel (shell);
|
||||
|
||||
maybe_block_accels_id = g_signal_connect (widget, "key_press_event",
|
||||
G_CALLBACK (maybe_block_accels), NULL);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
||||
g_signal_connect (selection, "changed",
|
||||
G_CALLBACK (selection_changed),
|
||||
WID (builder, "remove-button"));
|
||||
|
||||
allowed_keys = gconf_client_get_list (client,
|
||||
GCONF_BINDING_DIR "/allowed_keys",
|
||||
GCONF_VALUE_STRING,
|
||||
NULL);
|
||||
if (allowed_keys != NULL)
|
||||
{
|
||||
g_slist_foreach (allowed_keys, (GFunc)g_free, NULL);
|
||||
g_slist_free (allowed_keys);
|
||||
gtk_widget_set_sensitive (WID (builder, "add-button"),
|
||||
FALSE);
|
||||
}
|
||||
|
||||
g_object_unref (client);
|
||||
|
||||
/* setup the custom shortcut dialog */
|
||||
custom_shortcut_dialog = WID (builder,
|
||||
"custom-shortcut-dialog");
|
||||
custom_shortcut_name_entry = WID (builder,
|
||||
"custom-shortcut-name-entry");
|
||||
custom_shortcut_command_entry = WID (builder,
|
||||
"custom-shortcut-command-entry");
|
||||
g_signal_connect (WID (builder, "add-button"),
|
||||
"clicked", G_CALLBACK (add_button_clicked), builder);
|
||||
g_signal_connect (WID (builder, "remove-button"),
|
||||
"clicked", G_CALLBACK (remove_button_clicked), builder);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
|
||||
GTK_RESPONSE_OK);
|
||||
|
||||
gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog),
|
||||
GTK_WINDOW (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
on_window_manager_change (const char *wm_name, GtkBuilder *builder)
|
||||
{
|
||||
|
@ -1942,9 +1215,6 @@ gnome_keybinding_properties_init (CcPanel *panel, GtkBuilder *builder)
|
|||
wm_common_register_window_manager_change ((GFunc) on_window_manager_change,
|
||||
builder);
|
||||
|
||||
keyb_sections = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, free_key_list);
|
||||
setup_dialog (panel, builder);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -552,6 +552,7 @@
|
|||
<object class="GtkTreeView" id="section_treeview">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="headers_visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -565,8 +566,8 @@
|
|||
<object class="GtkScrolledWindow" id="actions_swindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="vscrollbar_policy">never</property>
|
||||
<property name="hscrollbar_policy">automatic</property>
|
||||
<property name="vscrollbar_policy">automatic</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="shortcut_treeview">
|
||||
|
|
|
@ -19,25 +19,791 @@
|
|||
* Rodrigo Moya <rodrigo@gnome.org>
|
||||
*/
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
#include <gconf/gconf-client.h>
|
||||
#include "eggcellrendererkeys.h"
|
||||
#include "keyboard-shortcuts.h"
|
||||
#include "wm-common.h"
|
||||
|
||||
GHashTable *kb_sections = NULL;
|
||||
#define GCONF_BINDING_DIR "/desktop/gnome/keybindings"
|
||||
#define WID(builder, name) (GTK_WIDGET (gtk_builder_get_object (builder, name)))
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
/* The gettext package to use to translate the section title */
|
||||
char *package;
|
||||
/* Name of the window manager the keys would apply to */
|
||||
char *wm_name;
|
||||
/* an array of KeyListEntry */
|
||||
GArray *entries;
|
||||
} KeyList;
|
||||
|
||||
typedef enum {
|
||||
COMPARISON_NONE = 0,
|
||||
COMPARISON_GT,
|
||||
COMPARISON_LT,
|
||||
COMPARISON_EQ
|
||||
} Comparison;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int value;
|
||||
char *key;
|
||||
char *description_name;
|
||||
char *cmd_name;
|
||||
Comparison comparison;
|
||||
} KeyListEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *gconf_key;
|
||||
guint keyval;
|
||||
guint keycode;
|
||||
EggVirtualModifierType mask;
|
||||
gboolean editable;
|
||||
GtkTreeModel *model;
|
||||
char *description;
|
||||
char *desc_gconf_key;
|
||||
gboolean desc_editable;
|
||||
char *command;
|
||||
char *cmd_gconf_key;
|
||||
gboolean cmd_editable;
|
||||
guint gconf_cnxn;
|
||||
guint gconf_cnxn_desc;
|
||||
guint gconf_cnxn_cmd;
|
||||
} KeyEntry;
|
||||
|
||||
enum
|
||||
{
|
||||
DESCRIPTION_COLUMN,
|
||||
KEYENTRY_COLUMN,
|
||||
N_COLUMNS
|
||||
};
|
||||
|
||||
static guint maybe_block_accels_id = 0;
|
||||
static GtkWidget *custom_shortcut_dialog = NULL;
|
||||
static GtkWidget *custom_shortcut_name_entry = NULL;
|
||||
static GtkWidget *custom_shortcut_command_entry = NULL;
|
||||
static GHashTable *kb_sections = NULL;
|
||||
|
||||
static void
|
||||
free_key_list (gpointer list)
|
||||
free_key_array (KeyEntry **keys)
|
||||
{
|
||||
if (keys != NULL)
|
||||
{
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (keys); i++)
|
||||
{
|
||||
g_free (keys[i]->gconf_key);
|
||||
g_free (keys[i]->description);
|
||||
g_free (keys[i]->desc_gconf_key);
|
||||
g_free (keys[i]->command);
|
||||
g_free (keys[i]->cmd_gconf_key);
|
||||
}
|
||||
|
||||
g_free (keys);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
should_show_key (const KeyListEntry *entry)
|
||||
{
|
||||
int value;
|
||||
GConfClient *client;
|
||||
|
||||
if (entry->comparison == COMPARISON_NONE)
|
||||
return TRUE;
|
||||
|
||||
g_return_val_if_fail (entry->key != NULL, FALSE);
|
||||
|
||||
client = gconf_client_get_default();
|
||||
value = gconf_client_get_int (client, entry->key, NULL);
|
||||
g_object_unref (client);
|
||||
|
||||
switch (entry->comparison) {
|
||||
case COMPARISON_NONE:
|
||||
/* For compiler warnings */
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
case COMPARISON_GT:
|
||||
if (value > entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
case COMPARISON_LT:
|
||||
if (value < entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
case COMPARISON_EQ:
|
||||
if (value == entry->value)
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
binding_from_string (const char *str,
|
||||
guint *accelerator_key,
|
||||
guint *keycode,
|
||||
EggVirtualModifierType *accelerator_mods)
|
||||
{
|
||||
g_return_val_if_fail (accelerator_key != NULL, FALSE);
|
||||
|
||||
if (str == NULL || strcmp (str, "disabled") == 0)
|
||||
{
|
||||
*accelerator_key = 0;
|
||||
*keycode = 0;
|
||||
*accelerator_mods = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
egg_accelerator_parse_virtual (str, accelerator_key, keycode, accelerator_mods);
|
||||
|
||||
if (*accelerator_key == 0)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
append_section (GtkBuilder *builder, const gchar *title, const KeyListEntry *keys_list)
|
||||
{
|
||||
GArray *keys_array;
|
||||
GtkTreeModel *model;
|
||||
GConfClient *client;
|
||||
GtkTreeIter iter;
|
||||
gint i;
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview")));
|
||||
|
||||
g_print ("Appending section %s\n", title);
|
||||
|
||||
/* Append the section to the left tree view */
|
||||
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
|
||||
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
|
||||
DESCRIPTION_COLUMN, title,
|
||||
-1);
|
||||
|
||||
/* Add all KeyEntry's for this section */
|
||||
keys_array = g_array_new (TRUE, FALSE, sizeof (KeyEntry));
|
||||
|
||||
for (i = 0; keys_list[i].name != NULL; i++)
|
||||
{
|
||||
GConfEntry *entry;
|
||||
const gchar *key_string;
|
||||
gchar *description, *command, *key_value;
|
||||
KeyEntry *key_entry;
|
||||
|
||||
if (!should_show_key (&keys_list[i]))
|
||||
continue;
|
||||
|
||||
key_string = keys_list[i].name;
|
||||
|
||||
entry = gconf_client_get_entry (client,
|
||||
key_string,
|
||||
NULL,
|
||||
TRUE,
|
||||
NULL);
|
||||
if (entry == NULL)
|
||||
{
|
||||
/* We don't actually want to popup a dialog - just skip this one */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys_list[i].description_name != NULL)
|
||||
{
|
||||
description = gconf_client_get_string (client, keys_list[i].description_name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
description = NULL;
|
||||
|
||||
if (gconf_entry_get_schema_name (entry))
|
||||
{
|
||||
GConfSchema *schema;
|
||||
|
||||
schema = gconf_client_get_schema (client,
|
||||
gconf_entry_get_schema_name (entry),
|
||||
NULL);
|
||||
if (schema != NULL)
|
||||
{
|
||||
description = g_strdup (gconf_schema_get_short_desc (schema));
|
||||
gconf_schema_free (schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (description == NULL)
|
||||
{
|
||||
/* Only print a warning for keys that should have a schema */
|
||||
if (keys_list[i].description_name == NULL)
|
||||
g_warning ("No description for key '%s'", key_string);
|
||||
}
|
||||
|
||||
if (keys_list[i].cmd_name != NULL)
|
||||
{
|
||||
command = gconf_client_get_string (client, keys_list[i].cmd_name, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
command = NULL;
|
||||
}
|
||||
|
||||
key_entry = g_new0 (KeyEntry, 1);
|
||||
key_entry->gconf_key = g_strdup (key_string);
|
||||
key_entry->editable = gconf_entry_get_is_writable (entry);
|
||||
key_entry->model = model;
|
||||
key_entry->description = description;
|
||||
key_entry->command = command;
|
||||
if (keys_list[i].description_name != NULL)
|
||||
{
|
||||
key_entry->desc_gconf_key = g_strdup (keys_list[i].description_name);
|
||||
key_entry->desc_editable = gconf_client_key_is_writable (client, key_entry->desc_gconf_key, NULL);
|
||||
/* key_entry->gconf_cnxn_desc = gconf_client_notify_add (client, */
|
||||
/* key_entry->desc_gconf_key, */
|
||||
/* (GConfClientNotifyFunc) &keybinding_description_changed, */
|
||||
/* key_entry, NULL, NULL); */
|
||||
}
|
||||
if (keys_list[i].cmd_name != NULL)
|
||||
{
|
||||
/* key_entry->cmd_gconf_key = g_strdup (keys_list[i].cmd_name); */
|
||||
/* key_entry->cmd_editable = gconf_client_key_is_writable (client, key_entry->cmd_gconf_key, NULL); */
|
||||
/* key_entry->gconf_cnxn_cmd = gconf_client_notify_add (client, */
|
||||
/* key_entry->cmd_gconf_key, */
|
||||
/* (GConfClientNotifyFunc) &keybinding_command_changed, */
|
||||
/* key_entry, NULL, NULL); */
|
||||
}
|
||||
|
||||
gconf_client_add_dir (client, key_string, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
/* key_entry->gconf_cnxn = gconf_client_notify_add (client, */
|
||||
/* key_string, */
|
||||
/* (GConfClientNotifyFunc) &keybinding_key_changed, */
|
||||
/* key_entry, NULL, NULL); */
|
||||
|
||||
key_value = gconf_client_get_string (client, key_string, NULL);
|
||||
binding_from_string (key_value, &key_entry->keyval, &key_entry->keycode, &key_entry->mask);
|
||||
g_free (key_value);
|
||||
|
||||
gconf_entry_free (entry);
|
||||
|
||||
g_array_append_val (keys_array, key_entry);
|
||||
}
|
||||
|
||||
g_object_unref (client);
|
||||
|
||||
/* Add the keys to the hash table */
|
||||
if (keys_array->len > 0)
|
||||
{
|
||||
g_hash_table_insert (kb_sections, g_strdup (title), keys_array->data);
|
||||
}
|
||||
|
||||
g_array_free (keys_array, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_start_tag (GMarkupParseContext *ctx,
|
||||
const gchar *element_name,
|
||||
const gchar **attr_names,
|
||||
const gchar **attr_values,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
KeyList *keylist = (KeyList *) user_data;
|
||||
KeyListEntry key;
|
||||
const char *name, *gconf_key;
|
||||
int value;
|
||||
Comparison comparison;
|
||||
|
||||
name = NULL;
|
||||
|
||||
/* The top-level element, names the section in the tree */
|
||||
if (g_str_equal (element_name, "KeyListEntries"))
|
||||
{
|
||||
const char *wm_name = NULL;
|
||||
const char *package = NULL;
|
||||
|
||||
while (*attr_names && *attr_values)
|
||||
{
|
||||
if (g_str_equal (*attr_names, "name"))
|
||||
{
|
||||
if (**attr_values)
|
||||
name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "wm_name")) {
|
||||
if (**attr_values)
|
||||
wm_name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "package")) {
|
||||
if (**attr_values)
|
||||
package = *attr_values;
|
||||
}
|
||||
++attr_names;
|
||||
++attr_values;
|
||||
}
|
||||
|
||||
if (name)
|
||||
{
|
||||
if (keylist->name)
|
||||
g_warning ("Duplicate section name");
|
||||
g_free (keylist->name);
|
||||
keylist->name = g_strdup (name);
|
||||
}
|
||||
if (wm_name)
|
||||
{
|
||||
if (keylist->wm_name)
|
||||
g_warning ("Duplicate window manager name");
|
||||
g_free (keylist->wm_name);
|
||||
keylist->wm_name = g_strdup (wm_name);
|
||||
}
|
||||
if (package)
|
||||
{
|
||||
if (keylist->package)
|
||||
g_warning ("Duplicate gettext package name");
|
||||
g_free (keylist->package);
|
||||
keylist->package = g_strdup (package);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_str_equal (element_name, "KeyListEntry")
|
||||
|| attr_names == NULL
|
||||
|| attr_values == NULL)
|
||||
return;
|
||||
|
||||
value = 0;
|
||||
comparison = COMPARISON_NONE;
|
||||
gconf_key = NULL;
|
||||
|
||||
while (*attr_names && *attr_values)
|
||||
{
|
||||
if (g_str_equal (*attr_names, "name"))
|
||||
{
|
||||
/* skip if empty */
|
||||
if (**attr_values)
|
||||
name = *attr_values;
|
||||
} else if (g_str_equal (*attr_names, "value")) {
|
||||
if (**attr_values) {
|
||||
value = (int) g_ascii_strtoull (*attr_values, NULL, 0);
|
||||
}
|
||||
} else if (g_str_equal (*attr_names, "key")) {
|
||||
if (**attr_values) {
|
||||
gconf_key = *attr_values;
|
||||
}
|
||||
} else if (g_str_equal (*attr_names, "comparison")) {
|
||||
if (**attr_values) {
|
||||
if (g_str_equal (*attr_values, "gt")) {
|
||||
comparison = COMPARISON_GT;
|
||||
} else if (g_str_equal (*attr_values, "lt")) {
|
||||
comparison = COMPARISON_LT;
|
||||
} else if (g_str_equal (*attr_values, "eq")) {
|
||||
comparison = COMPARISON_EQ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++attr_names;
|
||||
++attr_values;
|
||||
}
|
||||
|
||||
if (name == NULL)
|
||||
return;
|
||||
|
||||
key.name = g_strdup (name);
|
||||
key.description_name = NULL;
|
||||
key.value = value;
|
||||
if (gconf_key)
|
||||
key.key = g_strdup (gconf_key);
|
||||
else
|
||||
key.key = NULL;
|
||||
key.comparison = comparison;
|
||||
key.cmd_name = NULL;
|
||||
g_array_append_val (keylist->entries, key);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
strv_contains (char **strv,
|
||||
char *str)
|
||||
{
|
||||
char **p = strv;
|
||||
for (p = strv; *p; p++)
|
||||
if (strcmp (*p, str) == 0)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
append_sections_from_file (GtkBuilder *builder, const gchar *path, gchar **wm_keybindings)
|
||||
{
|
||||
GError *err = NULL;
|
||||
char *buf;
|
||||
gsize buf_len;
|
||||
KeyList *keylist;
|
||||
KeyListEntry key, *keys;
|
||||
const char *title;
|
||||
guint i;
|
||||
GMarkupParseContext *ctx;
|
||||
GMarkupParser parser = { parse_start_tag, NULL, NULL, NULL, NULL };
|
||||
|
||||
/* Parse file */
|
||||
if (!g_file_get_contents (path, &buf, &buf_len, &err))
|
||||
return;
|
||||
|
||||
keylist = g_new0 (KeyList, 1);
|
||||
keylist->entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
|
||||
ctx = g_markup_parse_context_new (&parser, 0, keylist, NULL);
|
||||
|
||||
if (!g_markup_parse_context_parse (ctx, buf, buf_len, &err))
|
||||
{
|
||||
g_warning ("Failed to parse '%s': '%s'", path, err->message);
|
||||
g_error_free (err);
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
g_free (keylist->wm_name);
|
||||
for (i = 0; i < keylist->entries->len; i++)
|
||||
g_free (((KeyListEntry *) &(keylist->entries->data[i]))->name);
|
||||
g_array_free (keylist->entries, TRUE);
|
||||
g_free (keylist);
|
||||
keylist = NULL;
|
||||
}
|
||||
g_markup_parse_context_free (ctx);
|
||||
g_free (buf);
|
||||
|
||||
if (keylist == NULL)
|
||||
return;
|
||||
|
||||
/* If there's no keys to add, or the settings apply to a window manager
|
||||
* that's not the one we're running */
|
||||
if (keylist->entries->len == 0
|
||||
|| (keylist->wm_name != NULL && !strv_contains (wm_keybindings, keylist->wm_name))
|
||||
|| keylist->name == NULL)
|
||||
{
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
g_free (keylist->wm_name);
|
||||
g_array_free (keylist->entries, TRUE);
|
||||
g_free (keylist);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Empty KeyListEntry to end the array */
|
||||
key.name = NULL;
|
||||
key.description_name = NULL;
|
||||
key.key = NULL;
|
||||
key.value = 0;
|
||||
key.comparison = COMPARISON_NONE;
|
||||
g_array_append_val (keylist->entries, key);
|
||||
|
||||
keys = (KeyListEntry *) g_array_free (keylist->entries, FALSE);
|
||||
if (keylist->package)
|
||||
{
|
||||
bind_textdomain_codeset (keylist->package, "UTF-8");
|
||||
title = dgettext (keylist->package, keylist->name);
|
||||
} else {
|
||||
title = _(keylist->name);
|
||||
}
|
||||
|
||||
append_section (builder, title, keys);
|
||||
|
||||
g_free (keylist->name);
|
||||
g_free (keylist->package);
|
||||
|
||||
for (i = 0; keys[i].name != NULL; i++)
|
||||
g_free (keys[i].name);
|
||||
|
||||
g_free (keylist);
|
||||
}
|
||||
|
||||
static void
|
||||
append_sections_from_gconf (GtkBuilder *builder, const gchar *gconf_path)
|
||||
{
|
||||
GConfClient *client;
|
||||
GSList *custom_list, *l;
|
||||
GArray *entries;
|
||||
KeyListEntry key;
|
||||
|
||||
/* load custom shortcuts from GConf */
|
||||
entries = g_array_new (FALSE, TRUE, sizeof (KeyListEntry));
|
||||
|
||||
key.key = NULL;
|
||||
key.value = 0;
|
||||
key.comparison = COMPARISON_NONE;
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
custom_list = gconf_client_all_dirs (client, gconf_path, NULL);
|
||||
|
||||
for (l = custom_list; l != NULL; l = l->next)
|
||||
{
|
||||
key.name = g_strconcat (l->data, "/binding", NULL);
|
||||
key.cmd_name = g_strconcat (l->data, "/action", NULL);
|
||||
key.description_name = g_strconcat (l->data, "/name", NULL);
|
||||
g_array_append_val (entries, key);
|
||||
|
||||
g_free (l->data);
|
||||
}
|
||||
|
||||
g_slist_free (custom_list);
|
||||
g_object_unref (client);
|
||||
|
||||
if (entries->len > 0)
|
||||
{
|
||||
KeyListEntry *keys;
|
||||
int i;
|
||||
|
||||
/* Empty KeyListEntry to end the array */
|
||||
key.name = NULL;
|
||||
key.description_name = NULL;
|
||||
g_array_append_val (entries, key);
|
||||
|
||||
keys = (KeyListEntry *) entries->data;
|
||||
append_section (builder, _("Custom Shortcuts"), keys);
|
||||
for (i = 0; i < entries->len; ++i)
|
||||
{
|
||||
g_free (keys[i].name);
|
||||
g_free (keys[i].description_name);
|
||||
}
|
||||
}
|
||||
|
||||
g_array_free (entries, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
reload_sections (GtkBuilder *builder)
|
||||
{
|
||||
gchar **wm_keybindings;
|
||||
GDir *dir;
|
||||
const gchar *name;
|
||||
GtkTreeModel *section_model, *shortcut_model;
|
||||
|
||||
section_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview")));
|
||||
shortcut_model = gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_builder_get_object (builder, "shortcut_treeview")));
|
||||
/* FIXME: get current selection and keep it after refreshing */
|
||||
|
||||
/* Clear previous models */
|
||||
gtk_list_store_clear (GTK_LIST_STORE (section_model));
|
||||
gtk_list_store_clear (GTK_LIST_STORE (shortcut_model));
|
||||
|
||||
/* Load WM keybindings */
|
||||
wm_keybindings = wm_common_get_current_keybindings ();
|
||||
|
||||
dir = g_dir_open (GNOMECC_KEYBINDINGS_DIR, 0, NULL);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
for (name = g_dir_read_name (dir) ; name ; name = g_dir_read_name (dir))
|
||||
{
|
||||
if (g_str_has_suffix (name, ".xml"))
|
||||
{
|
||||
gchar *path;
|
||||
|
||||
path = g_build_filename (GNOMECC_KEYBINDINGS_DIR, name, NULL);
|
||||
append_sections_from_file (builder, path, wm_keybindings);
|
||||
|
||||
g_free (path);
|
||||
}
|
||||
}
|
||||
|
||||
g_dir_close (dir);
|
||||
g_strfreev (wm_keybindings);
|
||||
|
||||
/* Load custom keybindings */
|
||||
append_sections_from_gconf (builder, GCONF_BINDING_DIR);
|
||||
}
|
||||
|
||||
static void
|
||||
accel_set_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
KeyEntry *key_entry;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
KEYENTRY_COLUMN, &key_entry,
|
||||
-1);
|
||||
|
||||
if (key_entry == NULL)
|
||||
g_object_set (cell,
|
||||
"visible", FALSE,
|
||||
NULL);
|
||||
else if (! key_entry->editable)
|
||||
g_object_set (cell,
|
||||
"visible", TRUE,
|
||||
"editable", FALSE,
|
||||
"accel_key", key_entry->keyval,
|
||||
"accel_mask", key_entry->mask,
|
||||
"keycode", key_entry->keycode,
|
||||
"style", PANGO_STYLE_ITALIC,
|
||||
NULL);
|
||||
else
|
||||
g_object_set (cell,
|
||||
"visible", TRUE,
|
||||
"editable", TRUE,
|
||||
"accel_key", key_entry->keyval,
|
||||
"accel_mask", key_entry->mask,
|
||||
"keycode", key_entry->keycode,
|
||||
"style", PANGO_STYLE_NORMAL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
description_set_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
KeyEntry *key_entry;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
KEYENTRY_COLUMN, &key_entry,
|
||||
-1);
|
||||
|
||||
if (key_entry != NULL)
|
||||
g_object_set (cell,
|
||||
"editable", FALSE,
|
||||
"text", key_entry->description != NULL ?
|
||||
key_entry->description : _("<Unknown Action>"),
|
||||
NULL);
|
||||
else
|
||||
g_object_set (cell,
|
||||
"editable", FALSE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_dialog (CcPanel *panel, GtkBuilder *builder)
|
||||
{
|
||||
GConfClient *client;
|
||||
GtkCellRenderer *renderer;
|
||||
GtkTreeViewColumn *column;
|
||||
GtkWidget *widget;
|
||||
GtkTreeView *treeview;
|
||||
GtkTreeSelection *selection;
|
||||
GSList *allowed_keys;
|
||||
CcShell *shell;
|
||||
GtkListStore *model;
|
||||
|
||||
/* Setup the section treeview */
|
||||
treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder, "section_treeview"));
|
||||
|
||||
renderer = gtk_cell_renderer_text_new ();
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Section"),
|
||||
renderer,
|
||||
"text", DESCRIPTION_COLUMN,
|
||||
NULL);
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
|
||||
model = gtk_list_store_new (1, G_TYPE_STRING);
|
||||
gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (model));
|
||||
|
||||
/* Setup the shortcut treeview */
|
||||
treeview = GTK_TREE_VIEW (gtk_builder_get_object (builder,
|
||||
"shortcut_treeview"));
|
||||
|
||||
client = gconf_client_get_default ();
|
||||
|
||||
/* 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_signal_connect (renderer, "edited", */
|
||||
/* G_CALLBACK (description_edited_callback), */
|
||||
/* treeview); */
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Action"),
|
||||
renderer,
|
||||
"text", DESCRIPTION_COLUMN,
|
||||
NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer, description_set_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_resizable (column, FALSE);
|
||||
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
gtk_tree_view_column_set_sort_column_id (column, DESCRIPTION_COLUMN);
|
||||
|
||||
renderer = (GtkCellRenderer *) g_object_new (EGG_TYPE_CELL_RENDERER_KEYS,
|
||||
"accel_mode", EGG_CELL_RENDERER_KEYS_MODE_X,
|
||||
NULL);
|
||||
|
||||
/* g_signal_connect (renderer, "accel_edited", */
|
||||
/* G_CALLBACK (accel_edited_callback), */
|
||||
/* treeview); */
|
||||
|
||||
/* g_signal_connect (renderer, "accel_cleared", */
|
||||
/* G_CALLBACK (accel_cleared_callback), */
|
||||
/* treeview); */
|
||||
|
||||
column = gtk_tree_view_column_new_with_attributes (_("Shortcut"), renderer, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer, accel_set_func, NULL, NULL);
|
||||
gtk_tree_view_column_set_resizable (column, FALSE);
|
||||
|
||||
gtk_tree_view_append_column (treeview, column);
|
||||
gtk_tree_view_column_set_sort_column_id (column, KEYENTRY_COLUMN);
|
||||
|
||||
gconf_client_add_dir (client, GCONF_BINDING_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
gconf_client_add_dir (client, "/apps/metacity/general", GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
|
||||
/* gconf_client_notify_add (client, */
|
||||
/* "/apps/metacity/general/num_workspaces", */
|
||||
/* (GConfClientNotifyFunc) key_entry_controlling_key_changed, */
|
||||
/* builder, NULL, NULL); */
|
||||
|
||||
/* set up the dialog */
|
||||
shell = cc_panel_get_shell (CC_PANEL (panel));
|
||||
widget = cc_shell_get_toplevel (shell);
|
||||
|
||||
/* maybe_block_accels_id = g_signal_connect (widget, "key_press_event", */
|
||||
/* G_CALLBACK (maybe_block_accels), NULL); */
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
|
||||
/* g_signal_connect (selection, "changed", */
|
||||
/* G_CALLBACK (selection_changed), */
|
||||
/* WID (builder, "remove-button")); */
|
||||
|
||||
allowed_keys = gconf_client_get_list (client,
|
||||
GCONF_BINDING_DIR "/allowed_keys",
|
||||
GCONF_VALUE_STRING,
|
||||
NULL);
|
||||
if (allowed_keys != NULL)
|
||||
{
|
||||
g_slist_foreach (allowed_keys, (GFunc)g_free, NULL);
|
||||
g_slist_free (allowed_keys);
|
||||
gtk_widget_set_sensitive (WID (builder, "add-button"),
|
||||
FALSE);
|
||||
}
|
||||
|
||||
g_object_unref (client);
|
||||
|
||||
/* setup the custom shortcut dialog */
|
||||
custom_shortcut_dialog = WID (builder,
|
||||
"custom-shortcut-dialog");
|
||||
custom_shortcut_name_entry = WID (builder,
|
||||
"custom-shortcut-name-entry");
|
||||
custom_shortcut_command_entry = WID (builder,
|
||||
"custom-shortcut-command-entry");
|
||||
/* g_signal_connect (WID (builder, "add-button"), */
|
||||
/* "clicked", G_CALLBACK (add_button_clicked), builder); */
|
||||
/* g_signal_connect (WID (builder, "remove-button"), */
|
||||
/* "clicked", G_CALLBACK (remove_button_clicked), builder); */
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (custom_shortcut_dialog),
|
||||
GTK_RESPONSE_OK);
|
||||
|
||||
gtk_window_set_transient_for (GTK_WINDOW (custom_shortcut_dialog),
|
||||
GTK_WINDOW (widget));
|
||||
}
|
||||
|
||||
void
|
||||
keyboard_shortcuts_init (CcPanel *panel, GtkBuilder *builder)
|
||||
{
|
||||
kb_sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_key_list);
|
||||
kb_sections = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, (GDestroyNotify) free_key_array);
|
||||
setup_dialog (panel, builder);
|
||||
reload_sections (builder);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue