4
* Copyright (C) 2011 Collabora Ltd.
5
* Copyright (C) 2010 Stefan Walter
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU Lesser General Public License as
9
* published by the Free Software Foundation; either version 2.1 of
10
* the License, or (at your option) any later version.
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22
* Author: Stef Walter <stefw@collabora.co.uk>
27
#include "gcr-collection-model.h"
28
#include "gcr-internal.h"
29
#include "gcr-list-selector.h"
30
#include "gcr-list-selector-private.h"
31
#include "gcr-live-search.h"
33
#include <glib/gi18n-lib.h>
38
* SECTION:gcr-list-selector
39
* @title: GcrListSelector
40
* @short_description: A selector widget to one or more certificates from a list.
42
* The #GcrListSelector can be used to select one or more certificates or keys.
43
* Live search is available for quick filtering.
48
* @parent: Parent object
50
* A list selector widget.
54
* GcrListSelectorClass:
56
* The class for #GcrListSelector.
64
struct _GcrListSelectorPrivate {
65
GcrCollection *collection;
66
GcrCollectionModel *model;
68
GtkTreeModelFilter *filter;
69
GtkWidget *search_widget;
72
G_DEFINE_TYPE (GcrListSelector, gcr_list_selector, GTK_TYPE_TREE_VIEW);
75
object_is_visible (GcrListSelector *self, GObject *object)
80
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), "search-text"))
81
g_object_get (object, "search-text", &text, NULL);
83
g_object_get (object, "label", &text, NULL);
85
visible = _gcr_live_search_match (GCR_LIVE_SEARCH (self->pv->search_widget), text);
92
on_tree_filter_visible_func (GtkTreeModel *model, GtkTreeIter *iter,
95
GcrListSelector *self = GCR_LIST_SELECTOR (user_data);
98
if (self->pv->search_widget == NULL ||
99
!gtk_widget_get_visible (self->pv->search_widget))
102
object = gcr_collection_model_object_for_iter (self->pv->model, iter);
104
return object_is_visible (self, object);
110
on_tree_view_start_search (GtkTreeView *view, gpointer user_data)
112
GcrListSelector *self = GCR_LIST_SELECTOR (view);
114
if (self->pv->search_widget == NULL)
117
if (gtk_widget_get_visible (self->pv->search_widget))
118
gtk_widget_grab_focus (self->pv->search_widget);
120
gtk_widget_show (self->pv->search_widget);
126
on_search_widget_text_notify (GcrLiveSearch *search, GParamSpec *pspec,
129
GcrListSelector *self = GCR_LIST_SELECTOR (user_data);
131
GtkTreeViewColumn *focus_column;
135
gboolean set_cursor = FALSE;
138
gtk_tree_model_filter_refilter (self->pv->filter);
141
/* Set cursor on the first object. */
143
model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
144
gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &path, &focus_column);
147
path = gtk_tree_path_new_from_string ("0");
152
/* FIXME: Workaround for GTK bug #621651, we have to make sure
153
* the path is valid. */
154
if (gtk_tree_model_get_iter (model, &iter, path)) {
155
gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path,
156
focus_column, FALSE);
160
gtk_tree_path_free (path);
165
on_search_widget_activate (GtkWidget *search, gpointer user_data)
167
GcrListSelector *self = GCR_LIST_SELECTOR (user_data);
169
GtkTreeViewColumn *focus_column;
171
gtk_tree_view_get_cursor (GTK_TREE_VIEW (self), &path, &focus_column);
173
gtk_tree_view_row_activated (GTK_TREE_VIEW (self), path, focus_column);
174
gtk_tree_path_free (path);
176
gtk_widget_hide (search);
181
on_search_widget_key_navigation (GtkWidget *search, GdkEvent *event, gpointer user_data)
183
GcrListSelector *self = GCR_LIST_SELECTOR (user_data);
185
gboolean ret = FALSE;
187
new_event = gdk_event_copy (event);
188
gtk_widget_grab_focus (GTK_WIDGET (self));
189
ret = gtk_widget_event (GTK_WIDGET (self), new_event);
190
gtk_widget_grab_focus (search);
192
gdk_event_free (new_event);
198
on_check_column_toggled (GtkCellRendererToggle *cell, gchar *path, gpointer user_data)
200
GcrListSelector *self = GCR_LIST_SELECTOR (user_data);
201
GtkTreeIter iter, model_iter;
203
g_assert (path != NULL);
205
if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (self->pv->filter), &iter, path)) {
206
gtk_tree_model_filter_convert_iter_to_child_iter (self->pv->filter, &model_iter, &iter);
207
gcr_collection_model_toggle_selected (self->pv->model, &model_iter);
212
gcr_list_selector_constructed (GObject *object)
214
GcrListSelector *self = GCR_LIST_SELECTOR (object);
215
GtkCellRenderer *cell;
216
GtkTreeViewColumn *column;
219
G_OBJECT_CLASS (gcr_list_selector_parent_class)->constructed (object);
221
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self), FALSE);
223
self->pv->model = gcr_collection_model_new (self->pv->collection,
225
"markup", G_TYPE_STRING,
228
self->pv->filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (
229
GTK_TREE_MODEL (self->pv->model), NULL));
230
gtk_tree_model_filter_set_visible_func (self->pv->filter,
231
on_tree_filter_visible_func, self, NULL);
233
gtk_tree_view_set_model (GTK_TREE_VIEW (self), GTK_TREE_MODEL (self->pv->filter));
237
cell = gtk_cell_renderer_toggle_new ();
238
g_signal_connect (cell, "toggled", G_CALLBACK (on_check_column_toggled), self);
240
column_id = gcr_collection_model_column_for_selected (self->pv->model);
241
column = gtk_tree_view_column_new_with_attributes ("", cell, "active", column_id, NULL);
242
gtk_tree_view_column_set_resizable (column, FALSE);
243
gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
245
column = gtk_tree_view_column_new ();
248
cell = gtk_cell_renderer_pixbuf_new ();
249
g_object_set (cell, "stock-size", GTK_ICON_SIZE_DND, NULL);
250
gtk_tree_view_column_pack_start (column, cell, FALSE);
251
gtk_tree_view_column_add_attribute (column, cell, "gicon", 0);
254
cell = gtk_cell_renderer_text_new ();
255
gtk_tree_view_column_pack_start (column, cell, TRUE);
256
gtk_tree_view_column_add_attribute (column, cell, "markup", 1);
258
gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
262
gcr_list_selector_init (GcrListSelector *self)
264
self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_LIST_SELECTOR, GcrListSelectorPrivate);
268
gcr_list_selector_dispose (GObject *obj)
270
GcrListSelector *self = GCR_LIST_SELECTOR (obj);
272
if (self->pv->filter)
273
g_object_unref (self->pv->filter);
274
self->pv->filter = NULL;
277
g_object_unref (self->pv->model);
278
self->pv->model = NULL;
280
if (self->pv->collection)
281
g_object_unref (self->pv->collection);
282
self->pv->collection = NULL;
284
_gcr_list_selector_set_live_search (self, NULL);
286
G_OBJECT_CLASS (gcr_list_selector_parent_class)->dispose (obj);
290
gcr_list_selector_finalize (GObject *obj)
292
GcrListSelector *self = GCR_LIST_SELECTOR (obj);
294
g_assert (!self->pv->collection);
295
g_assert (!self->pv->model);
297
G_OBJECT_CLASS (gcr_list_selector_parent_class)->finalize (obj);
301
gcr_list_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
304
GcrListSelector *self = GCR_LIST_SELECTOR (obj);
307
case PROP_COLLECTION:
308
g_return_if_fail (!self->pv->collection);
309
self->pv->collection = g_value_dup_object (value);
310
g_return_if_fail (self->pv->collection);
313
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
319
gcr_list_selector_get_property (GObject *obj, guint prop_id, GValue *value,
322
GcrListSelector *self = GCR_LIST_SELECTOR (obj);
325
case PROP_COLLECTION:
326
g_value_set_object (value, gcr_list_selector_get_collection (self));
329
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
335
gcr_list_selector_class_init (GcrListSelectorClass *klass)
337
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
339
gobject_class->constructed = gcr_list_selector_constructed;
340
gobject_class->dispose = gcr_list_selector_dispose;
341
gobject_class->finalize = gcr_list_selector_finalize;
342
gobject_class->set_property = gcr_list_selector_set_property;
343
gobject_class->get_property = gcr_list_selector_get_property;
345
g_type_class_add_private (gobject_class, sizeof (GcrListSelectorPrivate));
348
* GcrListSelector:collection:
350
* The collection which contains the objects to display in the selector.
352
g_object_class_install_property (gobject_class, PROP_COLLECTION,
353
g_param_spec_object ("collection", "Collection", "Collection to select from",
354
GCR_TYPE_COLLECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
359
/* -----------------------------------------------------------------------------
365
* @collection: The collection that contains the objects to display
367
* Create a new #GcrTreeSelector.
369
* Returns: A newly allocated selector, which should be released with
373
gcr_list_selector_new (GcrCollection *collection)
375
return g_object_new (GCR_TYPE_LIST_SELECTOR,
376
"collection", collection,
381
* gcr_list_selector_get_collection:
382
* @self: The selector
384
* Get the collection that this selector is displaying objects from.
386
* Returns: The collection, owned by the selector.
389
gcr_list_selector_get_collection (GcrListSelector *self)
391
g_return_val_if_fail (GCR_IS_LIST_SELECTOR (self), NULL);
392
return self->pv->collection;
396
* gcr_list_selector_get_selected:
397
* @self: The selector
399
* Get a list of selected objects.
401
* Returns: The list of selected objects, to be released with g_list_free().
404
gcr_list_selector_get_selected (GcrListSelector *self)
406
g_return_val_if_fail (GCR_IS_LIST_SELECTOR (self), NULL);
407
return gcr_collection_model_get_selected_objects (self->pv->model);
411
* gcr_list_selector_set_selected:
412
* @self: The selector
413
* @selected: The list of objects to select.
415
* Select certain objects in the selector.
418
gcr_list_selector_set_selected (GcrListSelector *self, GList *selected)
420
g_return_if_fail (GCR_IS_LIST_SELECTOR (self));
421
gcr_collection_model_set_selected_objects (self->pv->model, selected);
426
_gcr_list_selector_set_live_search (GcrListSelector *self, GcrLiveSearch *search)
428
g_return_if_fail (GCR_IS_LIST_SELECTOR (self));
430
/* remove old handlers if old search was not null */
431
if (self->pv->search_widget != NULL) {
432
g_signal_handlers_disconnect_by_func (self, on_tree_view_start_search, NULL);
434
g_signal_handlers_disconnect_by_func (self->pv->search_widget,
435
on_search_widget_text_notify, self);
436
g_signal_handlers_disconnect_by_func (self->pv->search_widget,
437
on_search_widget_activate, self);
438
g_signal_handlers_disconnect_by_func (self->pv->search_widget,
439
on_search_widget_key_navigation, self);
440
g_object_unref (self->pv->search_widget);
441
self->pv->search_widget = NULL;
444
/* connect handlers if new search is not null */
445
if (search != NULL) {
446
self->pv->search_widget = g_object_ref (search);
448
g_signal_connect (self, "start-interactive-search",
449
G_CALLBACK (on_tree_view_start_search), NULL);
451
g_signal_connect (self->pv->search_widget, "notify::text",
452
G_CALLBACK (on_search_widget_text_notify), self);
453
g_signal_connect (self->pv->search_widget, "activate",
454
G_CALLBACK (on_search_widget_activate), self);
455
g_signal_connect (self->pv->search_widget, "key-navigation",
456
G_CALLBACK (on_search_widget_key_navigation), self);