gnome-control-center/panels/keyboard/cc-keyboard-panel.c

432 lines
12 KiB
C
Raw Normal View History

/*
* Copyright (C) 2010 Intel, Inc
* Copyright (C) 2016 Endless, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
2014-01-23 12:57:27 +01:00
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Thomas Wood <thomas.wood@intel.com>
* Georges Basile Stavracas Neto <gbsneto@gnome.org>
*
*/
#include <glib/gi18n.h>
#include "cc-keyboard-item.h"
#include "cc-keyboard-manager.h"
#include "cc-keyboard-option.h"
#include "cc-keyboard-panel.h"
#include "cc-keyboard-resources.h"
#include "cc-keyboard-shortcut-editor.h"
#include "keyboard-shortcuts.h"
typedef struct {
CcKeyboardItem *item;
gchar *section_title;
gchar *section_id;
} RowData;
struct _CcKeyboardPanel
{
CcPanel parent;
/* Shortcut models */
GtkWidget *listbox;
GtkListBoxRow *add_shortcut_row;
GtkSizeGroup *accelerator_sizegroup;
/* Custom shortcut dialog */
GtkWidget *shortcut_editor;
GRegex *pictures_regex;
CcKeyboardManager *manager;
};
CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
enum {
PROP_0,
PROP_PARAMETERS
};
/* RowData functions */
static RowData *
row_data_new (CcKeyboardItem *item,
const gchar *section_id,
const gchar *section_title)
{
RowData *data;
data = g_new0 (RowData, 1);
data->item = g_object_ref (item);
data->section_id = g_strdup (section_id);
data->section_title = g_strdup (section_title);
return data;
}
static void
row_data_free (RowData *data)
{
g_object_unref (data->item);
g_free (data->section_id);
g_free (data->section_title);
g_free (data);
}
static gboolean
transform_binding_to_accel (GBinding *binding,
const GValue *from_value,
GValue *to_value,
gpointer user_data)
{
CcKeyboardItem *item;
gchar *accelerator;
item = CC_KEYBOARD_ITEM (g_binding_get_source (binding));
accelerator = convert_keysym_state_to_string (item->keyval, item->mask, item->keycode);
g_value_take_string (to_value, accelerator);
return TRUE;
}
static void
add_item (CcKeyboardPanel *self,
CcKeyboardItem *item,
const gchar *section_id,
const gchar *section_title)
{
GtkWidget *row, *box, *label;
/* Horizontal box */
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
/* Shortcut title */
label = gtk_label_new (item->description);
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
gtk_widget_set_hexpand (label, TRUE);
g_object_bind_property (item,
"description",
label,
"label",
G_BINDING_DEFAULT);
gtk_container_add (GTK_CONTAINER (box), label);
/* Shortcut accelerator */
label = gtk_label_new ("");
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_size_group_add_widget (self->accelerator_sizegroup, label);
g_object_bind_property_full (item,
"binding",
label,
"label",
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
transform_binding_to_accel,
NULL, NULL, NULL);
gtk_container_add (GTK_CONTAINER (box), label);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
/* The row */
row = gtk_list_box_row_new ();
gtk_container_add (GTK_CONTAINER (row), box);
gtk_widget_show_all (row);
g_object_set_data_full (G_OBJECT (row),
"data",
row_data_new (item, section_id, section_title),
(GDestroyNotify) row_data_free);
gtk_container_add (GTK_CONTAINER (self->listbox), row);
}
static void
remove_item (CcKeyboardPanel *self,
CcKeyboardItem *item)
{
GList *children, *l;
children = gtk_container_get_children (GTK_CONTAINER (self->listbox));
for (l = children; l != NULL; l = l->next)
{
RowData *row_data;
row_data = g_object_get_data (l->data, "data");
if (row_data->item == item)
{
gtk_container_remove (GTK_CONTAINER (self->listbox), l->data);
break;
}
}
g_list_free (children);
}
static gint
sort_function (GtkListBoxRow *a,
GtkListBoxRow *b,
gpointer user_data)
{
CcKeyboardPanel *self;
RowData *a_data, *b_data;
gint retval;
self = user_data;
if (a == self->add_shortcut_row)
return 1;
if (b == self->add_shortcut_row)
return -1;
a_data = g_object_get_data (G_OBJECT (a), "data");
b_data = g_object_get_data (G_OBJECT (b), "data");
/* Put custom shortcuts below everything else */
if (a_data->item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
return 1;
else if (b_data->item->type == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
return -1;
retval = g_strcmp0 (a_data->section_title, b_data->section_title);
if (retval != 0)
return retval;
return g_strcmp0 (a_data->item->description, b_data->item->description);
}
static void
header_function (GtkListBoxRow *row,
GtkListBoxRow *before,
gpointer user_data)
{
CcKeyboardPanel *self;
gboolean add_header;
RowData *data;
self = user_data;
add_header = FALSE;
/* The + row always has a separator */
if (row == self->add_shortcut_row)
{
GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
gtk_widget_show (separator);
gtk_list_box_row_set_header (row, separator);
return;
}
data = g_object_get_data (G_OBJECT (row), "data");
if (before)
{
RowData *before_data = g_object_get_data (G_OBJECT (before), "data");
if (before_data)
add_header = g_strcmp0 (before_data->section_id, data->section_id) != 0;
}
else
{
add_header = TRUE;
}
if (add_header)
{
GtkWidget *box, *label;
gchar *markup;
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
gtk_widget_set_margin_top (box, before ? 18 : 6);
markup = g_strdup_printf ("<b>%s</b>", _(data->section_title));
label = g_object_new (GTK_TYPE_LABEL,
"label", markup,
"use-markup", TRUE,
"xalign", 0.0,
"margin-start", 6,
NULL);
gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
gtk_container_add (GTK_CONTAINER (box), label);
gtk_container_add (GTK_CONTAINER (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
gtk_list_box_row_set_header (row, box);
gtk_widget_show_all (box);
g_free (markup);
}
else
{
gtk_list_box_row_set_header (row, NULL);
}
}
static void
shortcut_row_activated (GtkWidget *button,
GtkListBoxRow *row,
CcKeyboardPanel *self)
{
CcKeyboardShortcutEditor *editor;
editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
if (row != self->add_shortcut_row)
{
RowData *data = g_object_get_data (G_OBJECT (row), "data");
cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT);
cc_keyboard_shortcut_editor_set_item (editor, data->item);
}
else
{
cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE);
cc_keyboard_shortcut_editor_set_item (editor, NULL);
}
gtk_widget_show (self->shortcut_editor);
}
static void
cc_keyboard_panel_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_PARAMETERS:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static const char *
cc_keyboard_panel_get_help_uri (CcPanel *panel)
{
return "help:gnome-help/keyboard";
}
static void
cc_keyboard_panel_finalize (GObject *object)
{
CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
g_clear_pointer (&self->pictures_regex, g_regex_unref);
g_clear_object (&self->accelerator_sizegroup);
cc_keyboard_option_clear_all ();
G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
}
static void
cc_keyboard_panel_constructed (GObject *object)
{
CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
GtkWindow *toplevel;
G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructed (object);
/* Setup the dialog's transient parent */
toplevel = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), toplevel);
}
static void
cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
panel_class->get_help_uri = cc_keyboard_panel_get_help_uri;
object_class->set_property = cc_keyboard_panel_set_property;
object_class->finalize = cc_keyboard_panel_finalize;
object_class->constructed = cc_keyboard_panel_constructed;
g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/gnome-keyboard-panel.ui");
gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, add_shortcut_row);
gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, listbox);
gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated);
}
static void
cc_keyboard_panel_init (CcKeyboardPanel *self)
{
g_resources_register (cc_keyboard_get_resource ());
gtk_widget_init_template (GTK_WIDGET (self));
self->manager = cc_keyboard_manager_new ();
/* Use a sizegroup to make the accelerator labels the same width */
self->accelerator_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
/* Shortcut editor dialog */
self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
g_signal_connect_swapped (self->manager,
"shortcut-added",
G_CALLBACK (add_item),
self);
g_signal_connect_swapped (self->manager,
"shortcut-removed",
G_CALLBACK (remove_item),
self);
cc_keyboard_manager_load_shortcuts (self->manager);
/* Shortcut editor dialog */
self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
/* Setup the shortcuts listbox */
gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox),
sort_function,
self,
NULL);
gtk_list_box_set_header_func (GTK_LIST_BOX (self->listbox),
header_function,
self,
NULL);
}