[network-manager-applet/lr/pkcs11: 9/10] XXX import the gcr object chooser



commit 4462329f6f568a89fc636b4fa73c3315d6857df6
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Sun Feb 19 13:14:52 2017 +0000

    XXX import the gcr object chooser

 Makefile.am                             |   36 ++-
 configure.ac                            |   12 +
 src/libnma/gcr-object-chooser-button.c  |  183 ++++++++++
 src/libnma/gcr-object-chooser-button.h  |   38 ++
 src/libnma/gcr-object-chooser-button.ui |   58 +++
 src/libnma/gcr-object-chooser-dialog.c  |  168 +++++++++
 src/libnma/gcr-object-chooser-dialog.h  |   40 +++
 src/libnma/gcr-object-chooser-dialog.ui |   44 +++
 src/libnma/gcr-object-chooser-widget.c  |  572 +++++++++++++++++++++++++++++++
 src/libnma/gcr-object-chooser-widget.h  |   38 ++
 src/libnma/gcr-object-chooser-widget.ui |  127 +++++++
 src/libnma/gcr-object-chooser.c         |   93 +++++
 src/libnma/gcr-object-chooser.h         |   41 +++
 src/libnma/gcr-token-login-dialog.c     |  250 ++++++++++++++
 src/libnma/gcr-token-login-dialog.h     |   43 +++
 src/libnma/gcr-token-login-dialog.ui    |  111 ++++++
 src/libnma/gcr-tokens-sidebar-row.c     |  212 ++++++++++++
 src/libnma/gcr-tokens-sidebar-row.h     |   39 ++
 src/libnma/gcr-tokens-sidebar-row.ui    |   97 ++++++
 src/libnma/gcr-tokens-sidebar.c         |  222 ++++++++++++
 src/libnma/gcr-tokens-sidebar.h         |   39 ++
 src/libnma/nma.gresource.xml            |    7 +
 22 files changed, 2465 insertions(+), 5 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index a3a5551..ace77d2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -482,7 +482,15 @@ libnma_h_pub = \
        src/libnma/nma-vpn-password-dialog.h \
        src/libnma/nma-ui-utils.h \
        src/libnma/nma-cert-chooser.h \
-       src/libnma/nma-cert-file-chooser.h
+       src/libnma/nma-cert-file-chooser.h \
+       \
+       src/libnma/gcr-object-chooser-button.h \
+       src/libnma/gcr-object-chooser-dialog.h \
+       src/libnma/gcr-object-chooser-widget.h \
+       src/libnma/gcr-object-chooser.h \
+       src/libnma/gcr-token-login-dialog.h \
+       src/libnma/gcr-tokens-sidebar-row.h \
+       src/libnma/gcr-tokens-sidebar.h
 
 libnma_c_real = \
        src/libnma/nma-wifi-dialog.c \
@@ -492,7 +500,15 @@ libnma_c_real = \
        src/libnma/nma-ui-utils.c \
        src/libnma/init.c \
        src/libnma/nma-cert-chooser.c \
-       src/libnma/nma-cert-file-chooser.c
+       src/libnma/nma-cert-file-chooser.c \
+       \
+       src/libnma/gcr-object-chooser-button.c \
+       src/libnma/gcr-object-chooser-dialog.c \
+       src/libnma/gcr-object-chooser-widget.c \
+       src/libnma/gcr-object-chooser.c \
+       src/libnma/gcr-token-login-dialog.c \
+       src/libnma/gcr-tokens-sidebar-row.c \
+       src/libnma/gcr-tokens-sidebar.c
 
 src_libnma_libnmadir = $(includedir)/libnma
 
@@ -540,13 +556,17 @@ src_libnma_libnma_la_CFLAGS = \
        "-I$(srcdir)/src/libnma" \
        $(GTK_CFLAGS) \
        $(LIBNM_CFLAGS) \
-       $(GUDEV_CFLAGS)
+       $(GUDEV_CFLAGS) \
+       $(GCR_CFLAGS) \
+       -DGCR_API_SUBJECT_TO_CHANGE \
+       -DGCK_API_SUBJECT_TO_CHANGE
 
 src_libnma_libnma_la_LIBADD = \
        src/wireless-security/libwireless-security-libnm.la \
        $(GTK_LIBS) \
        $(LIBNM_LIBS) \
-       $(GUDEV_LIBS)
+       $(GUDEV_LIBS) \
+       $(GCR_LIBS)
 
 $(src_libnma_libnma_la_OBJECTS): $(libnma_h_priv_gen)
 
@@ -576,7 +596,13 @@ EXTRA_DIST += \
        src/libnma/libnma.pc.in \
        src/libnma/libnma.ver \
        src/libnma/wifi.ui \
-       src/libnma/nma.gresource.xml
+       src/libnma/nma.gresource.xml \
+       \
+       src/libnma/gcr-object-chooser-button.ui \
+       src/libnma/gcr-object-chooser-dialog.ui \
+       src/libnma/gcr-object-chooser-widget.ui \
+       src/libnma/gcr-token-login-dialog.ui \
+       src/libnma/gcr-tokens-sidebar-row.ui
 
 ###############################################################################
 
diff --git a/configure.ac b/configure.ac
index a390dc7..23ca0b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,6 +144,18 @@ else
 fi
 AM_CONDITIONAL(WITH_JANSSON, test "${with_team}" != "no")
 
+dnl Gcr for PKCS#11 certificate picker
+AC_ARG_WITH(gcr, AS_HELP_STRING([--with-gcr], [Enable PKCS#11 certificate chooser (default: yes)]))
+if (test "${with_gcr}" == "no"); then
+    AC_DEFINE(WITH_GCR, 0, [Define if Gcr is available])
+else
+    PKG_CHECK_MODULES(GCR,
+                      [gcr-3 >= 3.20, gck-1 >= 3.20],,
+                      AC_MSG_ERROR([gcr is needed for certificate chooser. Use --without-gcr to build 
without it.]))
+    AC_DEFINE(WITH_GCR, 1, [Define if Gcr is available])
+fi
+AM_CONDITIONAL(WITH_GCR, test "${with_gcr}" != "no")
+
 dnl Check for gobject introspection
 GOBJECT_INTROSPECTION_CHECK([0.9.6])
 
diff --git a/src/libnma/gcr-object-chooser-button.c b/src/libnma/gcr-object-chooser-button.c
new file mode 100644
index 0000000..8f79131
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-button.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-object-chooser-button.h"
+#include "gcr-object-chooser-dialog.h"
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+/**
+ * SECTION:gcr-object-chooser-button
+ * @title: GcrObjectChooserButton
+ * @short_description: The PKCS11 Object Chooser Button
+ * @see_also: #GcrObjectChooser
+ *
+ * #GcrObjectChooserButton is the PKCS11 object chooser button. It runs the
+ * #GcrObjectChooserDialog when clicked, to choose an object.
+ */
+
+struct _GcrObjectChooserButtonPrivate
+{
+       GtkWidget *dialog;
+       GtkLabel *label;
+
+       gchar *uri;
+};
+
+static void gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrObjectChooserButton, gcr_object_chooser_button, GTK_TYPE_BUTTON,
+                         G_ADD_PRIVATE (GcrObjectChooserButton)
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_OBJECT_CHOOSER, gcr_object_chooser_iface_init));
+
+static void
+set_uri (GcrObjectChooser *self, const gchar *uri)
+{
+       GcrObjectChooserButtonPrivate *priv = GCR_OBJECT_CHOOSER_BUTTON (self)->priv;
+       GckUriData *data;
+       GError *error = NULL;
+       gchar *label = NULL;
+       gchar *real_uri;
+       char *c;
+
+       if (priv->uri) {
+               g_free (priv->uri);
+               priv->uri = NULL;
+       }
+
+       if (uri) {
+               priv->uri = g_strdup (uri);
+       } else {
+               gtk_button_set_label (GTK_BUTTON (self), _("(None)"));
+               return;
+       }
+
+       /* XXX: Fix GckUri to handle URI attributes instead of chopping them off. */
+       real_uri = g_strdup (uri);
+       c = strchr (real_uri, '&');
+       if (c)
+               *c = '\0';
+       data = gck_uri_parse (real_uri, GCK_URI_FOR_ANY, &error);
+       g_free (real_uri);
+
+       if (data) {
+               if (!gck_attributes_find_string (data->attributes, CKA_LABEL, &label)) {
+                       if (data->token_info)
+                               label = g_strdup_printf (_("Certificate in %s"), data->token_info->label);
+               }
+       } else {
+               g_warning ("Bad URI: %s\n", error->message);
+               g_error_free (error);
+       }
+
+       if (!label)
+               label = g_strdup (_("(Unknown)"));
+       gtk_label_set_text (GTK_LABEL (priv->label), label);
+       g_free (label);
+       gck_uri_data_free (data);
+}
+
+static gchar *
+get_uri (GcrObjectChooser *self)
+{
+       GcrObjectChooserButtonPrivate *priv = GCR_OBJECT_CHOOSER_BUTTON (self)->priv;
+
+       return priv->uri;
+}
+
+static void
+gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface)
+{
+       iface->set_uri = set_uri;
+       iface->get_uri = get_uri;
+}
+
+static void
+clicked (GtkButton *button)
+{
+       GcrObjectChooserButtonPrivate *priv = GCR_OBJECT_CHOOSER_BUTTON (button)->priv;
+
+       if (gtk_dialog_run (GTK_DIALOG (priv->dialog)) == GTK_RESPONSE_ACCEPT) {
+               gcr_object_chooser_set_uri (GCR_OBJECT_CHOOSER (button),
+                                           gcr_object_chooser_get_uri (GCR_OBJECT_CHOOSER (priv->dialog)));
+       }
+       gtk_widget_hide (priv->dialog);
+
+}
+
+static void
+finalize (GObject *object)
+{
+       GcrObjectChooserButtonPrivate *priv = GCR_OBJECT_CHOOSER_BUTTON (object)->priv;
+
+       if (priv->uri)
+               g_free (priv->uri);
+       gtk_widget_destroy (priv->dialog);
+}
+
+static void
+gcr_object_chooser_button_class_init (GcrObjectChooserButtonClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->finalize = finalize;
+
+        gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gcr/ui/gcr-object-chooser-button.ui");
+        gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserButton, label);
+       gtk_widget_class_bind_template_callback (widget_class, clicked);
+}
+
+static void
+gcr_object_chooser_button_init (GcrObjectChooserButton *self)
+{
+       self->priv = gcr_object_chooser_button_get_instance_private (self);
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * gcr_object_chooser_button_new:
+ * @parent_window: (allow-none): parent window of the #GtkObjectChooserDialog or %NULL
+ *
+ * Creates the new #GcrObjectChooserButton.
+ *
+ * Returns: newly created #GcrObjectChooserButton
+ */
+
+GtkWidget *
+gcr_object_chooser_button_new (GtkWindow *parent_window)
+{
+       GtkWidget *self;
+       GcrObjectChooserButtonPrivate *priv;
+
+       self = GTK_WIDGET (g_object_new (GCR_TYPE_OBJECT_CHOOSER_BUTTON, NULL));
+
+       priv = GCR_OBJECT_CHOOSER_BUTTON (self)->priv;
+       priv->dialog = gcr_object_chooser_dialog_new (_("Choose a certificate"),
+                                                     parent_window,
+                                                     GTK_DIALOG_USE_HEADER_BAR,
+                                                     _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                                     _("_Select"), GTK_RESPONSE_ACCEPT,
+                                                     NULL);
+
+       return self;
+}
diff --git a/src/libnma/gcr-object-chooser-button.h b/src/libnma/gcr-object-chooser-button.h
new file mode 100644
index 0000000..5f09979
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-button.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GCR_OBJECT_CHOOSER_BUTTON_H__
+#define __GCR_OBJECT_CHOOSER_BUTTON_H__
+
+#include <gtk/gtk.h>
+
+#include "gcr-object-chooser.h"
+
+typedef struct _GcrObjectChooserButtonPrivate GcrObjectChooserButtonPrivate;
+
+struct _GcrObjectChooserButton
+{
+        GtkButton parent_instance;
+        GcrObjectChooserButtonPrivate *priv;
+};
+
+#define GCR_TYPE_OBJECT_CHOOSER_BUTTON gcr_object_chooser_button_get_type ()
+G_DECLARE_FINAL_TYPE (GcrObjectChooserButton, gcr_object_chooser_button, GCR, OBJECT_CHOOSER_BUTTON, 
GtkButton)
+
+GtkWidget *gcr_object_chooser_button_new (GtkWindow *parent_window);
+
+#endif /* __GCR_OBJECT_CHOOSER_BUTTON_H__ */
diff --git a/src/libnma/gcr-object-chooser-button.ui b/src/libnma/gcr-object-chooser-button.ui
new file mode 100644
index 0000000..e99b1b4
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-button.ui
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GcrObjectChooserButton" parent="GtkButton">
+    <property name="can_focus">False</property>
+    <property name="receives_default">False</property>
+    <signal name="clicked" handler="clicked" swapped="no"/>
+    <child>
+      <object class="GtkBox" id="box2">
+        <property name="can_focus">False</property>
+        <property name="spacing">4</property>
+        <child>
+          <object class="GtkLabel" id="label">
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="valign">baseline</property>
+            <property name="label" translatable="yes">(None)</property>
+            <property name="ellipsize">end</property>
+            <property name="xalign">0</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkSeparator" id="separator1">
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkImage" id="open_file_icon">
+            <property name="can_focus">False</property>
+            <property name="valign">baseline</property>
+            <property name="icon_name">document-open-symbolic</property>
+            <property name="icon_size">1</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <style>
+      <class name="file"/>
+    </style>
+  </template>
+</interface>
diff --git a/src/libnma/gcr-object-chooser-dialog.c b/src/libnma/gcr-object-chooser-dialog.c
new file mode 100644
index 0000000..a37fd73
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-dialog.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-object-chooser-dialog.h"
+#include "gcr-object-chooser-widget.h"
+
+#include <gtk/gtk.h>
+
+/**
+ * SECTION:gcr-object-chooser-dialog
+ * @title: GcrObjectChooserDialog
+ * @short_description: The PKCS11 Object Chooser Dialog
+ * @see_also: #GcrObjectChooser
+ *
+ * #GcrObjectChooserDialog is the PKCS11 object chooser dialog.
+ */
+
+struct _GcrObjectChooserDialogPrivate
+{
+       GcrObjectChooserWidget *chooser_widget;
+};
+
+static void gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrObjectChooserDialog, gcr_object_chooser_dialog, GTK_TYPE_DIALOG,
+                         G_ADD_PRIVATE (GcrObjectChooserDialog)
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_OBJECT_CHOOSER, gcr_object_chooser_iface_init));
+
+static void
+object_selected (GcrObjectChooser *chooser, GcrObjectChooserDialog *self)
+{
+       GcrObjectChooserDialogPrivate *priv = GCR_OBJECT_CHOOSER_DIALOG (self)->priv;
+       gchar *uri;
+
+       uri = gcr_object_chooser_get_uri (GCR_OBJECT_CHOOSER (priv->chooser_widget));
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT, uri != NULL);
+       g_free (uri);
+}
+
+static void
+object_activated (GcrObjectChooser *chooser, GcrObjectChooserDialog *self)
+{
+       if (gtk_window_activate_default (GTK_WINDOW (self)))
+               return;
+}
+
+static void
+set_uri (GcrObjectChooser *self, const gchar *uri)
+{
+       GcrObjectChooserDialogPrivate *priv = GCR_OBJECT_CHOOSER_DIALOG (self)->priv;
+
+       return gcr_object_chooser_set_uri (GCR_OBJECT_CHOOSER (priv->chooser_widget), uri);
+}
+
+static gchar *
+get_uri (GcrObjectChooser *self)
+{
+       GcrObjectChooserDialogPrivate *priv = GCR_OBJECT_CHOOSER_DIALOG (self)->priv;
+
+       return gcr_object_chooser_get_uri (GCR_OBJECT_CHOOSER (priv->chooser_widget));
+}
+
+static void
+gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface)
+{
+       iface->set_uri = set_uri;
+       iface->get_uri = get_uri;
+}
+
+static void
+gcr_object_chooser_dialog_class_init (GcrObjectChooserDialogClass *klass)
+{
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       g_type_ensure (GCR_TYPE_OBJECT_CHOOSER_WIDGET);
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gcr/ui/gcr-object-chooser-dialog.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserDialog, chooser_widget);
+       gtk_widget_class_bind_template_callback (widget_class, object_selected);
+       gtk_widget_class_bind_template_callback (widget_class, object_activated);
+}
+
+static void
+gcr_object_chooser_dialog_init (GcrObjectChooserDialog *self)
+{
+       GcrObjectChooserDialogPrivate *priv;
+
+       self->priv = gcr_object_chooser_dialog_get_instance_private (self);
+       priv = self->priv;
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+       g_object_set (G_OBJECT (priv->chooser_widget), "parent-window", self, NULL);
+       gtk_widget_show_all (GTK_WIDGET (priv->chooser_widget));
+}
+
+static GtkWidget *
+gcr_object_chooser_dialog_new_valist (const gchar *title, GtkWindow *parent,
+                                      GtkDialogFlags flags,
+                                      const gchar *first_button_text,
+                                      va_list varargs)
+{
+       GtkWidget *self;
+       const char *button_text = first_button_text;
+       gint response_id;
+
+       self = g_object_new (GCR_TYPE_OBJECT_CHOOSER_DIALOG,
+                              "use-header-bar", !!(flags & GTK_DIALOG_USE_HEADER_BAR),
+                              "title", title,
+                              NULL);
+
+       if (parent)
+               gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+
+       while (button_text) {
+               response_id = va_arg (varargs, gint);
+               gtk_dialog_add_button (GTK_DIALOG (self), button_text, response_id);
+               button_text = va_arg (varargs, const gchar *);
+       }
+
+       gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT, FALSE);
+
+       return self;
+}
+
+/**
+ * gcr_object_chooser_dialog_new:
+ * @title: The dialog window title
+ * @parent: (allow-none): The parent window or %NULL
+ * @flags: The dialog flags
+ * @first_button_text: (allow-none): The text of the first button
+ * @...: response ID for the first button, texts and response ids for other buttons, terminated with %NULL
+ *
+ * Creates the new #GcrObjectChooserDialog.
+ *
+ * Returns: newly created #GcrObjectChooserDialog
+ */
+
+GtkWidget *
+gcr_object_chooser_dialog_new (const gchar *title, GtkWindow *parent,
+                               GtkDialogFlags flags,
+                               const gchar *first_button_text, ...)
+{
+       GtkWidget *result;
+       va_list varargs;
+
+       va_start (varargs, first_button_text);
+       result = gcr_object_chooser_dialog_new_valist (title, parent, flags,
+                                                      first_button_text,
+                                                      varargs);
+       va_end (varargs);
+
+       return result;
+}
diff --git a/src/libnma/gcr-object-chooser-dialog.h b/src/libnma/gcr-object-chooser-dialog.h
new file mode 100644
index 0000000..a2011b0
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-dialog.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GCR_OBJECT_CHOOSER_DIALOG_H__
+#define __GCR_OBJECT_CHOOSER_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+#include "gcr-object-chooser.h"
+
+typedef struct _GcrObjectChooserDialogPrivate GcrObjectChooserDialogPrivate;
+
+struct _GcrObjectChooserDialog
+{
+        GtkDialog parent_instance;
+        GcrObjectChooserDialogPrivate *priv;
+};
+
+#define GCR_TYPE_OBJECT_CHOOSER_DIALOG gcr_object_chooser_dialog_get_type ()
+G_DECLARE_FINAL_TYPE (GcrObjectChooserDialog, gcr_object_chooser_dialog, GCR, OBJECT_CHOOSER_DIALOG, 
GtkDialog)
+
+GtkWidget *gcr_object_chooser_dialog_new (const gchar *title, GtkWindow *parent, GtkDialogFlags flags,
+                                          const gchar *first_button_text, ...);
+
+#endif /* __GCR_OBJECT_CHOOSER_DIALOG_H__ */
diff --git a/src/libnma/gcr-object-chooser-dialog.ui b/src/libnma/gcr-object-chooser-dialog.ui
new file mode 100644
index 0000000..b02fc09
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-dialog.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GcrObjectChooserDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="role">GcrObjectChooserDialog</property>
+    <property name="default_width">600</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GcrObjectChooserWidget" id="chooser_widget">
+            <property name="visible">1</property>
+            <signal name="object-selected" handler="object_selected" swapped="no"/>
+            <signal name="object-activated" handler="object_activated" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup" id="buttons">
+    <property name="mode">vertical</property>
+  </object>
+</interface>
diff --git a/src/libnma/gcr-object-chooser-widget.c b/src/libnma/gcr-object-chooser-widget.c
new file mode 100644
index 0000000..14115ff
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-widget.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-object-chooser.h"
+#include "gcr-object-chooser-widget.h"
+#include "gcr-tokens-sidebar.h"
+#include "gcr-token-login-dialog.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gcr/gcr.h>
+#include <gck/gck.h>
+#include <p11-kit/pkcs11x.h>
+
+/**
+ * SECTION:gcr-object-chooser-widget
+ * @title: GcrObjectChooserWidget
+ * @short_description: The PKCS11 Object Chooser Widget
+ * @see_also: #GcrObjectChooser, #GcrObjectChooserWidget
+ *
+ * #GcrObjectChooserDialog is the PKCS11 object chooser widget.
+ */
+
+enum {
+       COLUMN_LABEL,
+       COLUMN_ISSUER,
+       COLUMN_URI,
+       COLUMN_SLOT,
+       N_COLUMNS
+};
+
+struct _GcrObjectChooserWidgetPrivate
+{
+       GcrTokensSidebar *tokens_sidebar;
+       GtkTreeView *objects_view;
+       GtkTreeViewColumn *list_name_column;
+       GtkCellRenderer *list_name_renderer;
+       GtkTreeViewColumn *list_issued_by_column;
+       GtkCellRenderer *list_issued_by_renderer;
+       GtkLabel *error_label;
+       GtkRevealer *error_revealer;
+
+       GtkListStore *store;
+       GtkTreeModel *filter;
+
+       GtkWindow *parent_window;
+       gchar *uri;
+};
+
+static void gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GcrObjectChooserWidget, gcr_object_chooser_widget, GTK_TYPE_BOX,
+                         G_ADD_PRIVATE (GcrObjectChooserWidget)
+                         G_IMPLEMENT_INTERFACE (GCR_TYPE_OBJECT_CHOOSER, gcr_object_chooser_iface_init));
+
+enum
+{
+       PROP_0,
+       PROP_PARENT_WINDOW,
+};
+
+typedef struct {
+       GcrObjectChooserWidget *self;
+       GckSlot *slot;
+       int remaining;
+       guchar *pin_value;
+       gulong pin_length;
+       gboolean remember_pin;
+} ObjectIterData;
+
+static void
+object_iter_data_maybe_free (ObjectIterData *data)
+{
+       if (data->remaining == 0) {
+               g_object_unref (data->self);
+               if (data->pin_value)
+                       g_free (data->pin_value);
+               g_slice_free (ObjectIterData, data);
+       }
+}
+
+static void
+remove_objects_from_slot (GcrObjectChooserWidget *self, GckSlot *slot)
+{
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GtkTreeIter iter;
+       GckSlot *row_slot;
+       gboolean more;
+
+       more = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter);
+       while (more) {
+               gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, COLUMN_SLOT, &row_slot, -1);
+               if (slot == row_slot)
+                       more = gtk_list_store_remove (priv->store, &iter);
+               else
+                       more = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter);
+       }
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+}
+
+static void
+object_details (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+       GckObject *object = GCK_OBJECT (source_object);
+       ObjectIterData *data = user_data;
+       GcrObjectChooserWidget *self = data->self;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GckAttributes *attrs;
+       GtkTreeIter iter;
+       gulong category;
+       const GckAttribute *attr;
+       GtkTreePath *path;
+       GcrCertificate *cert;
+       gchar *label, *issuer, *uri, *real_uri;
+       GckUriData uri_data = { 0, };
+       GError *error = NULL;
+
+       attrs = gck_object_get_finish (object, res, &error);
+       if (!attrs) {
+               /* No better idea than to just ignore the object. */
+               g_warning ("Error getting attributes: %s\n", error->message);
+               g_error_free (error);
+               goto out;
+       }
+
+       /* Ignore the uninteresting objects.
+        * XXX: There's more to ignore... */
+       if (gck_attributes_find_ulong (attrs, CKA_CERTIFICATE_CATEGORY, &category)) {
+               if (category == 2) /* Authority */
+                       goto out;
+               if (category == 0) /* Other -- distrust */
+                       goto out;
+       }
+
+       attr = gck_attributes_find (attrs, CKA_VALUE);
+       if (attr && attr->value && attr->length) {
+               cert = gcr_simple_certificate_new (attr->value, attr->length);
+               label = gcr_certificate_get_subject_name (cert);
+               issuer = gcr_certificate_get_issuer_name (cert);
+
+               uri_data.attributes = attrs;
+               uri_data.token_info = gck_slot_get_token_info (data->slot);
+               uri = gck_uri_build (&uri_data, GCK_URI_FOR_OBJECT_ON_TOKEN);
+
+               if (data->remember_pin) {
+                       /* XXX: We're assuming that the URI has no attributes. That is the
+                        * case for Gck now, but may change in future? */
+                       real_uri = g_strdup_printf ("%s&pin-value=%s", uri, data->pin_value);
+                       g_free (uri);
+                       uri = real_uri;
+               }
+
+               gtk_list_store_append (priv->store, &iter);
+
+               /* Ignore the attributes, such as PIN. */
+               if (   priv->uri && g_str_has_prefix (priv->uri, uri)
+                   && (priv->uri[strlen(uri)] == '&' || priv->uri[strlen(uri)] == '\0')) {
+                       path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->store), &iter);
+                       gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->objects_view), path, NULL, FALSE);
+                       gtk_tree_path_free (path);
+                       g_free (uri);
+                       uri = g_strdup (priv->uri);
+               }
+
+               gtk_list_store_set (priv->store, &iter,
+                                   COLUMN_LABEL, label,
+                                   COLUMN_ISSUER, issuer,
+                                   COLUMN_URI, uri,
+                                   COLUMN_SLOT, data->slot,
+                                   -1);
+
+               gck_token_info_free (uri_data.token_info);
+               g_free (label);
+               g_free (issuer);
+               g_free (uri);
+               g_object_unref (cert);
+       }
+
+out:
+       if (attrs)
+               gck_attributes_unref (attrs);
+       data->remaining--;
+       object_iter_data_maybe_free (data);
+}
+
+static void
+next_object (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       GckEnumerator *enm = GCK_ENUMERATOR (obj);
+       ObjectIterData *data = user_data;
+       GList *objects;
+       GList *iter;
+       GError *error = NULL;
+
+       objects = gck_enumerator_next_finish (enm, res, &error);
+       if (error) {
+               /* Again, no better idea than to just ignore the object. */
+               g_warning ("Error getting object: %s", error->message);
+               g_error_free (error);
+               goto out;
+       }
+
+       for (iter = objects; iter; iter = iter->next) {
+               GckObject *object = GCK_OBJECT (iter->data);
+               const gulong attr_types[] = { CKA_ID, CKA_LABEL, CKA_ISSUER,
+                                             CKA_VALUE, CKA_CERTIFICATE_CATEGORY };
+
+               data->remaining++;
+               gck_object_get_async (object, attr_types,
+                                     sizeof(attr_types) / sizeof(attr_types[0]),
+                                     NULL, object_details, data);
+       }
+
+       gck_list_unref_free (objects);
+out:
+       object_iter_data_maybe_free (data);
+}
+
+static void
+reload_slot (GckSession *session, ObjectIterData *data)
+{
+       GckBuilder *builder;
+       GckEnumerator *enm;
+
+       remove_objects_from_slot (data->self, data->slot);
+       builder = gck_builder_new (GCK_BUILDER_NONE);
+       gck_builder_add_ulong (builder, CKA_CLASS, CKO_CERTIFICATE);
+       enm = gck_session_enumerate_objects (session, gck_builder_end (builder));
+       gck_enumerator_next_async (enm, -1, NULL, next_object, data);
+}
+
+static void
+logged_in (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       GckSession *session = GCK_SESSION (obj);
+       ObjectIterData *data = user_data;
+       GcrObjectChooserWidget *self = data->self;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GError *error = NULL;
+
+       if (!gck_session_login_finish (session, res, &error)) {
+               g_prefix_error (&error, _("Error logging in: "));
+               gtk_label_set_label (priv->error_label, error->message);
+               gtk_revealer_set_reveal_child (priv->error_revealer, TRUE);
+               g_error_free (error);
+               object_iter_data_maybe_free (data);
+               return;
+       }
+
+       reload_slot (session, data);
+       g_clear_object (&session);
+}
+
+static void
+session_opened (GObject *obj, GAsyncResult *res, gpointer user_data)
+{
+       ObjectIterData *data = user_data;
+       GckSession *session;
+       GError *error = NULL;
+
+       session = gck_slot_open_session_finish (data->slot, res, &error);
+       if (error) {
+               /* It probably doesn't make sense to tell the user, since
+                * there's not much they could do about it. It could be that
+                * this occured on the initial token scan for multiple tokens
+                * and the errors would displace the other one anyway. */
+               g_warning ("Error opening a session: %s", error->message);
+               g_error_free (error);
+               object_iter_data_maybe_free (data);
+               return;
+       }
+
+       if (data->pin_value) {
+               gck_session_login_async (session, CKU_USER,
+                                        data->pin_value, data->pin_length,
+                                        NULL, logged_in, data);
+       } else {
+               reload_slot (session, data);
+               g_clear_object (&session);
+       }
+}
+
+static void
+token_login (GcrTokensSidebar *tokens_sidebar, GckSlot *slot, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GtkWidget *dialog;
+       ObjectIterData *data;
+       GckTokenInfo *token_info;
+       gboolean has_pin_pad = FALSE;
+
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+
+       /* See if the token has a PIN pad. */
+       token_info = gck_slot_get_token_info (slot);
+       if (token_info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+               has_pin_pad = TRUE;
+       gck_token_info_free (token_info);
+
+       if (has_pin_pad) {
+               /* Login with empty credentials makes the token
+                * log in on its PIN pad. */
+               data = g_slice_alloc0 (sizeof (ObjectIterData));
+               data->self = g_object_ref (self);
+               data->slot = slot;
+               data->pin_length = 0;
+               data->pin_value =  g_memdup ("", 1);
+               data->remember_pin = TRUE;
+               gck_slot_open_session_async (slot, GCK_SESSION_READ_ONLY, NULL, session_opened, data);
+               return;
+       }
+
+       /* The token doesn't have a PIN pad. Ask for PIN. */
+        dialog = gcr_token_login_dialog_new (slot);
+       gtk_window_set_transient_for (GTK_WINDOW (dialog), priv->parent_window);
+       gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+
+       if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
+               data = g_slice_alloc0 (sizeof (ObjectIterData));
+               data->self = g_object_ref (self);
+               data->slot = slot;
+               data->pin_length = gcr_token_login_dialog_get_pin_length (GCR_TOKEN_LOGIN_DIALOG (dialog));
+               data->pin_value = g_memdup (gcr_token_login_dialog_get_pin_value (GCR_TOKEN_LOGIN_DIALOG 
(dialog)),
+                                           data->pin_length + 1);
+               data->remember_pin = gcr_token_login_dialog_get_remember_pin (GCR_TOKEN_LOGIN_DIALOG 
(dialog));
+               gck_slot_open_session_async (slot, GCK_SESSION_READ_ONLY, NULL, session_opened, data);
+       }
+
+       gtk_widget_destroy (dialog);
+}
+
+static void
+token_added (GcrTokensSidebar *tokens_sidebar, GckSlot *slot, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+       ObjectIterData *data;
+
+       data = g_slice_alloc0 (sizeof (ObjectIterData));
+       data->self = g_object_ref (self);
+       data->slot = slot;
+       gck_slot_open_session_async (slot, GCK_SESSION_READ_ONLY, NULL, session_opened, data);
+}
+
+static void
+open_token (GcrTokensSidebar *tokens_sidebar, GckSlot *slot, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
+}
+
+static void
+set_uri (GcrObjectChooser *self, const gchar *uri)
+{
+       GcrObjectChooserWidgetPrivate *priv = GCR_OBJECT_CHOOSER_WIDGET (self)->priv;
+
+       if (priv->uri)
+               g_free (priv->uri);
+       priv->uri = g_strdup (uri);
+}
+
+static gchar *
+get_uri (GcrObjectChooser *chooser)
+{
+       GcrObjectChooserWidget *self = GCR_OBJECT_CHOOSER_WIDGET (chooser);
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GtkTreePath *path;
+       GtkTreeIter iter;
+       gchar *uri;
+
+       gtk_tree_view_get_cursor (priv->objects_view, &path, NULL);
+       if (path == NULL)
+               return NULL;
+
+       if (!gtk_tree_model_get_iter (priv->filter, &iter, path))
+               g_return_val_if_reached (NULL);
+
+       gtk_tree_model_get (priv->filter, &iter, COLUMN_URI, &uri, -1);
+
+       return uri;
+}
+
+static void
+row_activated (GtkTreeView *tree_view, GtkTreePath *path,
+               GtkTreeViewColumn *column, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+
+       g_signal_emit_by_name (self, "object-activated");
+}
+
+static void
+cursor_changed (GtkTreeView *tree_view, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+
+       g_signal_emit_by_name (self, "object-selected");
+}
+
+
+static gboolean
+slot_visible (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+       GcrObjectChooserWidget *self = data;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+       GckSlot *active_slot;
+
+       active_slot = gcr_tokens_sidebar_get_slot (priv->tokens_sidebar);
+       if (active_slot) {
+               GckSlot *slot;
+
+               gtk_tree_model_get (model, iter, COLUMN_SLOT, &slot, -1);
+               if (slot)
+                       g_object_unref (slot);
+               return slot == active_slot;
+       } else {
+               /* All tokens. */
+               return TRUE;
+       }
+}
+
+static void
+error_close (GtkInfoBar *bar, gint response_id, gpointer user_data)
+{
+       GcrObjectChooserWidget *self = user_data;
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+
+       gtk_revealer_set_reveal_child (priv->error_revealer, FALSE);
+}
+
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+       GcrObjectChooserWidget *self = GCR_OBJECT_CHOOSER_WIDGET (object);
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+
+       switch (prop_id) {
+       case PROP_PARENT_WINDOW:
+               if (priv->parent_window)
+                       g_value_set_object (value, priv->parent_window);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+       GcrObjectChooserWidget *self = GCR_OBJECT_CHOOSER_WIDGET (object);
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+
+       switch (prop_id) {
+       case PROP_PARENT_WINDOW:
+               priv->parent_window = g_value_dup_object (value);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+finalize (GObject *object)
+{
+       GcrObjectChooserWidget *self = GCR_OBJECT_CHOOSER_WIDGET (object);
+       GcrObjectChooserWidgetPrivate *priv = self->priv;
+
+       g_clear_object (&priv->store);
+       g_clear_object (&priv->filter);
+       g_clear_object (&priv->parent_window);
+
+       if (priv->uri)
+               g_free (priv->uri);
+
+       G_OBJECT_CLASS (gcr_object_chooser_widget_parent_class)->finalize (object);
+}
+
+static void
+gcr_object_chooser_iface_init (GcrObjectChooserInterface *iface)
+{
+       iface->set_uri = set_uri;
+       iface->get_uri = get_uri;
+}
+
+static void
+gcr_object_chooser_widget_class_init (GcrObjectChooserWidgetClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+       object_class->finalize = finalize;
+
+       g_object_class_install_property (object_class, PROP_PARENT_WINDOW,
+               g_param_spec_object ("parent-window", "Parent Window", "Window to which to attach login 
idalogs",
+                                    GTK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       g_type_ensure (GCR_TYPE_TOKENS_SIDEBAR);
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gcr/ui/gcr-object-chooser-widget.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, tokens_sidebar);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, objects_view);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, list_name_column);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, 
list_name_renderer);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, 
list_issued_by_column);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, 
list_issued_by_renderer);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, error_revealer);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrObjectChooserWidget, error_label);
+       gtk_widget_class_bind_template_callback (widget_class, token_added);
+       gtk_widget_class_bind_template_callback (widget_class, token_login);
+       gtk_widget_class_bind_template_callback (widget_class, open_token);
+       gtk_widget_class_bind_template_callback (widget_class, row_activated);
+       gtk_widget_class_bind_template_callback (widget_class, cursor_changed);
+       gtk_widget_class_bind_template_callback (widget_class, error_close);
+}
+
+static void
+gcr_object_chooser_widget_init (GcrObjectChooserWidget *self)
+{
+       GcrObjectChooserWidgetPrivate *priv;
+
+       self->priv = gcr_object_chooser_widget_get_instance_private (self);
+       priv = self->priv;
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+
+       gtk_tree_view_column_set_attributes (priv->list_name_column, priv->list_name_renderer, "text", 0, 
NULL);
+       gtk_tree_view_column_set_attributes (priv->list_issued_by_column, priv->list_issued_by_renderer, 
"text", 1, NULL);
+
+       priv->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, 
GCK_TYPE_SLOT);
+       priv->filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store), NULL);
+       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter), slot_visible, self, 
NULL);
+       gtk_tree_view_set_model (GTK_TREE_VIEW (priv->objects_view), GTK_TREE_MODEL (priv->filter));
+       gtk_widget_set_size_request (GTK_WIDGET (priv->objects_view), 380, -1);
+}
+
+/**
+ * gcr_object_chooser_widget_new:
+ * @parent_window: (allow-none): The parent window or %NULL
+ *
+ * Creates the new #GcrObjectChooserWidget. The optional parent window
+ * will be used to attach the PIN prompts to.
+ *
+ * Returns: newly created #GcrObjectChooserWidget
+ */
+
+GtkWidget *
+gcr_object_chooser_widget_new (GtkWindow *parent_window)
+{
+       return GTK_WIDGET (g_object_new (GCR_TYPE_OBJECT_CHOOSER_WIDGET,
+                                        "parent-window", parent_window,
+                                        NULL, NULL));
+}
diff --git a/src/libnma/gcr-object-chooser-widget.h b/src/libnma/gcr-object-chooser-widget.h
new file mode 100644
index 0000000..24eb5dd
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-widget.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GCR_OBJECT_CHOOSER_WIDGET_H__
+#define __GCR_OBJECT_CHOOSER_WIDGET_H__
+
+#include <gtk/gtk.h>
+
+#include "gcr-object-chooser.h"
+
+typedef struct _GcrObjectChooserWidgetPrivate GcrObjectChooserWidgetPrivate;
+
+struct _GcrObjectChooserWidget
+{
+        GtkBox parent_instance;
+        GcrObjectChooserWidgetPrivate *priv;
+};
+
+#define GCR_TYPE_OBJECT_CHOOSER_WIDGET gcr_object_chooser_widget_get_type ()
+G_DECLARE_FINAL_TYPE (GcrObjectChooserWidget, gcr_object_chooser_widget, GCR, OBJECT_CHOOSER_WIDGET, GtkBox)
+
+GtkWidget *gcr_object_chooser_widget_new (GtkWindow *parent_window);
+
+#endif /* __GCR_OBJECT_CHOOSER_WIDGET_H__ */
diff --git a/src/libnma/gcr-object-chooser-widget.ui b/src/libnma/gcr-object-chooser-widget.ui
new file mode 100644
index 0000000..84b817b
--- /dev/null
+++ b/src/libnma/gcr-object-chooser-widget.ui
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GcrObjectChooserWidget" parent="GtkBox">
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkRevealer" id="error_revealer">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkInfoBar" id="error_bar">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="message_type">error</property>
+            <property name="show_close_button">True</property>
+            <signal name="response" handler="error_close" swapped="no"/>
+            <child internal-child="action_area">
+              <object class="GtkButtonBox">
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child internal-child="content_area">
+              <object class="GtkBox">
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="error_label">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkPaned" id="paned">
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GcrTokensSidebar" id="tokens_sidebar">
+        <property name="can_focus">False</property>
+        <property name="hscrollbar_policy">never</property>
+        <signal name="token-added" handler="token_added" swapped="no"/>
+        <signal name="token-login" handler="token_login" swapped="no"/>
+        <signal name="open-token" handler="open_token" swapped="no"/>
+        <style>
+          <class name="sidebar"/>
+        </style>
+      </object>
+          <packing>
+            <property name="resize">False</property>
+            <property name="shrink">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow">
+            <property name="can_focus">False</property>
+            <property name="hscrollbar_policy">never</property>
+            <child>
+              <object class="GtkTreeView" id="objects_view">
+                <property name="can_focus">False</property>
+                <property name="enable_search">False</property>
+                <signal name="cursor-changed" handler="cursor_changed" swapped="no"/>
+                <signal name="row-activated" handler="row_activated" swapped="no"/>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="objects_view_selection"/>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="list_name_column">
+                    <property name="title" translatable="yes">Name</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="list_name_renderer">
+                        <property name="xpad">6</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkTreeViewColumn" id="list_issued_by_column">
+                    <property name="title" translatable="yes">Issued By</property>
+                    <child>
+                      <object class="GtkCellRendererText" id="list_issued_by_renderer">
+                        <property name="xpad">6</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="resize">True</property>
+            <property name="shrink">True</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/src/libnma/gcr-object-chooser.c b/src/libnma/gcr-object-chooser.c
new file mode 100644
index 0000000..6a9f869
--- /dev/null
+++ b/src/libnma/gcr-object-chooser.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-object-chooser.h"
+#include "gcr-tokens-sidebar.h"
+
+#include <gtk/gtk.h>
+
+/**
+ * SECTION:gcr-object-chooser
+ * @title: GcrObjectChooser
+ * @short_description: The PKCS11 Object Chooser Interface
+ * @see_also: #GcrObjectChooserDialog
+ *
+ * #GcrObjectChooser is an interface for PKCS11 object chooser widgets.
+ */
+
+G_DEFINE_INTERFACE (GcrObjectChooser, gcr_object_chooser, G_TYPE_OBJECT)
+
+static void
+gcr_object_chooser_default_init (GcrObjectChooserInterface *iface)
+{
+
+       /**
+        * GcrObjectChooser::object-activated
+        *
+        * Emitted when an object choice is confirmed (e.g. with a double click).
+        */
+       g_signal_new ("object-activated",
+                     G_TYPE_FROM_INTERFACE (iface),
+                     G_SIGNAL_RUN_LAST,
+                     0, NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE, 0);
+
+       /**
+        * GcrObjectChooser::object-selected
+        *
+        * Emitted when the object is selected; not yet confirmed.
+        */
+       g_signal_new ("object-selected",
+                     G_TYPE_FROM_INTERFACE (iface),
+                     G_SIGNAL_RUN_LAST,
+                     0, NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE, 0);
+}
+
+/**
+ * gcr_object_chooser_set_uri:
+ * @self: the #GcrObjectChooser
+ * @uri: the object URI
+ *
+ * Set the URI of the focused object. If it's not yet added, then it will
+ * be focused as soon as it appears.
+ */
+void
+gcr_object_chooser_set_uri (GcrObjectChooser *self, const gchar *uri)
+{
+       g_return_if_fail (GCR_IS_OBJECT_CHOOSER (self));
+
+       GCR_OBJECT_CHOOSER_GET_IFACE (self)->set_uri (self, uri);
+}
+
+/**
+ * gcr_object_chooser_get_uri:
+ * @self: the #GcrObjectChooser
+ *
+ * Get the URI of the currently selected object.
+ *
+ * Returns: (transfer full): the URI of the object.
+ */
+gchar *
+gcr_object_chooser_get_uri (GcrObjectChooser *self)
+{
+       g_return_val_if_fail (GCR_IS_OBJECT_CHOOSER (self), NULL);
+
+       return GCR_OBJECT_CHOOSER_GET_IFACE (self)->get_uri (self);
+}
diff --git a/src/libnma/gcr-object-chooser.h b/src/libnma/gcr-object-chooser.h
new file mode 100644
index 0000000..204be13
--- /dev/null
+++ b/src/libnma/gcr-object-chooser.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __GCR_OBJECT_CHOOSER_H__
+#define __GCR_OBJECT_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+typedef struct _GcrObjectChooser GcrObjectChooser;
+
+struct _GcrObjectChooserInterface
+{
+       GTypeInterface g_iface;
+
+       void (*set_uri) (GcrObjectChooser *self, const gchar *uri);
+       gchar *(*get_uri) (GcrObjectChooser *self);
+};
+
+#define GCR_TYPE_OBJECT_CHOOSER gcr_object_chooser_get_type ()
+G_DECLARE_INTERFACE (GcrObjectChooser, gcr_object_chooser, GCR, OBJECT_CHOOSER, GObject)
+
+void gcr_object_chooser_set_uri (GcrObjectChooser *self, const gchar *uri);
+
+gchar *gcr_object_chooser_get_uri (GcrObjectChooser *self);
+
+#endif /* __GCR_OBJECT_CHOOSER_H__ */
diff --git a/src/libnma/gcr-token-login-dialog.c b/src/libnma/gcr-token-login-dialog.c
new file mode 100644
index 0000000..2687757
--- /dev/null
+++ b/src/libnma/gcr-token-login-dialog.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-token-login-dialog.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+/**
+ * SECTION:gcr-token-login-dialog
+ * @title: GcrTokenLoginDialog
+ * @short_description: The PKCS11 PIN Dialog
+ * @see_also: #GcrObjectChooserDialog
+ *
+ * #GcrTokenLoginDialog asks for the PKCS11 login pin.
+ * It enforces the PIN constrains (maximum & minimum length).
+ *
+ * Used by the #GcrObjectChooserDialog when the #GcrTokensSidebar indicates
+ * that the user requested the token to be logged in.
+ */
+
+struct _GcrTokenLoginDialogPrivate
+{
+       GckSlot *slot;
+       GckTokenInfo *info;
+
+       GtkEntry *pin_entry;
+       GtkCheckButton *remember;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GcrTokenLoginDialog, gcr_token_login_dialog, GTK_TYPE_DIALOG,
+                         G_ADD_PRIVATE (GcrTokenLoginDialog));
+
+enum
+{
+       PROP_0,
+       PROP_TOKEN_SLOT,
+};
+
+/**
+ * gcr_token_login_dialog_get_pin_value:
+ * @self: The #GcrTokenLoginDialog
+ *
+ * Returns: the entered PIN
+ */
+
+const guchar *
+gcr_token_login_dialog_get_pin_value (GcrTokenLoginDialog *self)
+{
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+       GtkEntryBuffer *buffer = gtk_entry_get_buffer (priv->pin_entry);
+
+       return (guchar *) gtk_entry_buffer_get_text (buffer);
+}
+
+/**
+ * gcr_token_login_dialog_get_pin_length:
+ * @self: The #GcrTokenLoginDialog
+ *
+ * Returns: the PIN length
+ */
+
+gulong
+gcr_token_login_dialog_get_pin_length (GcrTokenLoginDialog *self)
+{
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+       GtkEntryBuffer *buffer = gtk_entry_get_buffer (priv->pin_entry);
+
+       return gtk_entry_buffer_get_bytes (buffer);
+}
+
+/**
+ * gcr_token_login_dialog_get_remember_pin:
+ * @self: The #GcrTokenLoginDialog
+ *
+ * Returns: %TRUE if the "Remember PIN" checkbox was checked
+ */
+
+gboolean
+gcr_token_login_dialog_get_remember_pin (GcrTokenLoginDialog *self)
+{
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+
+       return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->remember));
+}
+
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+       GcrTokenLoginDialog *self = GCR_TOKEN_LOGIN_DIALOG (object);
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+
+       switch (prop_id) {
+       case PROP_TOKEN_SLOT:
+               if (priv->slot)
+                       g_value_set_object (value, priv->slot);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static gboolean
+can_activate (GcrTokenLoginDialog *self)
+{
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+       GtkEntryBuffer *buffer = gtk_entry_get_buffer (priv->pin_entry);
+       guint len = gtk_entry_buffer_get_length (buffer);
+
+       return len <= priv->info->max_pin_len && len >= priv->info->min_pin_len;
+}
+
+static void
+set_slot (GcrTokenLoginDialog *self, GckSlot *slot)
+{
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+       gchar *title;
+
+       g_clear_object (&priv->slot);
+       if (priv->info)
+               gck_token_info_free (priv->info);
+
+       priv->slot = slot;
+       priv->info = gck_slot_get_token_info (slot);
+
+       title = g_strdup_printf (_("Enter %s PIN"), priv->info->label);
+       gtk_window_set_title (GTK_WINDOW (self), title);
+       g_free (title);
+
+       gtk_entry_set_max_length (priv->pin_entry, priv->info->max_pin_len);
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT,
+                                          can_activate (self));
+}
+
+
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+       GcrTokenLoginDialog *self = GCR_TOKEN_LOGIN_DIALOG (object);
+
+       switch (prop_id) {
+       case PROP_TOKEN_SLOT:
+               set_slot (self, g_value_dup_object (value));
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+finalize (GObject *object)
+{
+       GcrTokenLoginDialog *self = GCR_TOKEN_LOGIN_DIALOG (object);
+       GcrTokenLoginDialogPrivate *priv = self->priv;
+
+       g_clear_object (&priv->slot);
+       if (priv->info) {
+               gck_token_info_free (priv->info);
+               priv->info = NULL;
+       }
+
+       G_OBJECT_CLASS (gcr_token_login_dialog_parent_class)->finalize (object);
+}
+
+static void
+pin_changed (GtkEditable *editable, gpointer user_data)
+{
+       gtk_dialog_set_response_sensitive (GTK_DIALOG (user_data), GTK_RESPONSE_ACCEPT,
+                                          can_activate (GCR_TOKEN_LOGIN_DIALOG (user_data)));
+}
+
+
+static void
+pin_activate (GtkEditable *editable, gpointer user_data)
+{
+       if (can_activate (GCR_TOKEN_LOGIN_DIALOG (user_data)))
+               gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+gcr_token_login_dialog_class_init (GcrTokenLoginDialogClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+       object_class->finalize = finalize;
+
+       /**
+        * GcrTokenLoginDialog::token-slot:
+        *
+        * Slot that contains the pin for which the dialog requests
+        * the PIN.
+        */
+       g_object_class_install_property (object_class, PROP_TOKEN_SLOT,
+               g_param_spec_object ("token-slot", "Slot", "Slot containing the Token",
+                                    GCK_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gcr/ui/gcr-token-login-dialog.ui");
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokenLoginDialog, pin_entry);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokenLoginDialog, remember);
+       gtk_widget_class_bind_template_callback (widget_class, pin_changed);
+       gtk_widget_class_bind_template_callback (widget_class, pin_activate);
+}
+
+static void
+gcr_token_login_dialog_init (GcrTokenLoginDialog *self)
+{
+       self->priv = gcr_token_login_dialog_get_instance_private (self);
+
+       gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * gcr_token_login_dialog_new:
+ * @slot: Slot that contains the pin for which the dialog requests the PIN
+ *
+ * Creates the new PKCS11 login dialog.
+ *
+ * Returns: the newly created #GcrTokenLoginDialog
+ */
+GtkWidget *
+gcr_token_login_dialog_new (GckSlot *slot)
+{
+       return GTK_WIDGET (g_object_new (GCR_TYPE_TOKEN_LOGIN_DIALOG,
+"use-header-bar", TRUE,
+                          "token-slot", slot,
+                          NULL));
+#if 0
+GtkDialogFlags flags,
+                              "use-header-bar", !!(flags & GTK_DIALOG_USE_HEADER_BAR),
+                              "title", title,
+#endif
+}
diff --git a/src/libnma/gcr-token-login-dialog.h b/src/libnma/gcr-token-login-dialog.h
new file mode 100644
index 0000000..141f1d2
--- /dev/null
+++ b/src/libnma/gcr-token-login-dialog.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GCR_TOKEN_LOGIN_DIALOG_H__
+#define __GCR_TOKEN_LOGIN_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+typedef struct _GcrTokenLoginDialogPrivate GcrTokenLoginDialogPrivate;
+
+struct _GcrTokenLoginDialog
+{
+        GtkDialog parent_instance;
+        GcrTokenLoginDialogPrivate *priv;
+};
+
+#define GCR_TYPE_TOKEN_LOGIN_DIALOG gcr_token_login_dialog_get_type ()
+G_DECLARE_FINAL_TYPE (GcrTokenLoginDialog, gcr_token_login_dialog, GCR, TOKEN_LOGIN_DIALOG, GtkDialog)
+
+GtkWidget *gcr_token_login_dialog_new (GckSlot *slot);
+
+const guchar *gcr_token_login_dialog_get_pin_value (GcrTokenLoginDialog *self);
+
+gulong gcr_token_login_dialog_get_pin_length (GcrTokenLoginDialog *self);
+
+gboolean gcr_token_login_dialog_get_remember_pin (GcrTokenLoginDialog *self);
+
+#endif /* __GCR_TOKEN_LOGIN_DIALOG_H__ */
diff --git a/src/libnma/gcr-token-login-dialog.ui b/src/libnma/gcr-token-login-dialog.ui
new file mode 100644
index 0000000..df66b0b
--- /dev/null
+++ b/src/libnma/gcr-token-login-dialog.ui
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <template class="GcrTokenLoginDialog" parent="GtkDialog">
+    <property name="can_focus">False</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel">
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="pack_type">end</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="login">
+                <property name="label" translatable="yes">_Login</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">12</property>
+            <property name="margin_right">12</property>
+            <property name="margin_top">12</property>
+            <property name="margin_bottom">12</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkEntry" id="pin_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="visibility">False</property>
+                <property name="input_purpose">password</property>
+                <signal name="activate" handler="pin_activate" swapped="no"/>
+                <signal name="changed" handler="pin_changed" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">6</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="remember">
+                <property name="label" translatable="yes">_Remember PIN</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="halign">start</property>
+                <property name="use_underline">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="padding">6</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel</action-widget>
+      <action-widget response="-3">login</action-widget>
+    </action-widgets>
+  </template>
+</interface>
diff --git a/src/libnma/gcr-tokens-sidebar-row.c b/src/libnma/gcr-tokens-sidebar-row.c
new file mode 100644
index 0000000..4c9229a
--- /dev/null
+++ b/src/libnma/gcr-tokens-sidebar-row.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-tokens-sidebar-row.h"
+
+#include <glib/gi18n.h>
+#include <gck/gck.h>
+
+/**
+ * SECTION:gcr-tokens-sidebar-row
+ * @title: GcrTokensSidebarRow
+ * @short_description: The PKCS11 Tokens Sidebar Row
+ * @see_also: #GcrTokensSidebarRow
+ *
+ * #GcrTokensSidebarRow is the tokens sidebar row widget.
+ */
+
+enum {
+       TOKEN_LOGIN,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GcrTokensSidebarRowPrivate
+{
+       GckSlot *slot;
+       GtkEventBox *event_box;
+       GtkImage *icon_widget;
+       GtkLabel *label_widget;
+       GtkButton *login_button;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GcrTokensSidebarRow, gcr_tokens_sidebar_row, GTK_TYPE_LIST_BOX_ROW,
+                         G_ADD_PRIVATE (GcrTokensSidebarRow));
+
+enum
+{
+       PROP_0,
+       PROP_SLOT,
+};
+
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+       GcrTokensSidebarRow *self = GCR_TOKENS_SIDEBAR_ROW (object);
+       GcrTokensSidebarRowPrivate *priv = self->priv;
+
+       switch (prop_id) {
+       case PROP_SLOT:
+               if (priv->slot)
+                       g_value_set_object (value, priv->slot);
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+       GcrTokensSidebarRow *self = GCR_TOKENS_SIDEBAR_ROW (object);
+       GcrTokensSidebarRowPrivate *priv = self->priv;
+       gboolean needs_login = FALSE;
+
+       switch (prop_id) {
+       case PROP_SLOT:
+               /* Construct only. */
+               priv->slot = g_value_dup_object (value);
+
+               if (priv->slot) {
+                       GckTokenInfo *info;
+
+                       info = gck_slot_get_token_info (priv->slot);
+                       g_return_if_fail (info);
+                       gtk_label_set_text (priv->label_widget, info->label);
+                       needs_login = info->flags & CKF_LOGIN_REQUIRED;
+                       gck_token_info_free (info);
+               } else {
+                       gtk_widget_hide (GTK_WIDGET (priv->icon_widget));
+                       gtk_widget_set_no_show_all (GTK_WIDGET (priv->icon_widget), TRUE);
+                       gtk_label_set_markup (priv->label_widget, _("<b>All tokens</b>"));
+               }
+
+               if (!needs_login) {
+                       gtk_widget_hide (GTK_WIDGET (priv->login_button));
+                       gtk_widget_set_no_show_all (GTK_WIDGET (priv->login_button), TRUE);
+               }
+
+               break;
+       default:
+               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+finalize (GObject *object)
+{
+       GcrTokensSidebarRow *self = GCR_TOKENS_SIDEBAR_ROW (object);
+       GcrTokensSidebarRowPrivate *priv = self->priv;
+
+       g_clear_object (&priv->slot);
+
+       G_OBJECT_CLASS (gcr_tokens_sidebar_row_parent_class)->finalize (object);
+}
+
+
+static void
+login_clicked (GtkButton *button, gpointer user_data)
+{
+       GcrTokensSidebarRow *self = GCR_TOKENS_SIDEBAR_ROW (user_data);
+
+       g_signal_emit (self, signals[TOKEN_LOGIN], 0);
+}
+
+static void
+gcr_tokens_sidebar_row_class_init (GcrTokensSidebarRowClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->get_property = get_property;
+       object_class->set_property = set_property;
+       object_class->finalize = finalize;
+
+       /**
+        * GcrTokensSidebarRowClass::token-login:
+        *
+        * Emitted with the login button is clicked.
+        */
+       signals[TOKEN_LOGIN] = g_signal_new ("token-login",
+                                            G_OBJECT_CLASS_TYPE (object_class),
+                                            G_SIGNAL_RUN_FIRST,
+                                            0, NULL, NULL,
+                                            g_cclosure_marshal_VOID__VOID,
+                                            G_TYPE_NONE,
+                                            0);
+
+       /**
+        * GcrTokensSidebarRowClass::slot:
+        *
+        * The PKCS#11 slot or %NULL for all slots.
+        */
+       g_object_class_install_property (object_class, PROP_SLOT,
+               g_param_spec_object ("slot", "Slot", "PKCS#11 Slot",
+                                    GCK_TYPE_SLOT,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+       gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/gcr/ui/gcr-tokens-sidebar-row.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokensSidebarRow, event_box);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokensSidebarRow, icon_widget);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokensSidebarRow, label_widget);
+       gtk_widget_class_bind_template_child_private (widget_class, GcrTokensSidebarRow, login_button);
+       gtk_widget_class_bind_template_callback (widget_class, login_clicked);
+
+       gtk_widget_class_set_css_name (widget_class, "row");
+}
+
+static void
+gcr_tokens_sidebar_row_init (GcrTokensSidebarRow *self)
+{
+       self->priv = gcr_tokens_sidebar_row_get_instance_private (self);
+       gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * gcr_tokens_sidebar_row_get_slot:
+ * @self: The #GcrTokensSidebarRow
+ *
+ * The slot getter.
+ *
+ * Returns: the associated slot, or %NULL
+ */
+GckSlot *
+gcr_tokens_sidebar_row_get_slot (GcrTokensSidebarRow *self)
+{
+       GcrTokensSidebarRowPrivate *priv = self->priv;
+
+       return priv->slot;
+}
+
+/**
+ * gcr_tokens_sidebar_row_new:
+ * @slot: the associated %slot
+ *
+ * Creates the new #GcrTokensSidebarRow for given slot.
+ * If %NULL is given for the slot, then the "All slots" row
+ * is created.
+ *
+ * Returns: the newly created #GcrTokensSidebarRow
+ */
+GtkWidget *
+gcr_tokens_sidebar_row_new (GckSlot *slot)
+{
+       return GTK_WIDGET (g_object_new (GCR_TYPE_TOKENS_SIDEBAR_ROW,
+                                        "slot", slot, NULL));
+}
diff --git a/src/libnma/gcr-tokens-sidebar-row.h b/src/libnma/gcr-tokens-sidebar-row.h
new file mode 100644
index 0000000..56b7cd9
--- /dev/null
+++ b/src/libnma/gcr-tokens-sidebar-row.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GCR_TOKENS_SIDEBAR_ROW_H__
+#define __GCR_TOKENS_SIDEBAR_ROW_H__
+
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+typedef struct _GcrTokensSidebarRowPrivate GcrTokensSidebarRowPrivate;
+
+struct _GcrTokensSidebarRow
+{
+       GtkListBoxRow parent_instance;
+       GcrTokensSidebarRowPrivate *priv;
+};
+
+#define GCR_TYPE_TOKENS_SIDEBAR_ROW gcr_tokens_sidebar_row_get_type ()
+G_DECLARE_FINAL_TYPE (GcrTokensSidebarRow, gcr_tokens_sidebar_row, GCR, TOKENS_SIDEBAR_ROW, GtkListBoxRow);
+
+GtkWidget *gcr_tokens_sidebar_row_new (GckSlot *slot);
+
+GckSlot *gcr_tokens_sidebar_row_get_slot (GcrTokensSidebarRow *self);
+
+#endif /* __GCR_TOKENS_SIDEBAR_ROW_H__ */
diff --git a/src/libnma/gcr-tokens-sidebar-row.ui b/src/libnma/gcr-tokens-sidebar-row.ui
new file mode 100644
index 0000000..c27d494
--- /dev/null
+++ b/src/libnma/gcr-tokens-sidebar-row.ui
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.10"/>
+  <template class="GcrTokensSidebarRow" parent="GtkListBoxRow">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="margin_top">1</property>
+    <property name="margin_bottom">1</property>
+    <child>
+      <object class="GtkRevealer" id="revealer">
+        <property name="visible">1</property>
+        <property name="reveal-child">1</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkEventBox" id="event_box">
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkBox">
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkImage" id="icon_widget">
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">media-flash-symbolic</property>
+                    <style>
+                      <class name="sidebar-icon"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label_widget">
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">False</property>
+                    <property name="xalign">0</property>
+                    <style>
+                      <class name="sidebar-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="login_button">
+                    <property name="can_focus">False</property>
+                    <property name="receives_default">False</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="margin_start">4</property>
+                    <signal name="clicked" handler="login_clicked" swapped="no"/>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">dialog-password-symbolic</property>
+                        <property name="icon_size">1</property>
+                      </object>
+                    </child>
+                    <style>
+                      <class name="image-button"/>
+                      <class name="sidebar-button"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <style>
+          <class name="sidebar-revealer"/>
+        </style>
+      </object>
+    </child>
+    <style>
+      <class name="sidebar-row"/>
+    </style>
+  </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">vertical</property>
+    <widgets>
+      <widget name="login_button"/>
+      <widget name="label_widget"/>
+      <widget name="icon_widget"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/src/libnma/gcr-tokens-sidebar.c b/src/libnma/gcr-tokens-sidebar.c
new file mode 100644
index 0000000..a75c2f1
--- /dev/null
+++ b/src/libnma/gcr-tokens-sidebar.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gcr-tokens-sidebar.h"
+#include "gcr-tokens-sidebar-row.h"
+
+#include <gck/gck.h>
+
+/**
+ * SECTION:gcr-tokens-sidebar
+ * @title: GcrTokensSidebar
+ * @short_description: The PKCS11 Tokens Sidebar
+ * @see_also: #GcrObjectChooserDialog
+ *
+ * #GcrTokensSidebar is the list of selectable PKCS11 slots present in the
+ * system. The slots that support PIN have a login button.
+ */
+
+enum {
+       TOKEN_ADDED,
+       TOKEN_LOGIN,
+       OPEN_TOKEN,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GcrTokensSidebarPrivate
+{
+       GtkWidget *viewport;
+       GtkWidget *listbox;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GcrTokensSidebar, gcr_tokens_sidebar, GTK_TYPE_SCROLLED_WINDOW,
+                         G_ADD_PRIVATE (GcrTokensSidebar));
+
+
+static void
+token_login (GcrTokensSidebarRow *row, gpointer user_data)
+{
+       GcrTokensSidebar *self = user_data;
+       GcrTokensSidebarPrivate *priv = self->priv;
+
+       gtk_list_box_select_row (GTK_LIST_BOX (priv->listbox), GTK_LIST_BOX_ROW (row));
+
+       g_signal_emit (self, signals[TOKEN_LOGIN], 0,
+                      gcr_tokens_sidebar_row_get_slot (row));
+}
+
+static void
+gcr_tokens_sidebar_add_slot (GcrTokensSidebar *self, GckSlot *slot)
+{
+       GcrTokensSidebarPrivate *priv = self->priv;
+       GtkWidget *row;
+
+       row = gcr_tokens_sidebar_row_new (slot);
+       g_signal_connect (row, "token-login", G_CALLBACK (token_login), self);
+
+       gtk_container_add (GTK_CONTAINER (priv->listbox), row);
+       gtk_widget_show_all (row);
+}
+
+static void
+modules_initialized (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+       GcrTokensSidebar *self = GCR_TOKENS_SIDEBAR (user_data);
+       GList *slots;
+       GList *iter;
+       GError *error = NULL;
+       GList *modules;
+
+       modules = gck_modules_initialize_registered_finish (res, &error);
+       if (!modules) {
+               /* The Front Fell Off. */
+               g_critical ("Error getting registered modules: %s", error->message);
+               g_error_free (error);
+       }
+
+       slots = gck_modules_get_slots (modules, FALSE);
+
+       for (iter = slots; iter; iter = iter->next) {
+               GckSlot *slot = GCK_SLOT (iter->data);
+
+               g_signal_emit (self, signals[TOKEN_ADDED], 0, slot);
+               gcr_tokens_sidebar_add_slot (self, slot);
+       }
+
+       gck_list_unref_free (slots);
+       gck_list_unref_free (modules);
+}
+
+static void
+gcr_tokens_sidebar_class_init (GcrTokensSidebarClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       gtk_widget_class_set_css_name (widget_class, "placessidebar");
+
+       /**
+        * GcrTokensSidebar::token-added:
+        * @slot: The slot that is populated with a token
+        *
+        * Emmitted when a token was found in a slot.
+        * The #GcrTokensSidebar initiates a search for tokens when
+        * constructed and emits this signal for each slot.
+        */
+       signals[TOKEN_ADDED] = g_signal_new ("token-added",
+                                           G_OBJECT_CLASS_TYPE (object_class),
+                                           G_SIGNAL_RUN_FIRST,
+                                           0, NULL, NULL,
+                                           g_cclosure_marshal_VOID__OBJECT,
+                                           G_TYPE_NONE,
+                                           1, G_TYPE_OBJECT);
+
+       /**
+        * GcrTokensSidebar::token-login:
+        * @slot: The slot for which login was requested
+        *
+        * Emmitted when the login button was clocked, requesting
+        * the entry of a PIN.
+        */
+       signals[TOKEN_LOGIN] = g_signal_new ("token-login",
+                                           G_OBJECT_CLASS_TYPE (object_class),
+                                           G_SIGNAL_RUN_FIRST,
+                                           0, NULL, NULL,
+                                           g_cclosure_marshal_VOID__OBJECT,
+                                           G_TYPE_NONE,
+                                           1, G_TYPE_OBJECT);
+
+       /**
+        * GcrTokensSidebar::open-token:
+        * @slot: The slot that contains the selected token
+        *
+        * Emmitted when the token was selected.
+        */
+       signals[OPEN_TOKEN] = g_signal_new ("open-token",
+                                           G_OBJECT_CLASS_TYPE (object_class),
+                                           G_SIGNAL_RUN_FIRST,
+                                           0, NULL, NULL,
+                                           g_cclosure_marshal_VOID__OBJECT,
+                                           G_TYPE_NONE,
+                                           1, G_TYPE_OBJECT);
+}
+
+static void
+row_activated (GtkListBox *box, GtkListBoxRow *row, gpointer user_data)
+{
+       GcrTokensSidebar *self = user_data;
+
+       g_signal_emit (self, signals[OPEN_TOKEN], 0,
+                      gcr_tokens_sidebar_row_get_slot (GCR_TOKENS_SIDEBAR_ROW (row)));
+}
+
+static void
+gcr_tokens_sidebar_init (GcrTokensSidebar *self)
+{
+       GcrTokensSidebarPrivate *priv;
+
+       self->priv = gcr_tokens_sidebar_get_instance_private (self);
+       priv = self->priv;
+
+       gtk_widget_set_size_request (GTK_WIDGET (self), 260, 280);
+
+       priv->viewport = gtk_viewport_new (NULL, NULL);
+       gtk_container_add (GTK_CONTAINER (self), priv->viewport);
+
+       priv->listbox = gtk_list_box_new ();
+       gtk_container_add (GTK_CONTAINER (priv->viewport), priv->listbox);
+
+       g_signal_connect (priv->listbox, "row-activated", G_CALLBACK (row_activated), self);
+
+       gcr_tokens_sidebar_add_slot (self, NULL);
+       gck_modules_initialize_registered_async (NULL, modules_initialized, self);
+}
+
+/**
+ * gcr_tokens_sidebar_get_slot:
+ * @self: The #GcrTokensSidebar
+ *
+ * Gets the currently selected slot.
+ *
+ * Returns: the currently selected slot of %NULL if "All tokens" are selected.
+ */
+GckSlot *
+gcr_tokens_sidebar_get_slot (GcrTokensSidebar *self)
+{
+       GcrTokensSidebarPrivate *priv = self->priv;
+       GtkListBoxRow *row = gtk_list_box_get_selected_row (GTK_LIST_BOX (priv->listbox));
+
+       if (row == NULL)
+               return NULL;
+
+       return gcr_tokens_sidebar_row_get_slot (GCR_TOKENS_SIDEBAR_ROW (row));
+}
+
+/**
+ * gcr_tokens_sidebar_new:
+ *
+ * Creates the new Tokens sidebar.
+ *
+ * Returns: the newly created #GcrTokensSidebar
+ */
+GtkWidget *
+gcr_tokens_sidebar_new ()
+{
+       return GTK_WIDGET (g_object_new (GCR_TYPE_TOKENS_SIDEBAR, NULL));
+}
diff --git a/src/libnma/gcr-tokens-sidebar.h b/src/libnma/gcr-tokens-sidebar.h
new file mode 100644
index 0000000..9027385
--- /dev/null
+++ b/src/libnma/gcr-tokens-sidebar.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 Lubomir Rintel
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GCR_TOKENS_SIDEBAR_H__
+#define __GCR_TOKENS_SIDEBAR_H__
+
+#include <gtk/gtk.h>
+#include <gck/gck.h>
+
+typedef struct _GcrTokensSidebarPrivate GcrTokensSidebarPrivate;
+
+struct _GcrTokensSidebar
+{
+       GtkScrolledWindow parent_instance;
+       GcrTokensSidebarPrivate *priv;
+};
+
+#define GCR_TYPE_TOKENS_SIDEBAR gcr_tokens_sidebar_get_type ()
+G_DECLARE_FINAL_TYPE (GcrTokensSidebar, gcr_tokens_sidebar, GCR, TOKENS_SIDEBAR, GtkScrolledWindow)
+
+GtkWidget *gcr_tokens_sidebar_new (void);
+
+GckSlot *gcr_tokens_sidebar_get_slot (GcrTokensSidebar *self);
+
+#endif /* __GCR_TOKENS_SIDEBAR_H__ */
diff --git a/src/libnma/nma.gresource.xml b/src/libnma/nma.gresource.xml
index a8a181e..ec2b310 100644
--- a/src/libnma/nma.gresource.xml
+++ b/src/libnma/nma.gresource.xml
@@ -3,4 +3,11 @@
        <gresource prefix="/org/freedesktop/network-manager-applet">
                <file preprocess="xml-stripblanks">wifi.ui</file>
        </gresource>
+       <gresource prefix="/org/gnome/gcr/ui/">
+               <file preprocess="xml-stripblanks">gcr-object-chooser-button.ui</file>
+               <file preprocess="xml-stripblanks">gcr-object-chooser-dialog.ui</file>
+               <file preprocess="xml-stripblanks">gcr-object-chooser-widget.ui</file>
+               <file preprocess="xml-stripblanks">gcr-token-login-dialog.ui</file>
+               <file preprocess="xml-stripblanks">gcr-tokens-sidebar-row.ui</file>
+       </gresource>
 </gresources>



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