Since we can't export IBus sources through the localed API we shouldn't even allow users to choose them. This allows us to stop having to show an apologising dialog which makes us look a bit bad and was using deprecated gtk+ API that we're trying to get rid of. Instead, we show the same information up front, in the input chooser. https://bugzilla.gnome.org/show_bug.cgi?id=740986
1228 lines
36 KiB
C
1228 lines
36 KiB
C
/*
|
|
* Copyright (C) 2013 Red Hat, 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
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <locale.h>
|
|
#include <glib/gi18n.h>
|
|
|
|
#define GNOME_DESKTOP_USE_UNSTABLE_API
|
|
#include <libgnome-desktop/gnome-languages.h>
|
|
|
|
#include "cc-common-language.h"
|
|
#include "cc-util.h"
|
|
#include "cc-input-chooser.h"
|
|
|
|
#ifdef HAVE_IBUS
|
|
#include <ibus.h>
|
|
#include "cc-ibus-utils.h"
|
|
#endif /* HAVE_IBUS */
|
|
|
|
#define INPUT_SOURCE_TYPE_XKB "xkb"
|
|
#define INPUT_SOURCE_TYPE_IBUS "ibus"
|
|
|
|
#define MAIN_WINDOW_WIDTH_RATIO 0.60
|
|
#define FILTER_TIMEOUT 150 /* ms */
|
|
|
|
typedef enum {
|
|
ROW_TRAVEL_DIRECTION_NONE,
|
|
ROW_TRAVEL_DIRECTION_FORWARD,
|
|
ROW_TRAVEL_DIRECTION_BACKWARD
|
|
} RowTravelDirection;
|
|
|
|
typedef enum {
|
|
ROW_LABEL_POSITION_START,
|
|
ROW_LABEL_POSITION_CENTER,
|
|
ROW_LABEL_POSITION_END
|
|
} RowLabelPosition;
|
|
|
|
typedef struct {
|
|
/* Not owned */
|
|
GtkWidget *add_button;
|
|
GtkWidget *filter_entry;
|
|
GtkWidget *list;
|
|
GtkWidget *scrolledwindow;
|
|
GtkAdjustment *adjustment;
|
|
GnomeXkbInfo *xkb_info;
|
|
GHashTable *ibus_engines;
|
|
|
|
/* Owned */
|
|
GtkListBoxRow *more_row;
|
|
GtkWidget *no_results;
|
|
GHashTable *locales;
|
|
GHashTable *locales_by_language;
|
|
gboolean showing_extra;
|
|
guint filter_timeout_id;
|
|
gchar **filter_words;
|
|
|
|
gboolean is_login;
|
|
} CcInputChooserPrivate;
|
|
|
|
#define GET_PRIVATE(chooser) ((CcInputChooserPrivate *) g_object_get_data (G_OBJECT (chooser), "private"))
|
|
#define WID(name) ((GtkWidget *) gtk_builder_get_object (builder, name))
|
|
|
|
typedef struct {
|
|
gchar *id;
|
|
gchar *name;
|
|
gchar *unaccented_name;
|
|
gchar *untranslated_name;
|
|
GtkListBoxRow *default_input_source_row;
|
|
GtkListBoxRow *locale_row;
|
|
GtkListBoxRow *back_row;
|
|
GHashTable *layout_rows_by_id;
|
|
GHashTable *engine_rows_by_id;
|
|
} LocaleInfo;
|
|
|
|
static void
|
|
locale_info_free (gpointer data)
|
|
{
|
|
LocaleInfo *info = data;
|
|
|
|
g_free (info->id);
|
|
g_free (info->name);
|
|
g_free (info->unaccented_name);
|
|
g_free (info->untranslated_name);
|
|
g_object_unref (info->default_input_source_row);
|
|
g_object_unref (info->locale_row);
|
|
g_object_unref (info->back_row);
|
|
g_hash_table_destroy (info->layout_rows_by_id);
|
|
g_hash_table_destroy (info->engine_rows_by_id);
|
|
g_free (info);
|
|
}
|
|
|
|
static void
|
|
set_row_widget_margins (GtkWidget *widget)
|
|
{
|
|
gtk_widget_set_margin_start (widget, 20);
|
|
gtk_widget_set_margin_end (widget, 20);
|
|
gtk_widget_set_margin_top (widget, 6);
|
|
gtk_widget_set_margin_bottom (widget, 6);
|
|
}
|
|
|
|
static GtkWidget *
|
|
padded_label_new (const gchar *text,
|
|
RowLabelPosition position,
|
|
RowTravelDirection direction,
|
|
gboolean dim_label)
|
|
{
|
|
GtkWidget *widget;
|
|
GtkWidget *label;
|
|
GtkWidget *arrow;
|
|
GtkAlign alignment;
|
|
|
|
if (position == ROW_LABEL_POSITION_START)
|
|
alignment = GTK_ALIGN_START;
|
|
else if (position == ROW_LABEL_POSITION_CENTER)
|
|
alignment = GTK_ALIGN_CENTER;
|
|
else
|
|
alignment = GTK_ALIGN_END;
|
|
|
|
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
|
|
if (direction == ROW_TRAVEL_DIRECTION_BACKWARD)
|
|
{
|
|
arrow = gtk_image_new_from_icon_name ("go-previous-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_box_pack_start (GTK_BOX (widget), arrow, FALSE, TRUE, 0);
|
|
}
|
|
|
|
label = gtk_label_new (text);
|
|
gtk_widget_set_halign (label, alignment);
|
|
set_row_widget_margins (label);
|
|
gtk_box_pack_start (GTK_BOX (widget), label, TRUE, TRUE, 0);
|
|
if (dim_label)
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
|
|
|
|
if (direction == ROW_TRAVEL_DIRECTION_FORWARD)
|
|
{
|
|
arrow = gtk_image_new_from_icon_name ("go-next-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_box_pack_start (GTK_BOX (widget), arrow, FALSE, TRUE, 0);
|
|
}
|
|
|
|
return widget;
|
|
}
|
|
|
|
static GtkListBoxRow *
|
|
more_row_new (void)
|
|
{
|
|
GtkWidget *row;
|
|
GtkWidget *box;
|
|
GtkWidget *arrow;
|
|
|
|
row = gtk_list_box_row_new ();
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
|
gtk_container_add (GTK_CONTAINER (row), box);
|
|
gtk_widget_set_tooltip_text (row, _("More…"));
|
|
|
|
arrow = gtk_image_new_from_icon_name ("view-more-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (arrow), "dim-label");
|
|
set_row_widget_margins (arrow);
|
|
gtk_box_pack_start (GTK_BOX (box), arrow, TRUE, TRUE, 0);
|
|
|
|
return GTK_LIST_BOX_ROW (row);
|
|
}
|
|
|
|
static GtkWidget *
|
|
no_results_widget_new (void)
|
|
{
|
|
return padded_label_new (_("No input sources found"), ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, TRUE);
|
|
}
|
|
|
|
static GtkListBoxRow *
|
|
back_row_new (const gchar *text)
|
|
{
|
|
GtkWidget *row;
|
|
GtkWidget *widget;
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_BACKWARD, TRUE);
|
|
gtk_container_add (GTK_CONTAINER (row), widget);
|
|
|
|
return GTK_LIST_BOX_ROW (row);
|
|
}
|
|
|
|
static GtkListBoxRow *
|
|
locale_row_new (const gchar *text)
|
|
{
|
|
GtkWidget *row;
|
|
GtkWidget *widget;
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, FALSE);
|
|
gtk_container_add (GTK_CONTAINER (row), widget);
|
|
|
|
return GTK_LIST_BOX_ROW (row);
|
|
}
|
|
|
|
static GtkWidget *
|
|
locale_header_widget_new (const gchar *text)
|
|
{
|
|
GtkWidget *widget;
|
|
|
|
widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_box_pack_start (GTK_BOX (widget),
|
|
gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
|
|
FALSE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (widget),
|
|
padded_label_new (text, ROW_LABEL_POSITION_CENTER, ROW_TRAVEL_DIRECTION_NONE, TRUE),
|
|
FALSE, FALSE, 0);
|
|
gtk_box_pack_start (GTK_BOX (widget),
|
|
gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
|
|
FALSE, FALSE, 0);
|
|
gtk_widget_show_all (widget);
|
|
return widget;
|
|
}
|
|
|
|
static GtkListBoxRow *
|
|
input_source_row_new (GtkWidget *chooser,
|
|
const gchar *type,
|
|
const gchar *id)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GtkWidget *row = NULL;
|
|
GtkWidget *widget;
|
|
|
|
if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
|
|
{
|
|
const gchar *display_name;
|
|
|
|
gnome_xkb_info_get_layout_info (priv->xkb_info, id, &display_name, NULL, NULL, NULL);
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = padded_label_new (display_name,
|
|
ROW_LABEL_POSITION_START,
|
|
ROW_TRAVEL_DIRECTION_NONE,
|
|
FALSE);
|
|
gtk_container_add (GTK_CONTAINER (row), widget);
|
|
g_object_set_data (G_OBJECT (row), "name", (gpointer) display_name);
|
|
g_object_set_data_full (G_OBJECT (row), "unaccented-name",
|
|
cc_util_normalize_casefold_and_unaccent (display_name), g_free);
|
|
}
|
|
else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
|
|
{
|
|
#ifdef HAVE_IBUS
|
|
gchar *display_name;
|
|
GtkWidget *image;
|
|
|
|
display_name = engine_get_display_name (g_hash_table_lookup (priv->ibus_engines, id));
|
|
|
|
row = gtk_list_box_row_new ();
|
|
widget = padded_label_new (display_name,
|
|
ROW_LABEL_POSITION_START,
|
|
ROW_TRAVEL_DIRECTION_NONE,
|
|
FALSE);
|
|
gtk_container_add (GTK_CONTAINER (row), widget);
|
|
image = gtk_image_new_from_icon_name ("system-run-symbolic", GTK_ICON_SIZE_MENU);
|
|
set_row_widget_margins (image);
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label");
|
|
gtk_box_pack_start (GTK_BOX (widget), image, FALSE, TRUE, 0);
|
|
|
|
g_object_set_data_full (G_OBJECT (row), "name", display_name, g_free);
|
|
g_object_set_data_full (G_OBJECT (row), "unaccented-name",
|
|
cc_util_normalize_casefold_and_unaccent (display_name), g_free);
|
|
#else
|
|
widget = NULL;
|
|
#endif /* HAVE_IBUS */
|
|
}
|
|
|
|
if (row)
|
|
{
|
|
g_object_set_data (G_OBJECT (row), "type", (gpointer) type);
|
|
g_object_set_data (G_OBJECT (row), "id", (gpointer) id);
|
|
|
|
return GTK_LIST_BOX_ROW (row);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
remove_all_children (GtkContainer *container)
|
|
{
|
|
GList *list, *l;
|
|
|
|
list = gtk_container_get_children (container);
|
|
for (l = list; l; l = l->next)
|
|
gtk_container_remove (container, (GtkWidget *) l->data);
|
|
g_list_free (list);
|
|
}
|
|
|
|
static void
|
|
set_fixed_size (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GtkPolicyType policy;
|
|
gint width, height;
|
|
|
|
gtk_scrolled_window_get_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow), &policy, NULL);
|
|
if (policy == GTK_POLICY_AUTOMATIC)
|
|
return;
|
|
|
|
/* Don't let it automatically get wider than the main CC window nor
|
|
get taller than the initial height */
|
|
gtk_window_get_size (gtk_window_get_transient_for (GTK_WINDOW (chooser)),
|
|
&width, NULL);
|
|
gtk_window_get_size (GTK_WINDOW (chooser), NULL, &height);
|
|
gtk_widget_set_size_request (chooser, width * MAIN_WINDOW_WIDTH_RATIO, height);
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolledwindow),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
}
|
|
|
|
static void
|
|
update_header (GtkListBoxRow *row,
|
|
GtkListBoxRow *before,
|
|
gpointer user_data)
|
|
{
|
|
GtkWidget *current;
|
|
|
|
current = gtk_list_box_row_get_header (row);
|
|
|
|
if (before == NULL)
|
|
{
|
|
if (current)
|
|
gtk_list_box_row_set_header (row, NULL);
|
|
return;
|
|
}
|
|
|
|
if (current == NULL || !GTK_IS_SEPARATOR (current))
|
|
{
|
|
current = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
|
gtk_widget_show (current);
|
|
gtk_list_box_row_set_header (row, current);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_input_source_rows_for_locale (GtkWidget *chooser,
|
|
LocaleInfo *info)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GtkWidget *row;
|
|
GHashTableIter iter;
|
|
const gchar *id;
|
|
|
|
if (info->default_input_source_row)
|
|
gtk_container_add (GTK_CONTAINER (priv->list), GTK_WIDGET (info->default_input_source_row));
|
|
|
|
g_hash_table_iter_init (&iter, info->layout_rows_by_id);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &row))
|
|
gtk_container_add (GTK_CONTAINER (priv->list), row);
|
|
|
|
g_hash_table_iter_init (&iter, info->engine_rows_by_id);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &id, (gpointer *) &row))
|
|
gtk_container_add (GTK_CONTAINER (priv->list), row);
|
|
}
|
|
|
|
static void
|
|
show_input_sources_for_locale (GtkWidget *chooser,
|
|
LocaleInfo *info)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
|
|
set_fixed_size (chooser);
|
|
|
|
remove_all_children (GTK_CONTAINER (priv->list));
|
|
|
|
if (!info->back_row)
|
|
{
|
|
info->back_row = g_object_ref_sink (back_row_new (info->name));
|
|
g_object_set_data (G_OBJECT (info->back_row), "back", GINT_TO_POINTER (TRUE));
|
|
g_object_set_data (G_OBJECT (info->back_row), "locale-info", info);
|
|
}
|
|
gtk_container_add (GTK_CONTAINER (priv->list), GTK_WIDGET (info->back_row));
|
|
|
|
add_input_source_rows_for_locale (chooser, info);
|
|
|
|
gtk_widget_show_all (priv->list);
|
|
|
|
gtk_adjustment_set_value (priv->adjustment,
|
|
gtk_adjustment_get_lower (priv->adjustment));
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list), update_header, NULL, NULL);
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
|
|
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (priv->list), FALSE);
|
|
|
|
if (gtk_widget_is_visible (priv->filter_entry) &&
|
|
!gtk_widget_is_focus (priv->filter_entry))
|
|
gtk_widget_grab_focus (priv->filter_entry);
|
|
}
|
|
|
|
static gboolean
|
|
is_current_locale (const gchar *locale)
|
|
{
|
|
return g_strcmp0 (setlocale (LC_CTYPE, NULL), locale) == 0;
|
|
}
|
|
|
|
static void
|
|
show_locale_rows (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GHashTable *initial = NULL;
|
|
LocaleInfo *info;
|
|
GHashTableIter iter;
|
|
|
|
remove_all_children (GTK_CONTAINER (priv->list));
|
|
|
|
if (!priv->showing_extra)
|
|
initial = cc_common_language_get_initial_languages ();
|
|
|
|
g_hash_table_iter_init (&iter, priv->locales);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
|
|
{
|
|
if (!info->default_input_source_row &&
|
|
!g_hash_table_size (info->layout_rows_by_id) &&
|
|
!g_hash_table_size (info->engine_rows_by_id))
|
|
continue;
|
|
|
|
if (!info->locale_row)
|
|
{
|
|
info->locale_row = g_object_ref_sink (locale_row_new (info->name));
|
|
g_object_set_data (G_OBJECT (info->locale_row), "locale-info", info);
|
|
|
|
if (!priv->showing_extra &&
|
|
!g_hash_table_contains (initial, info->id) &&
|
|
!is_current_locale (info->id))
|
|
g_object_set_data (G_OBJECT (info->locale_row), "is-extra", GINT_TO_POINTER (TRUE));
|
|
}
|
|
gtk_container_add (GTK_CONTAINER (priv->list), GTK_WIDGET (info->locale_row));
|
|
}
|
|
|
|
gtk_container_add (GTK_CONTAINER (priv->list), GTK_WIDGET (priv->more_row));
|
|
|
|
gtk_widget_show_all (priv->list);
|
|
|
|
gtk_adjustment_set_value (priv->adjustment,
|
|
gtk_adjustment_get_lower (priv->adjustment));
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list), update_header, NULL, NULL);
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_NONE);
|
|
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (priv->list), TRUE);
|
|
|
|
if (gtk_widget_is_visible (priv->filter_entry) &&
|
|
!gtk_widget_is_focus (priv->filter_entry))
|
|
gtk_widget_grab_focus (priv->filter_entry);
|
|
|
|
if (!priv->showing_extra)
|
|
g_hash_table_destroy (initial);
|
|
}
|
|
|
|
static gint
|
|
list_sort (gconstpointer a,
|
|
gconstpointer b,
|
|
gpointer data)
|
|
{
|
|
GtkWidget *chooser = data;
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
LocaleInfo *ia;
|
|
LocaleInfo *ib;
|
|
const gchar *la;
|
|
const gchar *lb;
|
|
gint retval;
|
|
|
|
/* Always goes at the end */
|
|
if (a == priv->more_row)
|
|
return 1;
|
|
if (b == priv->more_row)
|
|
return -1;
|
|
|
|
ia = g_object_get_data (G_OBJECT (a), "locale-info");
|
|
ib = g_object_get_data (G_OBJECT (b), "locale-info");
|
|
|
|
/* The "Other" locale always goes at the end */
|
|
if (!ia->id[0] && ib->id[0])
|
|
return 1;
|
|
else if (ia->id[0] && !ib->id[0])
|
|
return -1;
|
|
|
|
retval = g_strcmp0 (ia->name, ib->name);
|
|
if (retval)
|
|
return retval;
|
|
|
|
la = g_object_get_data (G_OBJECT (a), "name");
|
|
lb = g_object_get_data (G_OBJECT (b), "name");
|
|
|
|
/* Only input sources have a "name" property and they should always
|
|
go after their respective heading */
|
|
if (la && !lb)
|
|
return 1;
|
|
else if (!la && lb)
|
|
return -1;
|
|
else if (!la && !lb)
|
|
return 0; /* Shouldn't happen */
|
|
|
|
/* The default input source always goes first in its group */
|
|
if (g_object_get_data (G_OBJECT (a), "default"))
|
|
return -1;
|
|
if (g_object_get_data (G_OBJECT (b), "default"))
|
|
return 1;
|
|
|
|
return g_strcmp0 (la, lb);
|
|
}
|
|
|
|
static gboolean
|
|
match_all (gchar **words,
|
|
const gchar *str)
|
|
{
|
|
gchar **w;
|
|
|
|
for (w = words; *w; ++w)
|
|
if (!strstr (str, *w))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
list_filter (GtkListBoxRow *row,
|
|
gpointer user_data)
|
|
{
|
|
GtkDialog *chooser = user_data;
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
LocaleInfo *info;
|
|
gboolean is_extra;
|
|
const gchar *source_name;
|
|
|
|
if (row == priv->more_row)
|
|
return !priv->showing_extra;
|
|
|
|
is_extra = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-extra"));
|
|
|
|
if (!priv->showing_extra && is_extra)
|
|
return FALSE;
|
|
|
|
if (!priv->filter_words)
|
|
return TRUE;
|
|
|
|
info = g_object_get_data (G_OBJECT (row), "locale-info");
|
|
|
|
if (match_all (priv->filter_words, info->unaccented_name))
|
|
return TRUE;
|
|
|
|
if (match_all (priv->filter_words, info->untranslated_name))
|
|
return TRUE;
|
|
|
|
source_name = g_object_get_data (G_OBJECT (row), "unaccented-name");
|
|
if (source_name && match_all (priv->filter_words, source_name))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
update_header_filter (GtkListBoxRow *row,
|
|
GtkListBoxRow *before,
|
|
gpointer user_data)
|
|
{
|
|
LocaleInfo *row_info = NULL;
|
|
LocaleInfo *before_info = NULL;
|
|
GtkWidget *current;
|
|
|
|
if (row)
|
|
row_info = g_object_get_data (G_OBJECT (row), "locale-info");
|
|
|
|
if (before)
|
|
before_info = g_object_get_data (G_OBJECT (before), "locale-info");
|
|
|
|
if (!row_info && !before_info)
|
|
return;
|
|
|
|
current = gtk_list_box_row_get_header (row);
|
|
if (row_info == before_info)
|
|
{
|
|
/* Create a regular separator if we don't have one */
|
|
if (current && !GTK_IS_SEPARATOR (current))
|
|
{
|
|
gtk_list_box_row_set_header (row, NULL);
|
|
current = NULL;
|
|
}
|
|
|
|
if (current == NULL)
|
|
gtk_list_box_row_set_header (row,
|
|
gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
|
|
}
|
|
else
|
|
{
|
|
/* Create a locale heading separator if we don't have one */
|
|
if (current && GTK_IS_SEPARATOR (current))
|
|
{
|
|
gtk_list_box_row_set_header (row, NULL);
|
|
current = NULL;
|
|
}
|
|
|
|
if (current == NULL)
|
|
gtk_list_box_row_set_header (row,
|
|
locale_header_widget_new (row_info->name));
|
|
}
|
|
}
|
|
|
|
static void
|
|
show_filter_widgets (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
LocaleInfo *info;
|
|
GHashTableIter iter;
|
|
|
|
remove_all_children (GTK_CONTAINER (priv->list));
|
|
|
|
g_hash_table_iter_init (&iter, priv->locales);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
|
|
add_input_source_rows_for_locale (chooser, info);
|
|
|
|
gtk_widget_show_all (priv->list);
|
|
|
|
gtk_adjustment_set_value (priv->adjustment,
|
|
gtk_adjustment_get_lower (priv->adjustment));
|
|
gtk_list_box_set_header_func (GTK_LIST_BOX (priv->list),
|
|
update_header_filter, NULL, NULL);
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (priv->list), GTK_SELECTION_SINGLE);
|
|
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (priv->list), FALSE);
|
|
|
|
if (gtk_widget_is_visible (priv->filter_entry) &&
|
|
!gtk_widget_is_focus (priv->filter_entry))
|
|
gtk_widget_grab_focus (priv->filter_entry);
|
|
}
|
|
|
|
static gboolean
|
|
strvs_differ (gchar **av,
|
|
gchar **bv)
|
|
{
|
|
gchar **a, **b;
|
|
|
|
for (a = av, b = bv; *a && *b; ++a, ++b)
|
|
if (!g_str_equal (*a, *b))
|
|
return TRUE;
|
|
|
|
if (*a == NULL && *b == NULL)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
do_filter (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
gboolean was_filtering;
|
|
gchar **previous_words;
|
|
gchar *filter_contents = NULL;
|
|
|
|
priv->filter_timeout_id = 0;
|
|
|
|
previous_words = priv->filter_words;
|
|
was_filtering = previous_words != NULL;
|
|
|
|
filter_contents =
|
|
cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (priv->filter_entry)));
|
|
|
|
if (filter_contents)
|
|
{
|
|
priv->filter_words = g_strsplit_set (g_strstrip (filter_contents), " ", 0);
|
|
g_free (filter_contents);
|
|
}
|
|
|
|
if (!priv->filter_words || !priv->filter_words[0])
|
|
{
|
|
g_clear_pointer (&priv->filter_words, g_strfreev);
|
|
if (was_filtering)
|
|
show_locale_rows (chooser);
|
|
gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->list), NULL);
|
|
}
|
|
else
|
|
{
|
|
if (!was_filtering)
|
|
show_filter_widgets (chooser);
|
|
else if (strvs_differ (priv->filter_words, previous_words))
|
|
{
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
|
|
gtk_list_box_set_placeholder (GTK_LIST_BOX (priv->list), priv->no_results);
|
|
}
|
|
}
|
|
|
|
g_strfreev (previous_words);
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
static void
|
|
filter_changed (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
|
|
if (priv->filter_timeout_id == 0)
|
|
priv->filter_timeout_id = g_timeout_add (FILTER_TIMEOUT, (GSourceFunc) do_filter, chooser);
|
|
}
|
|
|
|
static void
|
|
show_more (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
|
|
set_fixed_size (chooser);
|
|
|
|
gtk_widget_show (priv->filter_entry);
|
|
gtk_widget_grab_focus (priv->filter_entry);
|
|
|
|
priv->showing_extra = TRUE;
|
|
|
|
gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->list));
|
|
}
|
|
|
|
static void
|
|
row_activated (GtkListBox *box,
|
|
GtkListBoxRow *row,
|
|
GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
gpointer data;
|
|
|
|
if (!row)
|
|
return;
|
|
|
|
if (row == priv->more_row)
|
|
{
|
|
show_more (chooser);
|
|
return;
|
|
}
|
|
|
|
data = g_object_get_data (G_OBJECT (row), "back");
|
|
if (data)
|
|
{
|
|
show_locale_rows (chooser);
|
|
return;
|
|
}
|
|
|
|
data = g_object_get_data (G_OBJECT (row), "name");
|
|
if (data)
|
|
{
|
|
gtk_dialog_response (GTK_DIALOG (chooser),
|
|
gtk_dialog_get_response_for_widget (GTK_DIALOG (chooser),
|
|
priv->add_button));
|
|
return;
|
|
}
|
|
|
|
data = g_object_get_data (G_OBJECT (row), "locale-info");
|
|
if (data)
|
|
{
|
|
show_input_sources_for_locale (chooser, (LocaleInfo *) data);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
selected_rows_changed (GtkListBox *box,
|
|
GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GtkListBoxRow *row;
|
|
gpointer data;
|
|
|
|
row = gtk_list_box_get_selected_row (box);
|
|
gtk_widget_set_sensitive (priv->add_button, row != NULL);
|
|
if (!row)
|
|
return;
|
|
|
|
data = g_object_get_data (G_OBJECT (row), "back");
|
|
if (data)
|
|
show_locale_rows (chooser);
|
|
}
|
|
|
|
static void
|
|
add_default_row (GtkWidget *chooser,
|
|
LocaleInfo *info,
|
|
const gchar *type,
|
|
const gchar *id)
|
|
{
|
|
info->default_input_source_row = input_source_row_new (chooser, type, id);
|
|
if (info->default_input_source_row)
|
|
{
|
|
g_object_ref_sink (info->default_input_source_row);
|
|
g_object_set_data (G_OBJECT (info->default_input_source_row), "default", GINT_TO_POINTER (TRUE));
|
|
g_object_set_data (G_OBJECT (info->default_input_source_row), "locale-info", info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_rows_to_table (GtkWidget *chooser,
|
|
LocaleInfo *info,
|
|
GList *list,
|
|
const gchar *type,
|
|
const gchar *default_id)
|
|
{
|
|
GHashTable *table;
|
|
GtkListBoxRow *row;
|
|
const gchar *id;
|
|
|
|
if (g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
|
|
table = info->layout_rows_by_id;
|
|
else if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS))
|
|
table = info->engine_rows_by_id;
|
|
else
|
|
return;
|
|
|
|
while (list)
|
|
{
|
|
id = (const gchar *) list->data;
|
|
|
|
/* The widget for the default input source lives elsewhere */
|
|
if (g_strcmp0 (id, default_id))
|
|
{
|
|
row = input_source_row_new (chooser, type, id);
|
|
if (row)
|
|
{
|
|
g_object_set_data (G_OBJECT (row), "locale-info", info);
|
|
g_hash_table_replace (table, (gpointer) id, g_object_ref_sink (row));
|
|
}
|
|
}
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_row (GtkWidget *chooser,
|
|
LocaleInfo *info,
|
|
const gchar *type,
|
|
const gchar *id)
|
|
{
|
|
GList tmp = { 0 };
|
|
tmp.data = (gpointer) id;
|
|
add_rows_to_table (chooser, info, &tmp, type, NULL);
|
|
}
|
|
|
|
static void
|
|
add_row_other (GtkWidget *chooser,
|
|
const gchar *type,
|
|
const gchar *id)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
LocaleInfo *info = g_hash_table_lookup (priv->locales, "");
|
|
add_row (chooser, info, type, id);
|
|
}
|
|
|
|
#ifdef HAVE_IBUS
|
|
static gboolean
|
|
maybe_set_as_default (GtkWidget *chooser,
|
|
LocaleInfo *info,
|
|
const gchar *engine_id)
|
|
{
|
|
const gchar *type, *id;
|
|
|
|
if (!gnome_get_input_source_from_locale (info->id, &type, &id))
|
|
return FALSE;
|
|
|
|
if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS) &&
|
|
g_str_equal (id, engine_id) &&
|
|
info->default_input_source_row == NULL)
|
|
{
|
|
add_default_row (chooser, info, type, id);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
get_ibus_locale_infos (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GHashTableIter iter;
|
|
LocaleInfo *info;
|
|
const gchar *engine_id;
|
|
IBusEngineDesc *engine;
|
|
|
|
if (!priv->ibus_engines || priv->is_login)
|
|
return;
|
|
|
|
g_hash_table_iter_init (&iter, priv->ibus_engines);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &engine_id, (gpointer *) &engine))
|
|
{
|
|
gchar *lang_code = NULL;
|
|
gchar *country_code = NULL;
|
|
const gchar *ibus_locale = ibus_engine_desc_get_language (engine);
|
|
|
|
if (gnome_parse_locale (ibus_locale, &lang_code, &country_code, NULL, NULL) &&
|
|
lang_code != NULL &&
|
|
country_code != NULL)
|
|
{
|
|
gchar *locale = g_strdup_printf ("%s_%s.UTF-8", lang_code, country_code);
|
|
|
|
info = g_hash_table_lookup (priv->locales, locale);
|
|
if (info)
|
|
{
|
|
const gchar *type, *id;
|
|
|
|
if (gnome_get_input_source_from_locale (locale, &type, &id) &&
|
|
g_str_equal (type, INPUT_SOURCE_TYPE_IBUS) &&
|
|
g_str_equal (id, engine_id))
|
|
{
|
|
add_default_row (chooser, info, type, id);
|
|
}
|
|
else
|
|
{
|
|
add_row (chooser, info, INPUT_SOURCE_TYPE_IBUS, engine_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
add_row_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
|
|
}
|
|
|
|
g_free (locale);
|
|
}
|
|
else if (lang_code != NULL)
|
|
{
|
|
GHashTableIter iter;
|
|
GHashTable *locales_for_language;
|
|
gchar *language;
|
|
|
|
/* Most IBus engines only specify the language so we try to
|
|
add them to all locales for that language. */
|
|
|
|
language = gnome_get_language_from_code (lang_code, NULL);
|
|
if (language)
|
|
locales_for_language = g_hash_table_lookup (priv->locales_by_language, language);
|
|
else
|
|
locales_for_language = NULL;
|
|
g_free (language);
|
|
|
|
if (locales_for_language)
|
|
{
|
|
g_hash_table_iter_init (&iter, locales_for_language);
|
|
while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL))
|
|
if (!maybe_set_as_default (chooser, info, engine_id))
|
|
add_row (chooser, info, INPUT_SOURCE_TYPE_IBUS, engine_id);
|
|
}
|
|
else
|
|
{
|
|
add_row_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
add_row_other (chooser, INPUT_SOURCE_TYPE_IBUS, engine_id);
|
|
}
|
|
|
|
g_free (country_code);
|
|
g_free (lang_code);
|
|
}
|
|
}
|
|
#endif /* HAVE_IBUS */
|
|
|
|
static void
|
|
add_locale_to_table (GHashTable *table,
|
|
const gchar *lang_code,
|
|
LocaleInfo *info)
|
|
{
|
|
GHashTable *set;
|
|
gchar *language;
|
|
|
|
language = gnome_get_language_from_code (lang_code, NULL);
|
|
|
|
set = g_hash_table_lookup (table, language);
|
|
if (!set)
|
|
{
|
|
set = g_hash_table_new (NULL, NULL);
|
|
g_hash_table_replace (table, g_strdup (language), set);
|
|
}
|
|
g_hash_table_add (set, info);
|
|
|
|
g_free (language);
|
|
}
|
|
|
|
static void
|
|
add_ids_to_set (GHashTable *set,
|
|
GList *list)
|
|
{
|
|
while (list)
|
|
{
|
|
g_hash_table_add (set, list->data);
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_locale_infos (GtkWidget *chooser)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GHashTable *layouts_with_locale;
|
|
LocaleInfo *info;
|
|
gchar **locale_ids;
|
|
gchar **locale;
|
|
GList *list, *l;
|
|
|
|
priv->locales = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, locale_info_free);
|
|
priv->locales_by_language = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, (GDestroyNotify) g_hash_table_destroy);
|
|
|
|
layouts_with_locale = g_hash_table_new (g_str_hash, g_str_equal);
|
|
|
|
locale_ids = gnome_get_all_locales ();
|
|
for (locale = locale_ids; *locale; ++locale)
|
|
{
|
|
gchar *lang_code, *country_code;
|
|
gchar *simple_locale;
|
|
gchar *tmp;
|
|
const gchar *type = NULL;
|
|
const gchar *id = NULL;
|
|
|
|
if (!gnome_parse_locale (*locale, &lang_code, &country_code, NULL, NULL))
|
|
continue;
|
|
|
|
if (country_code != NULL)
|
|
simple_locale = g_strdup_printf ("%s_%s.UTF-8", lang_code, country_code);
|
|
else
|
|
simple_locale = g_strdup_printf ("%s.UTF-8", lang_code);
|
|
|
|
if (g_hash_table_contains (priv->locales, simple_locale))
|
|
{
|
|
g_free (simple_locale);
|
|
g_free (country_code);
|
|
g_free (lang_code);
|
|
continue;
|
|
}
|
|
|
|
info = g_new0 (LocaleInfo, 1);
|
|
info->id = simple_locale; /* Take ownership */
|
|
info->name = gnome_get_language_from_locale (simple_locale, NULL);
|
|
info->unaccented_name = cc_util_normalize_casefold_and_unaccent (info->name);
|
|
tmp = gnome_get_language_from_locale (simple_locale, "C");
|
|
info->untranslated_name = cc_util_normalize_casefold_and_unaccent (tmp);
|
|
g_free (tmp);
|
|
|
|
g_hash_table_replace (priv->locales, simple_locale, info);
|
|
add_locale_to_table (priv->locales_by_language, lang_code, info);
|
|
|
|
if (gnome_get_input_source_from_locale (simple_locale, &type, &id) &&
|
|
g_str_equal (type, INPUT_SOURCE_TYPE_XKB))
|
|
{
|
|
add_default_row (chooser, info, type, id);
|
|
g_hash_table_add (layouts_with_locale, (gpointer) id);
|
|
}
|
|
|
|
/* We don't own these ids */
|
|
info->layout_rows_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, g_object_unref);
|
|
info->engine_rows_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, g_object_unref);
|
|
|
|
list = gnome_xkb_info_get_layouts_for_language (priv->xkb_info, lang_code);
|
|
add_rows_to_table (chooser, info, list, INPUT_SOURCE_TYPE_XKB, id);
|
|
add_ids_to_set (layouts_with_locale, list);
|
|
g_list_free (list);
|
|
|
|
if (country_code != NULL)
|
|
{
|
|
list = gnome_xkb_info_get_layouts_for_country (priv->xkb_info, country_code);
|
|
add_rows_to_table (chooser, info, list, INPUT_SOURCE_TYPE_XKB, id);
|
|
add_ids_to_set (layouts_with_locale, list);
|
|
g_list_free (list);
|
|
}
|
|
|
|
g_free (lang_code);
|
|
g_free (country_code);
|
|
}
|
|
g_strfreev (locale_ids);
|
|
|
|
/* Add a "Other" locale to hold the remaining input sources */
|
|
info = g_new0 (LocaleInfo, 1);
|
|
info->id = g_strdup ("");
|
|
info->name = g_strdup (C_("Input Source", "Other"));
|
|
info->unaccented_name = g_strdup ("");
|
|
info->untranslated_name = g_strdup ("");
|
|
g_hash_table_replace (priv->locales, info->id, info);
|
|
|
|
info->layout_rows_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, g_object_unref);
|
|
info->engine_rows_by_id = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
NULL, g_object_unref);
|
|
|
|
list = gnome_xkb_info_get_all_layouts (priv->xkb_info);
|
|
for (l = list; l; l = l->next)
|
|
if (!g_hash_table_contains (layouts_with_locale, l->data))
|
|
add_row_other (chooser, INPUT_SOURCE_TYPE_XKB, l->data);
|
|
|
|
g_list_free (list);
|
|
|
|
g_hash_table_destroy (layouts_with_locale);
|
|
}
|
|
|
|
static void
|
|
cc_input_chooser_private_free (gpointer data)
|
|
{
|
|
CcInputChooserPrivate *priv = data;
|
|
|
|
g_object_unref (priv->more_row);
|
|
g_object_unref (priv->no_results);
|
|
g_hash_table_destroy (priv->locales);
|
|
g_hash_table_destroy (priv->locales_by_language);
|
|
g_strfreev (priv->filter_words);
|
|
if (priv->filter_timeout_id)
|
|
g_source_remove (priv->filter_timeout_id);
|
|
g_free (priv);
|
|
}
|
|
|
|
GtkWidget *
|
|
cc_input_chooser_new (GtkWindow *main_window,
|
|
gboolean is_login,
|
|
GnomeXkbInfo *xkb_info,
|
|
GHashTable *ibus_engines)
|
|
{
|
|
GtkBuilder *builder;
|
|
GtkWidget *chooser;
|
|
CcInputChooserPrivate *priv;
|
|
gint width;
|
|
GError *error = NULL;
|
|
|
|
builder = gtk_builder_new ();
|
|
if (gtk_builder_add_from_resource (builder, "/org/gnome/control-center/region/input-chooser.ui", &error) == 0)
|
|
{
|
|
g_object_unref (builder);
|
|
g_warning ("failed to load input chooser: %s", error->message);
|
|
g_error_free (error);
|
|
return NULL;
|
|
}
|
|
chooser = WID ("input-dialog");
|
|
priv = g_new0 (CcInputChooserPrivate, 1);
|
|
g_object_set_data_full (G_OBJECT (chooser), "private", priv, cc_input_chooser_private_free);
|
|
g_object_set_data_full (G_OBJECT (chooser), "builder", builder, g_object_unref);
|
|
|
|
priv->is_login = is_login;
|
|
priv->xkb_info = xkb_info;
|
|
priv->ibus_engines = ibus_engines;
|
|
|
|
priv->add_button = WID ("add-button");
|
|
priv->filter_entry = WID ("filter-entry");
|
|
priv->list = WID ("list");
|
|
priv->scrolledwindow = WID ("scrolledwindow");
|
|
priv->adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolledwindow));
|
|
|
|
priv->more_row = g_object_ref_sink (more_row_new ());
|
|
priv->no_results = g_object_ref_sink (no_results_widget_new ());
|
|
gtk_widget_show_all (priv->no_results);
|
|
|
|
gtk_list_box_set_adjustment (GTK_LIST_BOX (priv->list), priv->adjustment);
|
|
gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->list), list_filter, chooser, NULL);
|
|
gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->list), (GtkListBoxSortFunc)list_sort, chooser, NULL);
|
|
g_signal_connect (priv->list, "row-activated", G_CALLBACK (row_activated), chooser);
|
|
g_signal_connect (priv->list, "selected-rows-changed", G_CALLBACK (selected_rows_changed), chooser);
|
|
|
|
g_signal_connect_swapped (priv->filter_entry, "search-changed", G_CALLBACK (filter_changed), chooser);
|
|
|
|
if (priv->is_login)
|
|
gtk_widget_show (WID ("login-label"));
|
|
|
|
get_locale_infos (chooser);
|
|
#ifdef HAVE_IBUS
|
|
get_ibus_locale_infos (chooser);
|
|
#endif /* HAVE_IBUS */
|
|
show_locale_rows (chooser);
|
|
|
|
/* Try to come up with a sensible width */
|
|
gtk_window_get_size (main_window, &width, NULL);
|
|
gtk_widget_set_size_request (chooser, width * MAIN_WINDOW_WIDTH_RATIO, -1);
|
|
gtk_window_set_resizable (GTK_WINDOW (chooser), TRUE);
|
|
|
|
gtk_window_set_transient_for (GTK_WINDOW (chooser), main_window);
|
|
|
|
return chooser;
|
|
}
|
|
|
|
void
|
|
cc_input_chooser_set_ibus_engines (GtkWidget *chooser,
|
|
GHashTable *ibus_engines)
|
|
{
|
|
#ifdef HAVE_IBUS
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
|
|
/* This should only be called once when IBus shows up in case it
|
|
wasn't up yet when the user opened the input chooser dialog. */
|
|
g_return_if_fail (priv->ibus_engines == NULL);
|
|
|
|
priv->ibus_engines = ibus_engines;
|
|
get_ibus_locale_infos (chooser);
|
|
show_locale_rows (chooser);
|
|
#endif /* HAVE_IBUS */
|
|
}
|
|
|
|
gboolean
|
|
cc_input_chooser_get_selected (GtkWidget *chooser,
|
|
gchar **type,
|
|
gchar **id,
|
|
gchar **name)
|
|
{
|
|
CcInputChooserPrivate *priv = GET_PRIVATE (chooser);
|
|
GtkListBoxRow *selected;
|
|
const gchar *t, *i, *n;
|
|
|
|
selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->list));
|
|
if (!selected)
|
|
return FALSE;
|
|
|
|
t = g_object_get_data (G_OBJECT (selected), "type");
|
|
i = g_object_get_data (G_OBJECT (selected), "id");
|
|
n = g_object_get_data (G_OBJECT (selected), "name");
|
|
|
|
if (!t || !i || !n)
|
|
return FALSE;
|
|
|
|
*type = g_strdup (t);
|
|
*id = g_strdup (i);
|
|
*name = g_strdup (n);
|
|
|
|
return TRUE;
|
|
}
|