[gnome-keyring: 1/2] gcr: Split GcrSelector into GcrComboSelector and GcrTreeSelector



commit 7b5e4e3c1e8cf94a03f1bd21eacb53991b32f6c6
Author: Stef Walter <stefw collabora co uk>
Date:   Wed May 11 20:38:26 2011 +0200

    gcr: Split GcrSelector into GcrComboSelector and GcrTreeSelector
    
     * GcrComboSelector allows selecting one object at a time
       and uses 'markup' property to display the object.
     * GcrTreeSelector uses various columns, and a checkbox so that multiple
       objects can be selected at once.
     * Add images for both of the above.
     * Derive directly from GtkComboBox and GtkTreeView respectively.

 .gitignore                                         |    2 +
 docs/reference/gcr/Makefile.am                     |   10 +-
 docs/reference/gcr/gcr-docs.sgml                   |    4 +-
 docs/reference/gcr/gcr-sections.txt                |   60 +++--
 docs/reference/gcr/gcr-visual-index.xml            |    6 +
 docs/reference/gcr/gcr-widgets.c                   |   99 ++++++-
 docs/reference/gcr/images/combo-selector.png       |  Bin 0 -> 7855 bytes
 docs/reference/gcr/images/key-widget.png           |  Bin 8634 -> 8311 bytes
 docs/reference/gcr/images/tree-selector.png        |  Bin 0 -> 11930 bytes
 gcr/Makefile.am                                    |    6 +-
 gcr/gcr-certificate.c                              |    2 +-
 gcr/gcr-collection-model.c                         |   73 +++++-
 gcr/gcr-collection-model.h                         |    9 +-
 gcr/gcr-column.c                                   |    4 +-
 gcr/gcr-combo-selector.c                           |  272 +++++++++++++++++++
 gcr/gcr-combo-selector.h                           |   69 +++++
 gcr/gcr-selector.h                                 |   69 -----
 gcr/{gcr-selector.c => gcr-tree-selector.c}        |  280 ++++++-------------
 gcr/gcr-tree-selector.h                            |   70 +++++
 gcr/gcr.h                                          |    3 +-
 gcr/tests/Makefile.am                              |    5 +-
 .../{frob-selector.c => frob-combo-selector.c}     |   15 +-
 gcr/tests/frob-gnupg-selector.c                    |   14 +-
 .../{frob-selector.c => frob-tree-selector.c}      |   24 ++-
 24 files changed, 774 insertions(+), 322 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index f552444..1047213 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,6 +114,8 @@ run-tests
 /gcr/tests/frob-key
 /gcr/tests/frob-gnupg-selector
 /gcr/tests/frob-selector
+/gcr/tests/frob-combo-selector
+/gcr/tests/frob-tree-selector
 /gcr/tests/frob-unlock-options
 /gcr/tests/frob-parser
 /gcr/tests/test-certificate
diff --git a/docs/reference/gcr/Makefile.am b/docs/reference/gcr/Makefile.am
index fce4d4a..09b5fe4 100644
--- a/docs/reference/gcr/Makefile.am
+++ b/docs/reference/gcr/Makefile.am
@@ -8,7 +8,7 @@ AUTOMAKE_OPTIONS = 1.6
 # suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
 # of using the various options.
 
-# The name of the module, e.g. 'glib'.
+# The name the module, e.g. 'glib'.
 DOC_MODULE=gcr
 
 # Uncomment for versioned docs and specify the version of the module, e.g. '2'.
@@ -74,7 +74,9 @@ IGNORE_HFILES= \
 # e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
 HTML_IMAGES= \
 	$(srcdir)/images/certificate-widget.png \
-	$(srcdir)/images/key-widget.png
+	$(srcdir)/images/key-widget.png \
+	$(srcdir)/images/combo-selector.png \
+	$(srcdir)/images/tree-selector.png
 
 # Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
 # e.g. content_files=running.sgml building.sgml changes-2.0.sgml
@@ -116,7 +118,9 @@ DISTCLEANFILES = tmpl/gcr-unused.sgml
 
 WIDGETS = \
 	certificate-widget \
-	key-widget
+	key-widget \
+	combo-selector \
+	tree-selector
 
 shots: gcr-shooter
 	mkdir -p $(builddir)/images
diff --git a/docs/reference/gcr/gcr-docs.sgml b/docs/reference/gcr/gcr-docs.sgml
index d874ca1..6a8f182 100644
--- a/docs/reference/gcr/gcr-docs.sgml
+++ b/docs/reference/gcr/gcr-docs.sgml
@@ -30,10 +30,12 @@
 	</part>
 
 	<part id="widgets">
+		<title>Widgets</title>
 		<xi:include href="xml/gcr-certificate-widget.xml"/>
 		<xi:include href="xml/gcr-key-widget.xml"/>
 		<xi:include href="xml/gcr-column.xml"/>
-		<xi:include href="xml/gcr-selector.xml"/>
+		<xi:include href="xml/gcr-combo-selector.xml"/>
+		<xi:include href="xml/gcr-tree-selector.xml"/>
 		<xi:include href="xml/gcr-renderer.xml"/>
 		<xi:include href="xml/gcr-viewer.xml"/>
 		<chapter>
diff --git a/docs/reference/gcr/gcr-sections.txt b/docs/reference/gcr/gcr-sections.txt
index 2f652b9..84481e8 100644
--- a/docs/reference/gcr/gcr-sections.txt
+++ b/docs/reference/gcr/gcr-sections.txt
@@ -256,9 +256,11 @@ gcr_collection_model_new_full
 gcr_collection_model_set_columns
 gcr_collection_model_iter_for_object
 gcr_collection_model_object_for_iter
-gcr_collection_model_get_selected
-gcr_collection_model_set_selected
+gcr_collection_model_is_selected
+gcr_collection_model_change_selected
 gcr_collection_model_toggle_selected
+gcr_collection_model_get_selected_objects
+gcr_collection_model_set_selected_objects
 gcr_collection_model_column_for_selected
 <SUBSECTION Standard>
 gcr_collection_model_get_type
@@ -278,25 +280,43 @@ GcrColumnFlags
 </SECTION>
 
 <SECTION>
-<FILE>gcr-selector</FILE>
-GcrSelector
-GcrSelectorClass
-GcrSelectorMode
-gcr_selector_get_collection
-gcr_selector_get_columns
-gcr_selector_get_mode
-gcr_selector_new
+<FILE>gcr-tree-selector</FILE>
+GcrTreeSelector
+GcrTreeSelectorClass
+gcr_tree_selector_get_collection
+gcr_tree_selector_get_columns
+gcr_tree_selector_new
+gcr_tree_selector_get_selected
+gcr_tree_selector_set_selected
 <SUBSECTION Standard>
-gcr_selector_get_type
-gcr_selector_mode_get_type
-GCR_IS_SELECTOR
-GCR_IS_SELECTOR_CLASS
-GCR_SELECTOR
-GCR_SELECTOR_CLASS
-GCR_SELECTOR_GET_CLASS
-GCR_TYPE_SELECTOR
-GCR_TYPE_SELECTOR_MODE
-GcrSelectorPrivate
+gcr_tree_selector_get_type
+GCR_IS_TREE_SELECTOR
+GCR_IS_TREE_SELECTOR_CLASS
+GCR_TREE_SELECTOR
+GCR_TREE_SELECTOR_CLASS
+GCR_TREE_SELECTOR_GET_CLASS
+GCR_TYPE_TREE_SELECTOR
+GcrTreeSelectorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gcr-combo-selector</FILE>
+GcrComboSelector
+GcrComboSelectorClass
+gcr_combo_selector_get_collection
+gcr_combo_selector_get_columns
+gcr_combo_selector_new
+gcr_combo_selector_get_selected
+gcr_combo_selector_set_selected
+<SUBSECTION Standard>
+gcr_combo_selector_get_type
+GCR_IS_COMBO_SELECTOR
+GCR_IS_COMBO_SELECTOR_CLASS
+GCR_COMBO_SELECTOR
+GCR_COMBO_SELECTOR_CLASS
+GCR_COMBO_SELECTOR_GET_CLASS
+GCR_TYPE_COMBO_SELECTOR
+GcrComboSelectorPrivate
 </SECTION>
 
 <SECTION>
diff --git a/docs/reference/gcr/gcr-visual-index.xml b/docs/reference/gcr/gcr-visual-index.xml
index c3f43e8..e3136ec 100644
--- a/docs/reference/gcr/gcr-visual-index.xml
+++ b/docs/reference/gcr/gcr-visual-index.xml
@@ -9,4 +9,10 @@
   <link linkend="gcr-GcrKeyWidget">
     <inlinegraphic fileref="key-widget.png" format="PNG"></inlinegraphic>
   </link>
+  <link linkend="gcr-GcrComboSelector">
+    <inlinegraphic fileref="combo-selector.png" format="PNG"></inlinegraphic>
+  </link>
+  <link linkend="gcr-GcrTreeSelector">
+    <inlinegraphic fileref="tree-selector.png" format="PNG"></inlinegraphic>
+  </link>
 </para>
\ No newline at end of file
diff --git a/docs/reference/gcr/gcr-widgets.c b/docs/reference/gcr/gcr-widgets.c
index 30f08c3..9dbd89a 100644
--- a/docs/reference/gcr/gcr-widgets.c
+++ b/docs/reference/gcr/gcr-widgets.c
@@ -2,22 +2,30 @@
 #include "gcr-shooter.h"
 #include "gcr.h"
 
+static gpointer
+load_gcr_test_file (const gchar *name, gsize *length)
+{
+	GError *error = NULL;
+	gchar *contents;
+	gchar *filename;
+
+	filename = g_build_filename (TOPDIR, "gcr", "tests", "files", name, NULL);
+	if (!g_file_get_contents (filename, &contents, length, &error))
+		g_error ("couldn't read file: %s: %s", filename, error->message);
+	g_free (filename);
+	return contents;
+}
+
 static GcrShooterInfo *
 create_certificate_widget (const gchar *name)
 {
-	GError *error = NULL;
 	GcrCertificate *certificate;
 	GtkWidget *widget;
 	GtkWidget *align;
-	gchar *filename;
 	gchar *contents;
 	gsize length;
 
-	filename = g_build_filename (TOPDIR, "gcr", "tests", "files", "cacert.org.cer", NULL);
-	if (!g_file_get_contents (filename, &contents, &length, &error))
-		g_error ("couldn't read file: %s: %s", filename, error->message);
-	g_free (filename);
-
+	contents = load_gcr_test_file ("cacert.org.cer", &length);
 	certificate = gcr_simple_certificate_new (contents, length);
 	g_free (contents);
 
@@ -45,15 +53,10 @@ create_key_widget (const gchar *name)
 	GtkWidget *widget;
 	GtkWidget *align;
 	GcrParser *parser;
-	gchar *filename;
 	gchar *contents;
 	gsize length;
 
-	filename = g_build_filename (TOPDIR, "gcr", "tests", "files", "der-dsa-1024.key", NULL);
-	if (!g_file_get_contents (filename, &contents, &length, &error))
-		g_error ("couldn't read file: %s: %s", filename, error->message);
-	g_free (filename);
-
+	contents = load_gcr_test_file ("der-dsa-1024.key", &length);
 	parser = gcr_parser_new ();
 	g_signal_connect (parser, "parsed", G_CALLBACK (on_parser_key_parsed), &attrs);
 	if (!gcr_parser_parse_data (parser, contents, length, &error))
@@ -71,6 +74,72 @@ create_key_widget (const gchar *name)
 	return gcr_shooter_info_new (name, align, GCR_SHOOTER_LARGE);
 }
 
+static GcrShooterInfo *
+create_combo_selector (const gchar *name)
+{
+	GcrComboSelector *selector;
+	GcrCertificate *certificate;
+	GcrCollection *collection;
+	GtkWidget *align;
+	gchar *contents;
+	gsize length;
+
+	contents = load_gcr_test_file ("cacert.org.cer", &length);
+	certificate = gcr_simple_certificate_new (contents, length);
+	g_free (contents);
+
+	collection = gcr_simple_collection_new ();
+	gcr_simple_collection_add (GCR_SIMPLE_COLLECTION (collection), G_OBJECT (certificate));
+
+	selector = gcr_combo_selector_new (collection);
+	g_object_unref (collection);
+
+	gcr_combo_selector_set_selected (selector, G_OBJECT (certificate));
+	g_object_unref (certificate);
+
+	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (selector));
+
+	return gcr_shooter_info_new (name, align, GCR_SHOOTER_MEDIUM);
+}
+
+static GcrShooterInfo *
+create_tree_selector (const gchar *name)
+{
+	GcrTreeSelector *selector;
+	GcrCertificate *certificate;
+	GcrCollection *collection;
+	GtkWidget *align;
+	gchar *contents;
+	gsize length;
+	GList *selected = NULL;
+
+	collection = gcr_simple_collection_new ();
+	selector = gcr_tree_selector_new (collection, GCR_CERTIFICATE_COLUMNS);
+
+	contents = load_gcr_test_file ("cacert.org.cer", &length);
+	certificate = gcr_simple_certificate_new (contents, length);
+	g_free (contents);
+	gcr_simple_collection_add (GCR_SIMPLE_COLLECTION (collection), G_OBJECT (certificate));
+	selected = g_list_append (selected, certificate);
+	gcr_tree_selector_set_selected (selector, selected);
+	g_list_free (selected);
+	g_object_unref (certificate);
+
+	contents = load_gcr_test_file ("der-certificate-dsa.cer", &length);
+	certificate = gcr_simple_certificate_new (contents, length);
+	g_free (contents);
+	gcr_simple_collection_add (GCR_SIMPLE_COLLECTION (collection), G_OBJECT (certificate));
+	g_object_unref (certificate);
+
+	g_object_unref (collection);
+
+	align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (selector));
+
+	return gcr_shooter_info_new (name, align, GCR_SHOOTER_MEDIUM);
+}
+
 
 GcrShooterInfo*
 gcr_widgets_create (const gchar *name)
@@ -81,6 +150,10 @@ gcr_widgets_create (const gchar *name)
 		return create_certificate_widget (name);
 	else if (g_str_equal (name, "key-widget"))
 		return create_key_widget (name);
+	else if (g_str_equal (name, "combo-selector"))
+		return create_combo_selector (name);
+	else if (g_str_equal (name, "tree-selector"))
+		return create_tree_selector (name);
 
 	return NULL;
 }
diff --git a/docs/reference/gcr/images/combo-selector.png b/docs/reference/gcr/images/combo-selector.png
new file mode 100644
index 0000000..e398dd0
Binary files /dev/null and b/docs/reference/gcr/images/combo-selector.png differ
diff --git a/docs/reference/gcr/images/key-widget.png b/docs/reference/gcr/images/key-widget.png
index a0b6f59..253d4ac 100644
Binary files a/docs/reference/gcr/images/key-widget.png and b/docs/reference/gcr/images/key-widget.png differ
diff --git a/docs/reference/gcr/images/tree-selector.png b/docs/reference/gcr/images/tree-selector.png
new file mode 100644
index 0000000..e5e1320
Binary files /dev/null and b/docs/reference/gcr/images/tree-selector.png differ
diff --git a/gcr/Makefile.am b/gcr/Makefile.am
index bd20723..183f0a2 100644
--- a/gcr/Makefile.am
+++ b/gcr/Makefile.am
@@ -35,6 +35,7 @@ inc_HEADERS = \
 	gcr-collection.h \
 	gcr-collection-model.h \
 	gcr-column.h \
+	gcr-combo-selector.h \
 	gcr-comparable.h \
 	gcr-key-renderer.h \
 	gcr-key-widget.h \
@@ -43,9 +44,9 @@ inc_HEADERS = \
 	gcr-parser.h \
 	gcr-pkcs11-certificate.h \
 	gcr-renderer.h \
-	gcr-selector.h \
 	gcr-simple-certificate.h \
 	gcr-simple-collection.h \
+	gcr-tree-selector.h \
 	gcr-trust.h \
 	gcr-types.h \
 	gcr-unlock-options.h \
@@ -78,6 +79,7 @@ libgcr GCR_VERSION_SUFFIX@_la_SOURCES = \
 	gcr-collection.c gcr-collection.h \
 	gcr-collection-model.c gcr-collection-model.h \
 	gcr-colons.c gcr-colons.h \
+	gcr-combo-selector.c gcr-combo-selector.h \
 	gcr-debug.c gcr-debug.h \
 	gcr-display-scrolled.c gcr-display-scrolled.h \
 	gcr-comparable.c gcr-comparable.h \
@@ -94,9 +96,9 @@ libgcr GCR_VERSION_SUFFIX@_la_SOURCES = \
 	gcr-parser.c gcr-parser.h \
 	gcr-pkcs11-certificate.c gcr-pkcs11-certificate.h \
 	gcr-renderer.c gcr-renderer.h \
-	gcr-selector.c gcr-selector.h \
 	gcr-simple-certificate.c gcr-simple-certificate.h \
 	gcr-simple-collection.c gcr-simple-collection.h \
+	gcr-tree-selector.c gcr-tree-selector.h \
 	gcr-trust.c gcr-trust.h \
 	gcr-types.h \
 	gcr-unlock-options.h \
diff --git a/gcr/gcr-certificate.c b/gcr/gcr-certificate.c
index 8fddb65..0f4497e 100644
--- a/gcr/gcr-certificate.c
+++ b/gcr/gcr-certificate.c
@@ -76,7 +76,7 @@
  * GCR_CERTIFICATE_COLUMNS:
  *
  * The columns that are valid for a certificate. This is to be used with
- * the #GcrSelector or #GcrCollectionModel.
+ * the #GcrTreeSelector or #GcrCollectionModel.
  *
  * This is an array of #GcrColumn, owned by the gcr library.
  */
diff --git a/gcr/gcr-collection-model.c b/gcr/gcr-collection-model.c
index 3dcb8a4..d18611c 100644
--- a/gcr/gcr-collection-model.c
+++ b/gcr/gcr-collection-model.c
@@ -93,6 +93,12 @@ G_DEFINE_TYPE_WITH_CODE (GcrCollectionModel, gcr_collection_model, G_TYPE_OBJECT
 
 #define UNUSED_VALUE GINT_TO_POINTER (1)
 
+static GHashTable*
+selected_hash_table_new (void)
+{
+	return g_hash_table_new (g_direct_hash, g_direct_equal);
+}
+
 static gboolean
 column_id_is_valid_and_real (GcrCollectionModel *self, gint column_id)
 {
@@ -351,7 +357,7 @@ gcr_collection_model_real_get_value (GtkTreeModel *model, GtkTreeIter *iter,
 	/* The selected column? Last one */
 	if (column_id == self->pv->n_columns - 1) {
 		g_value_init (value, G_TYPE_BOOLEAN);
-		g_value_set_boolean (value, gcr_collection_model_get_selected (self, iter));
+		g_value_set_boolean (value, gcr_collection_model_is_selected (self, iter));
 		return;
 	}
 
@@ -633,6 +639,7 @@ gcr_collection_model_new (GcrCollection *collection, ...)
 
 	va_start (va, collection);
 	while ((arg = va_arg (va, const gchar*)) != NULL) {
+		memset (&column, 0, sizeof (column));
 		column.property_name = g_strdup (arg);
 		column.property_type = va_arg (va, GType);
 		column.column_type = column.property_type;
@@ -779,7 +786,7 @@ gcr_collection_model_toggle_selected (GcrCollectionModel *self, GtkTreeIter *ite
 	g_return_if_fail (G_IS_OBJECT (object));
 
 	if (!self->pv->selected)
-		self->pv->selected = g_hash_table_new (g_direct_hash, g_direct_equal);
+		self->pv->selected = selected_hash_table_new ();
 
 	if (g_hash_table_lookup (self->pv->selected, object))
 		g_hash_table_remove (self->pv->selected, object);
@@ -788,7 +795,7 @@ gcr_collection_model_toggle_selected (GcrCollectionModel *self, GtkTreeIter *ite
 }
 
 /**
- * gcr_collection_model_set_selected:
+ * gcr_collection_model_change_selected:
  * @self: The model
  * @iter: The row
  * @selected: Whether the row should be selected or not.
@@ -796,7 +803,7 @@ gcr_collection_model_toggle_selected (GcrCollectionModel *self, GtkTreeIter *ite
  * Set whether a given row is toggled selected or not.
  */
 void
-gcr_collection_model_set_selected (GcrCollectionModel *self, GtkTreeIter *iter, gboolean selected)
+gcr_collection_model_change_selected (GcrCollectionModel *self, GtkTreeIter *iter, gboolean selected)
 {
 	GtkTreePath *path;
 	GObject *object;
@@ -822,7 +829,7 @@ gcr_collection_model_set_selected (GcrCollectionModel *self, GtkTreeIter *iter,
 }
 
 /**
- * gcr_collection_model_get_selected:
+ * gcr_collection_model_is_selected:
  * @self: The model
  * @iter: The row
  *
@@ -831,7 +838,7 @@ gcr_collection_model_set_selected (GcrCollectionModel *self, GtkTreeIter *iter,
  * Returns: Whether the row has been selected.
  */
 gboolean
-gcr_collection_model_get_selected (GcrCollectionModel *self, GtkTreeIter *iter)
+gcr_collection_model_is_selected (GcrCollectionModel *self, GtkTreeIter *iter)
 {
 	GObject *object;
 
@@ -845,3 +852,57 @@ gcr_collection_model_get_selected (GcrCollectionModel *self, GtkTreeIter *iter)
 
 	return g_hash_table_lookup (self->pv->selected, object) ? TRUE : FALSE;
 }
+
+GList*
+gcr_collection_model_get_selected_objects (GcrCollectionModel *self)
+{
+	GHashTableIter iter;
+	GList *result = NULL;
+	gpointer key;
+
+	g_return_val_if_fail (GCR_IS_COLLECTION_MODEL (self), NULL);
+
+	if (!self->pv->selected)
+		return NULL;
+
+	g_hash_table_iter_init (&iter, self->pv->selected);
+	while (g_hash_table_iter_next (&iter, &key, NULL))
+		result = g_list_prepend (result, key);
+	return result;
+}
+
+void
+gcr_collection_model_set_selected_objects (GcrCollectionModel *self, GList *selected)
+{
+	GHashTable *newly_selected;
+	GList *old_selection;
+	GtkTreeIter iter;
+	GList *l;
+
+	old_selection = gcr_collection_model_get_selected_objects (self);
+	newly_selected = selected_hash_table_new ();
+
+	/* Select all the objects in selected which aren't already selected */
+	for (l = selected; l; l = g_list_next (l)) {
+		if (!self->pv->selected || !g_hash_table_lookup (self->pv->selected, l->data)) {
+			if (!gcr_collection_model_iter_for_object (self, l->data, &iter))
+				g_return_if_reached ();
+			gcr_collection_model_change_selected (self, &iter, TRUE);
+		}
+
+		/* Note that we've seen this one */
+		g_hash_table_insert (newly_selected, l->data, UNUSED_VALUE);
+	}
+
+	/* Unselect all the objects which aren't supposed to be selected */
+	for (l = old_selection; l; l = g_list_next (l)) {
+		if (!g_hash_table_lookup (newly_selected, l->data)) {
+			if (!gcr_collection_model_iter_for_object (self, l->data, &iter))
+				g_return_if_reached ();
+			gcr_collection_model_change_selected (self, &iter, FALSE);
+		}
+	}
+
+	g_list_free (old_selection);
+	g_hash_table_destroy (newly_selected);
+}
diff --git a/gcr/gcr-collection-model.h b/gcr/gcr-collection-model.h
index d1e95f6..c8a1775 100644
--- a/gcr/gcr-collection-model.h
+++ b/gcr/gcr-collection-model.h
@@ -72,11 +72,16 @@ gint                  gcr_collection_model_column_for_selected (GcrCollectionMod
 void                  gcr_collection_model_toggle_selected     (GcrCollectionModel *self,
                                                                 GtkTreeIter *iter);
 
-void                  gcr_collection_model_set_selected        (GcrCollectionModel *self,
+void                  gcr_collection_model_change_selected     (GcrCollectionModel *self,
                                                                 GtkTreeIter *iter,
                                                                 gboolean selected);
 
-gboolean              gcr_collection_model_get_selected        (GcrCollectionModel *self,
+gboolean              gcr_collection_model_is_selected         (GcrCollectionModel *self,
                                                                 GtkTreeIter *iter);
 
+GList*                gcr_collection_model_get_selected_objects  (GcrCollectionModel *self);
+
+void                  gcr_collection_model_set_selected_objects  (GcrCollectionModel *self,
+                                                                  GList *selected);
+
 #endif /* __GCR_COLLECTION_MODEL_H__ */
diff --git a/gcr/gcr-column.c b/gcr/gcr-column.c
index 2396d43..f23c95d 100644
--- a/gcr/gcr-column.c
+++ b/gcr/gcr-column.c
@@ -30,7 +30,7 @@
  * @title: GcrColumn
  * @short_description: Column information for selector or model.
  *
- * A #GcrColumn is used with #GcrSelector or #GcrCollectionModel to define
+ * A #GcrColumn is used with #GcrTreeSelector or #GcrCollectionModel to define
  * the columns to display.
  */
 
@@ -54,7 +54,7 @@
  *     are the same.
  * @user_data: User data associated with the column
  *
- * Represents a column to display in a #GcrCollectionModel or #GcrSelector.
+ * Represents a column to display in a #GcrCollectionModel or #GcrTreeSelector.
  *
  * The label should be set as a translatable string with a context of
  * <code>"column"</code>. This should be done with with this macro:
diff --git a/gcr/gcr-combo-selector.c b/gcr/gcr-combo-selector.c
new file mode 100644
index 0000000..f890a3c
--- /dev/null
+++ b/gcr/gcr-combo-selector.c
@@ -0,0 +1,272 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gcr-collection-model.h"
+#include "gcr-internal.h"
+#include "gcr-combo-selector.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <string.h>
+
+/**
+ * SECTION:gcr-combo-selector
+ * @title: GcrComboSelector
+ * @short_description: A selector widget to select a single certificate or key.
+ *
+ * The #GcrComboSelector can be used to select a certificate or key. It allows
+ * the user to select one object from the selector at a time.
+ */
+
+/**
+ * GcrComboSelector:
+ * @parent: Parent object
+ *
+ * A combo selector widget.
+ */
+
+/**
+ * GcrComboSelectorClass:
+ *
+ * The class for #GcrComboSelector.
+ */
+
+enum {
+	PROP_0,
+	PROP_COLLECTION
+};
+
+struct _GcrComboSelectorPrivate {
+	GcrCollection *collection;
+	GcrCollectionModel *model;
+};
+
+G_DEFINE_TYPE (GcrComboSelector, gcr_combo_selector, GTK_TYPE_COMBO_BOX);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
+
+static GObject*
+gcr_combo_selector_constructor (GType type, guint n_props, GObjectConstructParam *props)
+{
+	GcrComboSelector *self = GCR_COMBO_SELECTOR (G_OBJECT_CLASS (gcr_combo_selector_parent_class)->constructor(type, n_props, props));
+	GtkCellRenderer *cell;
+
+	g_return_val_if_fail (self, NULL);
+
+	self->pv->model = gcr_collection_model_new (self->pv->collection,
+	                                            "icon", G_TYPE_ICON,
+	                                            "markup", G_TYPE_STRING,
+	                                            NULL);
+
+	gtk_combo_box_set_model (GTK_COMBO_BOX (self), GTK_TREE_MODEL (self->pv->model));
+
+	/* The icon */
+	cell = gtk_cell_renderer_pixbuf_new ();
+	g_object_set (cell, "stock-size", GTK_ICON_SIZE_DND, NULL);
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, FALSE);
+	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, "gicon", 0);
+
+	/* The markup */
+	cell = gtk_cell_renderer_text_new ();
+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (self), cell, TRUE);
+	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (self), cell, "markup", 1);
+
+	return G_OBJECT (self);
+}
+
+static void
+gcr_combo_selector_init (GcrComboSelector *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_COMBO_SELECTOR, GcrComboSelectorPrivate);
+}
+
+static void
+gcr_combo_selector_dispose (GObject *obj)
+{
+	GcrComboSelector *self = GCR_COMBO_SELECTOR (obj);
+
+	if (self->pv->model)
+		g_object_unref (self->pv->model);
+	self->pv->model = NULL;
+
+	if (self->pv->collection)
+		g_object_unref (self->pv->collection);
+	self->pv->collection = NULL;
+
+	G_OBJECT_CLASS (gcr_combo_selector_parent_class)->dispose (obj);
+}
+
+static void
+gcr_combo_selector_finalize (GObject *obj)
+{
+	GcrComboSelector *self = GCR_COMBO_SELECTOR (obj);
+
+	g_assert (!self->pv->collection);
+	g_assert (!self->pv->model);
+
+	G_OBJECT_CLASS (gcr_combo_selector_parent_class)->finalize (obj);
+}
+
+static void
+gcr_combo_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
+                                 GParamSpec *pspec)
+{
+	GcrComboSelector *self = GCR_COMBO_SELECTOR (obj);
+
+	switch (prop_id) {
+	case PROP_COLLECTION:
+		g_return_if_fail (!self->pv->collection);
+		self->pv->collection = g_value_dup_object (value);
+		g_return_if_fail (self->pv->collection);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_combo_selector_get_property (GObject *obj, guint prop_id, GValue *value,
+                                 GParamSpec *pspec)
+{
+	GcrComboSelector *self = GCR_COMBO_SELECTOR (obj);
+
+	switch (prop_id) {
+	case PROP_COLLECTION:
+		g_value_set_object (value, gcr_combo_selector_get_collection (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gcr_combo_selector_class_init (GcrComboSelectorClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	gobject_class->constructor = gcr_combo_selector_constructor;
+	gobject_class->dispose = gcr_combo_selector_dispose;
+	gobject_class->finalize = gcr_combo_selector_finalize;
+	gobject_class->set_property = gcr_combo_selector_set_property;
+	gobject_class->get_property = gcr_combo_selector_get_property;
+
+	g_type_class_add_private (gobject_class, sizeof (GcrComboSelectorPrivate));
+
+	/**
+	 * GcrComboSelector:collection:
+	 *
+	 * The collection which contains the objects to display in the selector.
+	 */
+	g_object_class_install_property (gobject_class, PROP_COLLECTION,
+	           g_param_spec_object ("collection", "Collection", "Collection to select from",
+	                                GCR_TYPE_COLLECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	_gcr_initialize ();
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+/**
+ * gcr_selector_new:
+ * @collection: The collection that contains the objects to display
+ *
+ * Create a new #GcrTreeSelector.
+ *
+ * Returns: A newly allocated selector, which should be released with
+ *     g_object_unref().
+ */
+GcrComboSelector*
+gcr_combo_selector_new (GcrCollection *collection)
+{
+	return g_object_new (GCR_TYPE_COMBO_SELECTOR,
+	                     "collection", collection,
+	                     NULL);
+}
+
+/**
+ * gcr_combo_selector_get_collection:
+ * @self: The selector
+ *
+ * Get the collection that this selector is displaying objects from.
+ *
+ * Returns: The collection, owned by the selector.
+ */
+GcrCollection*
+gcr_combo_selector_get_collection (GcrComboSelector *self)
+{
+	g_return_val_if_fail (GCR_IS_COMBO_SELECTOR (self), NULL);
+	return self->pv->collection;
+}
+
+/**
+ * gcr_combo_selector_get_selected:
+ * @self: The selector
+ *
+ * Get the selected object in the selector, or %NULL if nothing selected.
+ *
+ * Returns: The selected object, owned by the selector, or %NULL.
+ */
+GObject*
+gcr_combo_selector_get_selected (GcrComboSelector *self)
+{
+	GtkTreeIter iter;
+
+	g_return_val_if_fail (GCR_IS_COMBO_SELECTOR (self), NULL);
+	gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self), &iter);
+
+	return gcr_collection_model_object_for_iter (self->pv->model, &iter);
+}
+
+/**
+ * gcr_combo_selector_set_selected:
+ * @self: The selector
+ * @selected: The object to select or %NULL.
+ *
+ * Set the currently selected object in the selector, or clear the selection
+ * if selected is set to %NULL.
+ */
+void
+gcr_combo_selector_set_selected (GcrComboSelector *self, GObject *selected)
+{
+	GtkTreeIter iter;
+
+	g_return_if_fail (GCR_IS_COMBO_SELECTOR (self));
+
+	if (selected) {
+		if (!gcr_collection_model_iter_for_object (self->pv->model, selected, &iter))
+			g_return_if_reached ();
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), &iter);
+	} else {
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (self), NULL);
+	}
+}
diff --git a/gcr/gcr-combo-selector.h b/gcr/gcr-combo-selector.h
new file mode 100644
index 0000000..8fcb05e
--- /dev/null
+++ b/gcr/gcr-combo-selector.h
@@ -0,0 +1,69 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_COMBO_SELECTOR_H__
+#define __GCR_COMBO_SELECTOR_H__
+
+#include "gcr-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_COMBO_SELECTOR               (gcr_combo_selector_get_type ())
+#define GCR_COMBO_SELECTOR(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_COMBO_SELECTOR, GcrComboSelector))
+#define GCR_COMBO_SELECTOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_COMBO_SELECTOR, GcrComboSelectorClass))
+#define GCR_IS_COMBO_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_COMBO_SELECTOR))
+#define GCR_IS_COMBO_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_COMBO_SELECTOR))
+#define GCR_COMBO_SELECTOR_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_COMBO_SELECTOR, GcrComboSelectorClass))
+
+typedef struct _GcrComboSelector GcrComboSelector;
+typedef struct _GcrComboSelectorClass GcrComboSelectorClass;
+typedef struct _GcrComboSelectorPrivate GcrComboSelectorPrivate;
+
+struct _GcrComboSelector {
+	GtkComboBox parent;
+
+	/*< private >*/
+	GcrComboSelectorPrivate *pv;
+};
+
+struct _GcrComboSelectorClass {
+	/*< private >*/
+	GtkComboBoxClass parent_class;
+};
+
+GType                    gcr_combo_selector_get_type         (void);
+
+GcrComboSelector*        gcr_combo_selector_new              (GcrCollection *collection);
+
+GcrCollection*           gcr_combo_selector_get_collection   (GcrComboSelector *self);
+
+const GcrColumn*         gcr_combo_selector_get_columns      (GcrComboSelector *self);
+
+GObject*                 gcr_combo_selector_get_selected     (GcrComboSelector *self);
+
+void                     gcr_combo_selector_set_selected     (GcrComboSelector *self,
+                                                              GObject *selected);
+
+G_END_DECLS
+
+#endif /* __GCR_COMBO_SELECTOR_H__ */
diff --git a/gcr/gcr-selector.c b/gcr/gcr-tree-selector.c
similarity index 57%
rename from gcr/gcr-selector.c
rename to gcr/gcr-tree-selector.c
index cfb7c7d..cd44978 100644
--- a/gcr/gcr-selector.c
+++ b/gcr/gcr-tree-selector.c
@@ -23,74 +23,48 @@
 
 #include "gcr-collection-model.h"
 #include "gcr-internal.h"
-#include "gcr-selector.h"
+#include "gcr-tree-selector.h"
 
 #include <glib/gi18n-lib.h>
 
 #include <string.h>
 
 /**
- * SECTION:gcr-selector
- * @title: GcrSelector
+ * SECTION:gcr-tree-selector
+ * @title: GcrTreeSelector
  * @short_description: A selector widget to select certificates or keys.
  *
- * The #GcrSelector can be used to select certificates or keys. The selector
- * comes in one of two modes: %GCR_SELECTOR_MODE_SINGLE and
- * %GCR_SELECTOR_MODE_MULTIPLE. The single selector mode allows the user to
- * select one object at a time, and the multiple selector allows the user
- * to select multiple objects from a list.
+ * The #GcrTreeSelector can be used to select certificates or keys. It allows
+ * the user to select multiple objects from a tree.
  */
 
 /**
- * GcrSelector:
+ * GcrTreeSelector:
+ * @parent: The parent object
  *
- * A selector widget.
+ * A tree selector widget.
  */
 
 /**
- * GcrSelectorClass:
+ * GcrTreeSelectorClass:
  *
- * The class for #GcrSelector.
- */
-
-/**
- * GcrSelectorMode:
- * @GCR_SELECTOR_MODE_SINGLE: User can select a single object.
- * @GCR_SELECTOR_MODE_MULTIPLE: The user can select multiple objects.
- *
- * The mode for the selector.
+ * The class for #GcrTreeSelector.
  */
 
 enum {
 	PROP_0,
 	PROP_COLLECTION,
-	PROP_COLUMNS,
-	PROP_MODE
-};
-
-struct _GcrSelector {
-	GtkAlignment parent;
-
-	/*< private >*/
-	GcrSelectorPrivate *pv;
-};
-
-struct _GcrSelectorClass {
-	/*< private >*/
-	GtkAlignmentClass parent_class;
+	PROP_COLUMNS
 };
 
-struct _GcrSelectorPrivate {
-	GtkComboBox *combo;
-	GtkTreeView *tree;
+struct _GcrTreeSelectorPrivate {
 	GcrCollection *collection;
 	const GcrColumn *columns;
 	GtkTreeModel *sort;
 	GcrCollectionModel *model;
-	GcrSelectorMode mode;
 };
 
-G_DEFINE_TYPE (GcrSelector, gcr_selector, GTK_TYPE_ALIGNMENT);
+G_DEFINE_TYPE (GcrTreeSelector, gcr_tree_selector, GTK_TYPE_TREE_VIEW);
 
 /* -----------------------------------------------------------------------------
  * INTERNAL
@@ -190,7 +164,7 @@ on_sort_column (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
 }
 
 static void
-add_string_column (GcrSelector *self, const GcrColumn *column, gint column_id)
+add_string_column (GcrTreeSelector *self, const GcrColumn *column, gint column_id)
 {
 	GtkCellRenderer *cell;
 	GtkTreeViewColumn *col;
@@ -201,16 +175,16 @@ add_string_column (GcrSelector *self, const GcrColumn *column, gint column_id)
 
 	cell = gtk_cell_renderer_text_new ();
 	g_object_set (G_OBJECT (cell), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-	label = g_dpgettext2 (NULL, "column", column->label);
+	label = column->label ? g_dpgettext2 (NULL, "column", column->label) : NULL;
 	col = gtk_tree_view_column_new_with_attributes (label, cell, "text", column_id, NULL);
 	gtk_tree_view_column_set_resizable (col, TRUE);
 	if (column->flags & GCR_COLUMN_SORTABLE)
 		gtk_tree_view_column_set_sort_column_id (col, column_id);
-	gtk_tree_view_append_column (self->pv->tree, col);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self), col);
 }
 
 static void
-add_icon_column (GcrSelector *self, const GcrColumn *column, gint column_id)
+add_icon_column (GcrTreeSelector *self, const GcrColumn *column, gint column_id)
 {
 	GtkCellRenderer *cell;
 	GtkTreeViewColumn *col;
@@ -221,16 +195,16 @@ add_icon_column (GcrSelector *self, const GcrColumn *column, gint column_id)
 
 	cell = gtk_cell_renderer_pixbuf_new ();
 	g_object_set (cell, "stock-size", GTK_ICON_SIZE_BUTTON, NULL);
-	label = g_dpgettext2 (NULL, "column", column->label);
+	label = column->label ? g_dpgettext2 (NULL, "column", column->label) : NULL;
 	col = gtk_tree_view_column_new_with_attributes (label, cell, "gicon", column_id, NULL);
 	gtk_tree_view_column_set_resizable (col, TRUE);
 	if (column->flags & GCR_COLUMN_SORTABLE)
 		gtk_tree_view_column_set_sort_column_id (col, column_id);
-	gtk_tree_view_append_column (self->pv->tree, col);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self), col);
 }
 
 static void
-add_check_column (GcrSelector *self, guint column_id)
+add_check_column (GcrTreeSelector *self, guint column_id)
 {
 	GtkCellRenderer *cell;
 	GtkTreeViewColumn *col;
@@ -240,54 +214,31 @@ add_check_column (GcrSelector *self, guint column_id)
 
 	col = gtk_tree_view_column_new_with_attributes ("", cell, "active", column_id, NULL);
 	gtk_tree_view_column_set_resizable (col, FALSE);
-	gtk_tree_view_append_column (self->pv->tree, col);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self), col);
 }
 
-static void
-construct_single_selector (GcrSelector *self)
-{
-	GtkCellRenderer *cell;
-	GtkWidget *widget;
-
-	self->pv->model = gcr_collection_model_new (self->pv->collection,
-	                                            "icon", G_TYPE_ICON,
-	                                            "markup", G_TYPE_STRING,
-	                                            NULL);
-
-	widget = gtk_combo_box_new_with_model (GTK_TREE_MODEL (self->pv->model));
-	self->pv->combo = GTK_COMBO_BOX (widget);
-
-	/* The icon */
-	cell = gtk_cell_renderer_pixbuf_new ();
-	g_object_set (cell, "stock-size", GTK_ICON_SIZE_DND, NULL);
-	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, FALSE);
-	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), cell, "gicon", 0);
-
-	/* The markup */
-	cell = gtk_cell_renderer_text_new ();
-	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), cell, TRUE);
-	gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (widget), cell, "markup", 1);
-
-	gtk_widget_show (widget);
-	gtk_container_add (GTK_CONTAINER (self), widget);
-}
+/* -----------------------------------------------------------------------------
+ * OBJECT
+ */
 
-static void
-construct_multiple_selector (GcrSelector *self)
+static GObject*
+gcr_tree_selector_constructor (GType type, guint n_props, GObjectConstructParam *props)
 {
+	GcrTreeSelector *self = GCR_TREE_SELECTOR (G_OBJECT_CLASS (gcr_tree_selector_parent_class)->constructor(type, n_props, props));
 	const GcrColumn *column;
-	GtkWidget *widget, *scroll;
 	GtkTreeSortable *sortable;
 	guint i;
 
+	g_return_val_if_fail (self, NULL);
+	g_return_val_if_fail (self->pv->columns, NULL);
+
 	self->pv->model = gcr_collection_model_new_full (self->pv->collection,
 	                                                 self->pv->columns);
 
 	self->pv->sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (self->pv->model));
 	sortable = GTK_TREE_SORTABLE (self->pv->sort);
 
-	widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->pv->sort));
-	self->pv->tree = GTK_TREE_VIEW (widget);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (self), GTK_TREE_MODEL (self->pv->sort));
 
 	/* First add the check mark column */
 	add_check_column (self, gcr_collection_model_column_for_selected (self->pv->model));
@@ -315,66 +266,19 @@ construct_multiple_selector (GcrSelector *self)
 		}
 	}
 
-	scroll = gtk_scrolled_window_new (NULL, NULL);
-	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_ETCHED_IN);
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-	gtk_container_add (GTK_CONTAINER (scroll), widget);
-	gtk_container_add (GTK_CONTAINER (self), scroll);
-
-	gtk_widget_show_all (scroll);
-}
-
-/* -----------------------------------------------------------------------------
- * OBJECT
- */
-
-GType
-gcr_selector_mode_get_type (void)
-{
-	static GType type = 0;
-	static GEnumValue values[] = {
-		{ GCR_SELECTOR_MODE_SINGLE, "single", "Single"},
-		{ GCR_SELECTOR_MODE_MULTIPLE, "multiple", "Multiple"},
-		{ 0, NULL, NULL }
-	};
-	if (!type)
-		type = g_enum_register_static ("GcrSelectorMode", values);
-	return type;
-}
-
-static GObject*
-gcr_selector_constructor (GType type, guint n_props, GObjectConstructParam *props)
-{
-	GcrSelector *self = GCR_SELECTOR (G_OBJECT_CLASS (gcr_selector_parent_class)->constructor(type, n_props, props));
-	g_return_val_if_fail (self, NULL);
-
-	g_return_val_if_fail (self->pv->columns, NULL);
-
-	switch (self->pv->mode) {
-	case GCR_SELECTOR_MODE_SINGLE:
-		construct_single_selector (self);
-		break;
-	case GCR_SELECTOR_MODE_MULTIPLE:
-		construct_multiple_selector (self);
-		break;
-	default:
-		g_assert_not_reached ();
-		break;
-	}
-
 	return G_OBJECT (self);
 }
 
 static void
-gcr_selector_init (GcrSelector *self)
+gcr_tree_selector_init (GcrTreeSelector *self)
 {
-	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_SELECTOR, GcrSelectorPrivate);
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCR_TYPE_TREE_SELECTOR, GcrTreeSelectorPrivate);
 }
 
 static void
-gcr_selector_dispose (GObject *obj)
+gcr_tree_selector_dispose (GObject *obj)
 {
-	GcrSelector *self = GCR_SELECTOR (obj);
+	GcrTreeSelector *self = GCR_TREE_SELECTOR (obj);
 
 	if (self->pv->model)
 		g_object_unref (self->pv->model);
@@ -388,27 +292,25 @@ gcr_selector_dispose (GObject *obj)
 		g_object_unref (self->pv->sort);
 	self->pv->sort = NULL;
 
-	G_OBJECT_CLASS (gcr_selector_parent_class)->dispose (obj);
+	G_OBJECT_CLASS (gcr_tree_selector_parent_class)->dispose (obj);
 }
 
 static void
-gcr_selector_finalize (GObject *obj)
+gcr_tree_selector_finalize (GObject *obj)
 {
-	GcrSelector *self = GCR_SELECTOR (obj);
+	GcrTreeSelector *self = GCR_TREE_SELECTOR (obj);
 
 	g_assert (!self->pv->collection);
 	g_assert (!self->pv->model);
-	self->pv->combo = NULL;
-	self->pv->tree = NULL;
 
-	G_OBJECT_CLASS (gcr_selector_parent_class)->finalize (obj);
+	G_OBJECT_CLASS (gcr_tree_selector_parent_class)->finalize (obj);
 }
 
 static void
-gcr_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
-                           GParamSpec *pspec)
+gcr_tree_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
+                                GParamSpec *pspec)
 {
-	GcrSelector *self = GCR_SELECTOR (obj);
+	GcrTreeSelector *self = GCR_TREE_SELECTOR (obj);
 	switch (prop_id) {
 	case PROP_COLLECTION:
 		g_return_if_fail (!self->pv->collection);
@@ -420,9 +322,6 @@ gcr_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
 		self->pv->columns = g_value_get_pointer (value);
 		g_return_if_fail (self->pv->columns);
 		break;
-	case PROP_MODE:
-		self->pv->mode = g_value_get_enum (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -430,20 +329,17 @@ gcr_selector_set_property (GObject *obj, guint prop_id, const GValue *value,
 }
 
 static void
-gcr_selector_get_property (GObject *obj, guint prop_id, GValue *value,
-                         GParamSpec *pspec)
+gcr_tree_selector_get_property (GObject *obj, guint prop_id, GValue *value,
+                                GParamSpec *pspec)
 {
-	GcrSelector *self = GCR_SELECTOR (obj);
+	GcrTreeSelector *self = GCR_TREE_SELECTOR (obj);
 
 	switch (prop_id) {
 	case PROP_COLLECTION:
-		g_value_set_object (value, gcr_selector_get_collection (self));
+		g_value_set_object (value, gcr_tree_selector_get_collection (self));
 		break;
 	case PROP_COLUMNS:
-		g_value_set_pointer (value, (gpointer)gcr_selector_get_columns (self));
-		break;
-	case PROP_MODE:
-		g_value_set_enum (value, gcr_selector_get_mode (self));
+		g_value_set_pointer (value, (gpointer)gcr_tree_selector_get_columns (self));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
@@ -452,20 +348,20 @@ gcr_selector_get_property (GObject *obj, guint prop_id, GValue *value,
 }
 
 static void
-gcr_selector_class_init (GcrSelectorClass *klass)
+gcr_tree_selector_class_init (GcrTreeSelectorClass *klass)
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-	gobject_class->constructor = gcr_selector_constructor;
-	gobject_class->dispose = gcr_selector_dispose;
-	gobject_class->finalize = gcr_selector_finalize;
-	gobject_class->set_property = gcr_selector_set_property;
-	gobject_class->get_property = gcr_selector_get_property;
+	gobject_class->constructor = gcr_tree_selector_constructor;
+	gobject_class->dispose = gcr_tree_selector_dispose;
+	gobject_class->finalize = gcr_tree_selector_finalize;
+	gobject_class->set_property = gcr_tree_selector_set_property;
+	gobject_class->get_property = gcr_tree_selector_get_property;
 
-	g_type_class_add_private (gobject_class, sizeof (GcrSelectorPrivate));
+	g_type_class_add_private (gobject_class, sizeof (GcrTreeSelectorPrivate));
 
 	/**
-	 * GcrSelector:collection:
+	 * GcrTreeSelector:collection:
 	 *
 	 * The collection which contains the objects to display in the selector.
 	 */
@@ -474,24 +370,14 @@ gcr_selector_class_init (GcrSelectorClass *klass)
 	                                GCR_TYPE_COLLECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
 	/**
-	 * GcrSelector:columns:
+	 * GcrTreeSelector:columns:
 	 *
 	 * The columns to use to display the objects.
 	 */
 	g_object_class_install_property (gobject_class, PROP_COLUMNS,
-	           g_param_spec_pointer ("columns", "Columns", "Columns to display in multiple selector",
+	           g_param_spec_pointer ("columns", "Columns", "Columns to display in selector",
 	                                 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
-	/**
-	 * GcrSelector:mode:
-	 *
-	 * The mode of the selector.
-	 */
-	g_object_class_install_property (gobject_class, PROP_MODE,
-	           g_param_spec_enum ("mode", "Mode", "The mode of the selector",
-	                              GCR_TYPE_SELECTOR_MODE, GCR_SELECTOR_MODE_SINGLE,
-	                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
 	_gcr_initialize ();
 }
 
@@ -500,28 +386,26 @@ gcr_selector_class_init (GcrSelectorClass *klass)
  */
 
 /**
- * gcr_selector_new:
+ * gcr_tree_selector_new:
  * @collection: The collection that contains the objects to display
  * @columns: The columns to use to display the objects
- * @mode: The mode of the selector
  *
- * Create a new #GcrSelector.
+ * Create a new #GcrTreeSelector.
  *
  * Returns: A newly allocated selector, which should be released with
  *     g_object_unref().
  */
-GcrSelector*
-gcr_selector_new (GcrCollection *collection, const GcrColumn *columns, GcrSelectorMode mode)
+GcrTreeSelector*
+gcr_tree_selector_new (GcrCollection *collection, const GcrColumn *columns)
 {
-	return g_object_new (GCR_TYPE_SELECTOR,
+	return g_object_new (GCR_TYPE_TREE_SELECTOR,
 	                     "collection", collection,
 	                     "columns", columns,
-	                     "mode", mode,
 	                     NULL);
 }
 
 /**
- * gcr_selector_get_collection:
+ * gcr_tree_selector_get_collection:
  * @self: The selector
  *
  * Get the collection that this selector is displaying objects from.
@@ -529,14 +413,14 @@ gcr_selector_new (GcrCollection *collection, const GcrColumn *columns, GcrSelect
  * Returns: The collection, owned by the selector.
  */
 GcrCollection*
-gcr_selector_get_collection (GcrSelector *self)
+gcr_tree_selector_get_collection (GcrTreeSelector *self)
 {
-	g_return_val_if_fail (GCR_IS_SELECTOR (self), NULL);
+	g_return_val_if_fail (GCR_IS_TREE_SELECTOR (self), NULL);
 	return self->pv->collection;
 }
 
 /**
- * gcr_selector_get_columns:
+ * gcr_tree_selector_get_columns:
  * @self: The selector
  *
  * Get the columns displayed in a selector in multiple mode.
@@ -544,23 +428,37 @@ gcr_selector_get_collection (GcrSelector *self)
  * Returns: The columns, owned by the selector.
  */
 const GcrColumn*
-gcr_selector_get_columns (GcrSelector *self)
+gcr_tree_selector_get_columns (GcrTreeSelector *self)
 {
-	g_return_val_if_fail (GCR_IS_SELECTOR (self), NULL);
+	g_return_val_if_fail (GCR_IS_TREE_SELECTOR (self), NULL);
 	return self->pv->columns;
 }
 
 /**
- * gcr_selector_get_mode:
+ * gcr_tree_selector_get_selected:
  * @self: The selector
  *
- * Get the mode of the selector, whether single or multiple selection.
+ * Get a list of selected objects.
+ *
+ * Returns: The list of selected objects, to be released with g_list_free().
+ */
+GList*
+gcr_tree_selector_get_selected (GcrTreeSelector *self)
+{
+	g_return_val_if_fail (GCR_IS_TREE_SELECTOR (self), NULL);
+	return gcr_collection_model_get_selected_objects (self->pv->model);
+}
+
+/**
+ * gcr_tree_selector_set_selected:
+ * @self: The selector
+ * @selected: The list of objects to select.
  *
- * Returns: The mode of the selector.
+ * Select certain objects in the selector.
  */
-GcrSelectorMode
-gcr_selector_get_mode (GcrSelector *self)
+void
+gcr_tree_selector_set_selected (GcrTreeSelector *self, GList *selected)
 {
-	g_return_val_if_fail (GCR_IS_SELECTOR (self), 0);
-	return self->pv->mode;
+	g_return_if_fail (GCR_IS_TREE_SELECTOR (self));
+	gcr_collection_model_set_selected_objects (self->pv->model, selected);
 }
diff --git a/gcr/gcr-tree-selector.h b/gcr/gcr-tree-selector.h
new file mode 100644
index 0000000..7792153
--- /dev/null
+++ b/gcr/gcr-tree-selector.h
@@ -0,0 +1,70 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2010 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GCR_TREE_SELECTOR_H__
+#define __GCR_TREE_SELECTOR_H__
+
+#include "gcr-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GCR_TYPE_TREE_SELECTOR               (gcr_tree_selector_get_type ())
+#define GCR_TREE_SELECTOR(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCR_TYPE_TREE_SELECTOR, GcrTreeSelector))
+#define GCR_TREE_SELECTOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCR_TYPE_TREE_SELECTOR, GcrTreeSelectorClass))
+#define GCR_IS_TREE_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCR_TYPE_TREE_SELECTOR))
+#define GCR_IS_TREE_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCR_TYPE_TREE_SELECTOR))
+#define GCR_TREE_SELECTOR_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCR_TYPE_TREE_SELECTOR, GcrTreeSelectorClass))
+
+typedef struct _GcrTreeSelector GcrTreeSelector;
+typedef struct _GcrTreeSelectorClass GcrTreeSelectorClass;
+typedef struct _GcrTreeSelectorPrivate GcrTreeSelectorPrivate;
+
+struct _GcrTreeSelector {
+	GtkTreeView parent;
+
+	/*< private >*/
+	GcrTreeSelectorPrivate *pv;
+};
+
+struct _GcrTreeSelectorClass {
+	/*< private >*/
+	GtkTreeViewClass parent_class;
+};
+
+GType                    gcr_tree_selector_get_type          (void);
+
+GcrTreeSelector*         gcr_tree_selector_new               (GcrCollection *collection,
+                                                              const GcrColumn *columns);
+
+GcrCollection*           gcr_tree_selector_get_collection    (GcrTreeSelector *self);
+
+const GcrColumn*         gcr_tree_selector_get_columns       (GcrTreeSelector *self);
+
+GList*                   gcr_tree_selector_get_selected      (GcrTreeSelector *self);
+
+void                     gcr_tree_selector_set_selected      (GcrTreeSelector *self,
+                                                              GList *selected);
+
+G_END_DECLS
+
+#endif /* __GCR_TREE_SELECTOR_H__ */
diff --git a/gcr/gcr.h b/gcr/gcr.h
index 9987dc7..12a90b2 100644
--- a/gcr/gcr.h
+++ b/gcr/gcr.h
@@ -39,6 +39,7 @@
 #include "gcr-certificate-renderer.h"
 #include "gcr-certificate-widget.h"
 #include "gcr-collection-model.h"
+#include "gcr-combo-selector.h"
 #include "gcr-key-renderer.h"
 #include "gcr-key-widget.h"
 #include "gcr-importer.h"
@@ -46,9 +47,9 @@
 #include "gcr-parser.h"
 #include "gcr-renderer.h"
 #include "gcr-pkcs11-certificate.h"
-#include "gcr-selector.h"
 #include "gcr-simple-certificate.h"
 #include "gcr-simple-collection.h"
+#include "gcr-tree-selector.h"
 #include "gcr-trust.h"
 #include "gcr-unlock-options.h"
 #include "gcr-unlock-options-widget.h"
diff --git a/gcr/tests/Makefile.am b/gcr/tests/Makefile.am
index e01cd15..854a3b1 100644
--- a/gcr/tests/Makefile.am
+++ b/gcr/tests/Makefile.am
@@ -47,8 +47,9 @@ EXTRA_DIST = \
 
 noinst_PROGRAMS = \
 	frob-certificate \
+	frob-combo-selector \
 	frob-gnupg-selector \
-	frob-parser \
 	frob-key \
-	frob-selector \
+	frob-tree-selector \
+	frob-parser \
 	frob-unlock-options
diff --git a/gcr/tests/frob-selector.c b/gcr/tests/frob-combo-selector.c
similarity index 87%
copy from gcr/tests/frob-selector.c
copy to gcr/tests/frob-combo-selector.c
index da01041..d05f9a4 100644
--- a/gcr/tests/frob-selector.c
+++ b/gcr/tests/frob-combo-selector.c
@@ -65,9 +65,10 @@ int
 main (int argc, char *argv[])
 {
 	GcrCollection *collection;
-	GcrSelector *selector;
+	GcrComboSelector *selector;
 	GtkDialog *dialog;
 	GcrParser *parser;
+	GObject *selected;
 	int i;
 
 	gtk_init (&argc, &argv);
@@ -76,7 +77,7 @@ main (int argc, char *argv[])
 	g_object_ref_sink (dialog);
 
 	collection = gcr_simple_collection_new ();
-	selector = gcr_selector_new (collection, GCR_CERTIFICATE_COLUMNS, GCR_SELECTOR_MODE_MULTIPLE);
+	selector = gcr_combo_selector_new (collection);
 
 	gtk_widget_show (GTK_WIDGET (selector));
 	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (selector));
@@ -99,6 +100,16 @@ main (int argc, char *argv[])
 	g_object_unref (collection);
 
 	gtk_dialog_run (dialog);
+
+	selected = gcr_combo_selector_get_selected (selector);
+	if (selected == NULL) {
+		g_print ("nothing selected\n");
+	} else {
+		gchar *label;
+		g_object_get (selected, "label", &label, NULL);
+		g_print ("selected: %s\n", label);
+	}
+
 	gtk_widget_destroy (GTK_WIDGET (dialog));
 	g_object_unref (dialog);
 
diff --git a/gcr/tests/frob-gnupg-selector.c b/gcr/tests/frob-gnupg-selector.c
index dfea94e..477c132 100644
--- a/gcr/tests/frob-gnupg-selector.c
+++ b/gcr/tests/frob-gnupg-selector.c
@@ -49,7 +49,8 @@ int
 main (int argc, char *argv[])
 {
 	GcrCollection *collection;
-	GcrSelector *selector;
+	GcrTreeSelector *selector;
+	GtkWidget *scroll;
 	GtkDialog *dialog;
 
 	gtk_init (&argc, &argv);
@@ -58,10 +59,15 @@ main (int argc, char *argv[])
 	g_object_ref_sink (dialog);
 
 	collection = _gcr_gnupg_collection_new (NULL);
-	selector = gcr_selector_new (collection, GCR_GNUPG_KEY_COLUMNS, GCR_SELECTOR_MODE_MULTIPLE);
+	selector = gcr_tree_selector_new (collection, GCR_GNUPG_KEY_COLUMNS);
 
-	gtk_widget_show (GTK_WIDGET (selector));
-	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (selector));
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_ETCHED_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (selector));
+
+	gtk_widget_show_all (scroll);
+	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (scroll));
 
 	_gcr_gnupg_collection_load_async (GCR_GNUPG_COLLECTION (collection), NULL,
 	                                  on_collection_loaded, NULL);
diff --git a/gcr/tests/frob-selector.c b/gcr/tests/frob-tree-selector.c
similarity index 74%
rename from gcr/tests/frob-selector.c
rename to gcr/tests/frob-tree-selector.c
index da01041..db04e91 100644
--- a/gcr/tests/frob-selector.c
+++ b/gcr/tests/frob-tree-selector.c
@@ -65,9 +65,11 @@ int
 main (int argc, char *argv[])
 {
 	GcrCollection *collection;
-	GcrSelector *selector;
+	GcrTreeSelector *selector;
 	GtkDialog *dialog;
 	GcrParser *parser;
+	GtkWidget *scroll;
+	GList *selected, *l;
 	int i;
 
 	gtk_init (&argc, &argv);
@@ -76,10 +78,16 @@ main (int argc, char *argv[])
 	g_object_ref_sink (dialog);
 
 	collection = gcr_simple_collection_new ();
-	selector = gcr_selector_new (collection, GCR_CERTIFICATE_COLUMNS, GCR_SELECTOR_MODE_MULTIPLE);
+	selector = gcr_tree_selector_new (collection, GCR_CERTIFICATE_COLUMNS);
+
+	scroll = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_ETCHED_IN);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (scroll), GTK_WIDGET (selector));
+	gtk_widget_show_all (scroll);
 
 	gtk_widget_show (GTK_WIDGET (selector));
-	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (selector));
+	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (dialog)), GTK_WIDGET (scroll));
 
 	gtk_window_set_default_size (GTK_WINDOW (dialog), 550, 400);
 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);
@@ -99,6 +107,16 @@ main (int argc, char *argv[])
 	g_object_unref (collection);
 
 	gtk_dialog_run (dialog);
+
+	selected = gcr_tree_selector_get_selected (selector);
+	for (l = selected; l; l = g_list_next (l)) {
+		gchar *label;
+		g_object_get (l->data, "label", &label, NULL);
+		g_print ("selected: %s\n", label);
+		g_free (label);
+	}
+	g_list_free (selected);
+
 	gtk_widget_destroy (GTK_WIDGET (dialog));
 	g_object_unref (dialog);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]