[epiphany] Add a new passwords dialog



commit 114f45faef774f32c7090d0e54c168389bd4f0ea
Author: William Jon McCann <william jon mccann gmail com>
Date:   Tue Dec 10 15:32:12 2013 +0100

    Add a new passwords dialog
    
    https://bugzilla.gnome.org/show_bug.cgi?id=720239

 po/POTFILES.in                    |    2 +
 src/Makefile.am                   |    2 +
 src/epiphany.gresource.xml        |    1 +
 src/passwords-dialog.c            |  454 +++++++++++++++++++++++++++++++++++++
 src/passwords-dialog.h            |   56 +++++
 src/prefs-dialog.c                |   14 ++
 src/resources/passwords-dialog.ui |  266 ++++++++++++++++++++++
 src/resources/prefs-dialog.ui     |   49 +++-
 8 files changed, 833 insertions(+), 11 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 910468a..ea29e32 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -47,6 +47,7 @@ src/bookmarks/ephy-topics-palette.c
 [type: gettext/glade]src/resources/cookies-dialog.ui
 [type: gettext/glade]src/resources/epiphany-application-menu.ui
 [type: gettext/glade]src/resources/epiphany.ui
+[type: gettext/glade]src/resources/passwords-dialog.ui
 [type: gettext/glade]src/resources/prefs-dialog.ui
 [type: gettext/glade]src/resources/prefs-lang-dialog.ui
 src/ephy-combined-stop-reload-action.c
@@ -63,4 +64,5 @@ src/popup-commands.c
 src/prefs-dialog.c
 src/clear-data-dialog.c
 src/cookies-dialog.c
+src/passwords-dialog.c
 src/window-commands.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 2de16bc..4eecc24 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,7 @@ NOINST_H_FILES = \
        languages.h                             \
        clear-data-dialog.h                     \
        cookies-dialog.h                        \
+       passwords-dialog.h                      \
        popup-commands.h                        \
        prefs-dialog.h                          \
        window-commands.h
@@ -66,6 +67,7 @@ libephymain_la_SOURCES = \
        ephy-window-action.c                    \
        clear-data-dialog.c                     \
        cookies-dialog.c                        \
+       passwords-dialog.c                      \
        popup-commands.c                        \
        prefs-dialog.c                          \
        window-commands.c                       \
diff --git a/src/epiphany.gresource.xml b/src/epiphany.gresource.xml
index 08ee0c0..ba2400b 100644
--- a/src/epiphany.gresource.xml
+++ b/src/epiphany.gresource.xml
@@ -6,6 +6,7 @@
     <file preprocess="xml-stripblanks" compressed="true">prefs-lang-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">clear-data-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">cookies-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">passwords-dialog.ui</file>
     <file preprocess="xml-stripblanks">epiphany-application-menu.ui</file>
     <file preprocess="xml-stripblanks">epiphany-ui.xml</file>
     <file preprocess="xml-stripblanks">epiphany-bookmark-editor-ui.xml</file>
diff --git a/src/passwords-dialog.c b/src/passwords-dialog.c
new file mode 100644
index 0000000..e144502
--- /dev/null
+++ b/src/passwords-dialog.c
@@ -0,0 +1,454 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Copyright © 2013 Red Hat, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#define SECRET_API_SUBJECT_TO_CHANGE
+#include <libsecret/secret.h>
+
+#include "ephy-form-auth-data.h"
+#include "ephy-string.h"
+#include "passwords-dialog.h"
+
+enum
+{
+       COL_PASSWORDS_HOST,
+       COL_PASSWORDS_USER,
+       COL_PASSWORDS_PASSWORD,
+       COL_PASSWORDS_INVISIBLE,
+       COL_PASSWORDS_DATA,
+};
+
+#define URI_KEY           "uri"
+#define FORM_USERNAME_KEY "form_username"
+#define FORM_PASSWORD_KEY "form_password"
+#define USERNAME_KEY      "username"
+
+struct PasswordsDialogPrivate
+{
+       GtkWidget *passwords_treeview;
+       GtkWidget *liststore;
+       GtkWidget *treemodelfilter;
+       GtkWidget *treemodelsort;
+       GtkWidget *remove_toolbutton;
+       GtkWidget *show_passwords_toolbutton;
+       GtkWidget *clear_button;
+       GtkWidget *password_column;
+       GtkWidget *password_renderer;
+
+       SecretService *ss;
+       GCancellable *ss_cancellable;
+       gboolean filled;
+
+       char *search_text;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (PasswordsDialog, passwords_dialog, GTK_TYPE_DIALOG)
+
+static void populate_model    (PasswordsDialog       *dialog);
+
+static void
+reload_model (PasswordsDialog *dialog)
+{
+       gtk_list_store_clear (GTK_LIST_STORE (dialog->priv->liststore));
+       dialog->priv->filled = FALSE;
+       populate_model (dialog);
+}
+
+static void
+passwords_dialog_dispose (GObject *object)
+{
+       PasswordsDialogPrivate *priv;
+
+       priv = EPHY_PASSWORDS_DIALOG (object)->priv;
+
+       if (priv->ss_cancellable != NULL) {
+               g_cancellable_cancel (priv->ss_cancellable);
+               g_clear_object (&priv->ss_cancellable);
+       }
+
+       g_clear_object (&priv->ss);
+       g_free (priv->search_text);
+       priv->search_text = NULL;
+
+       G_OBJECT_CLASS (passwords_dialog_parent_class)->dispose (object);
+}
+
+static void
+secret_remove_ready_cb (GObject *source,
+                       GAsyncResult *res,
+                       PasswordsDialog *dialog)
+{
+       secret_item_delete_finish (SECRET_ITEM (source), res, NULL);
+}
+
+static void
+secret_remove (PasswordsDialog *dialog,
+              SecretItem *item)
+{
+       secret_item_delete (item, NULL, (GAsyncReadyCallback)secret_remove_ready_cb, dialog);
+}
+
+static void
+delete_selection (PasswordsDialog *dialog)
+{
+       GList *llist, *rlist = NULL, *l, *r;
+       GtkTreeModel *model;
+       GtkTreeSelection *selection;
+       GtkTreePath *path;
+       GtkTreeIter iter, iter2;
+       GtkTreeRowReference *row_ref = NULL;
+
+       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->priv->passwords_treeview));
+       llist = gtk_tree_selection_get_selected_rows (selection, &model);
+
+       if (llist == NULL)
+       {
+               /* nothing to delete, return early */
+               return;
+       }
+
+       for (l = llist; l != NULL; l = l->next)
+       {
+               rlist = g_list_prepend (rlist, gtk_tree_row_reference_new (model, (GtkTreePath *)l->data));
+       }
+
+       /* Intelligent selection logic, no actual selection yet */
+
+       path = gtk_tree_row_reference_get_path ((GtkTreeRowReference *) g_list_first (rlist)->data);
+
+       gtk_tree_model_get_iter (model, &iter, path);
+       gtk_tree_path_free (path);
+       iter2 = iter;
+
+       if (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
+       {
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+               row_ref = gtk_tree_row_reference_new (model, path);
+       }
+       else
+       {
+               path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter2);
+               if (gtk_tree_path_prev (path))
+               {
+                       row_ref = gtk_tree_row_reference_new (model, path);
+               }
+       }
+       gtk_tree_path_free (path);
+
+       /* Removal */
+       for (r = rlist; r != NULL; r = r->next)
+       {
+               GValue val = { 0, };
+               SecretItem *item;
+               GtkTreeIter filter_iter;
+               GtkTreeIter child_iter;
+
+               path = gtk_tree_row_reference_get_path ((GtkTreeRowReference *)r->data);
+               gtk_tree_model_get_iter (model, &iter, path);
+               gtk_tree_model_get_value (model, &iter, COL_PASSWORDS_DATA, &val);
+               item = g_value_get_object (&val);
+               secret_remove (dialog, item);
+               g_value_unset (&val);
+
+               gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT 
(dialog->priv->treemodelsort),
+                                                               &filter_iter,
+                                                               &iter);
+
+               gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER 
(dialog->priv->treemodelfilter),
+                                                               &child_iter,
+                                                               &filter_iter);
+
+               gtk_list_store_remove (GTK_LIST_STORE (dialog->priv->liststore), &child_iter);
+
+               gtk_tree_row_reference_free ((GtkTreeRowReference *)r->data);
+               gtk_tree_path_free (path);
+       }
+
+       g_list_foreach (llist, (GFunc)gtk_tree_path_free, NULL);
+       g_list_free (llist);
+       g_list_free (rlist);
+
+       /* Selection */
+       if (row_ref != NULL)
+       {
+               path = gtk_tree_row_reference_get_path (row_ref);
+
+               if (path != NULL)
+               {
+                       gtk_tree_view_set_cursor (GTK_TREE_VIEW (dialog->priv->passwords_treeview), path, 
NULL, FALSE);
+                       gtk_tree_path_free (path);
+               }
+
+               gtk_tree_row_reference_free (row_ref);
+       }
+}
+
+static gboolean
+on_passwords_treeview_key_press_event (GtkWidget     *widget,
+                                    GdkEventKey   *event,
+                                    PasswordsDialog *dialog)
+{
+       if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete)
+       {
+               delete_selection (dialog);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static void
+on_remove_toolbutton_clicked (GtkToolButton *toolbutton,
+                             PasswordsDialog *dialog)
+{
+       delete_selection (dialog);
+
+       /* Restore the focus to the button */
+       gtk_widget_grab_focus (GTK_WIDGET (toolbutton));
+}
+
+static void
+on_show_passwords_toolbutton_toggled (GtkToggleToolButton *toolbutton,
+                                     PasswordsDialog *dialog)
+{
+       gboolean active;
+
+       active = gtk_toggle_tool_button_get_active (toolbutton);
+
+       gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (dialog->priv->password_column),
+                                            GTK_CELL_RENDERER (dialog->priv->password_renderer),
+                                            "text", (active ? COL_PASSWORDS_PASSWORD : 
COL_PASSWORDS_INVISIBLE),
+                                            NULL);
+       gtk_widget_queue_draw (dialog->priv->passwords_treeview);
+}
+
+static void
+on_treeview_selection_changed (GtkTreeSelection *selection,
+                              PasswordsDialog    *dialog)
+{
+       gboolean has_selection;
+
+       has_selection = gtk_tree_selection_count_selected_rows (selection) > 0;
+
+       gtk_widget_set_sensitive (dialog->priv->remove_toolbutton, has_selection);
+}
+
+static void
+on_search_entry_changed (GtkSearchEntry *entry,
+                        PasswordsDialog *dialog)
+{
+       const char *text;
+
+       text = gtk_entry_get_text (GTK_ENTRY (entry));
+       g_free (dialog->priv->search_text);
+       dialog->priv->search_text = g_strdup (text);
+       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (dialog->priv->treemodelfilter));
+}
+
+static void
+passwords_dialog_class_init (PasswordsDialogClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->dispose = passwords_dialog_dispose;
+
+       gtk_widget_class_set_template_from_resource (widget_class,
+                                                    "/org/gnome/epiphany/passwords-dialog.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, liststore);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, treemodelfilter);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, treemodelsort);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, passwords_treeview);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, clear_button);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, remove_toolbutton);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, 
show_passwords_toolbutton);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, password_column);
+       gtk_widget_class_bind_template_child_private (widget_class, PasswordsDialog, password_renderer);
+
+       gtk_widget_class_bind_template_callback (widget_class, on_passwords_treeview_key_press_event);
+       gtk_widget_class_bind_template_callback (widget_class, on_treeview_selection_changed);
+       gtk_widget_class_bind_template_callback (widget_class, on_remove_toolbutton_clicked);
+       gtk_widget_class_bind_template_callback (widget_class, on_show_passwords_toolbutton_toggled);
+       gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
+}
+
+static void
+delete_all_passwords_ready_cb (GObject *source_object,
+                              GAsyncResult *res,
+                              PasswordsDialog *dialog)
+{
+       secret_service_clear_finish (dialog->priv->ss, res, NULL);
+       reload_model (dialog);
+}
+
+static void
+delete_all_passwords (PasswordsDialog *dialog)
+{
+       GHashTable *attributes;
+
+       attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL);
+       secret_service_clear (dialog->priv->ss,
+                             EPHY_FORM_PASSWORD_SCHEMA,
+                             attributes,
+                             dialog->priv->ss_cancellable,
+                             (GAsyncReadyCallback)delete_all_passwords_ready_cb,
+                             dialog);
+       g_hash_table_unref (attributes);
+}
+
+static void
+passwords_dialog_response_cb (GtkDialog *widget,
+                           int response,
+                           PasswordsDialog *dialog)
+{
+       if (response == GTK_RESPONSE_REJECT) {
+               delete_all_passwords (dialog);
+               return;
+       }
+
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+secrets_search_ready_cb (GObject *source_object,
+                        GAsyncResult *res,
+                        PasswordsDialog *dialog)
+{
+       GList *matches;
+       GList *l;
+
+       matches = secret_service_search_finish (dialog->priv->ss, res, NULL);
+
+       for (l = matches; l != NULL; l = l->next) {
+               SecretItem *item = l->data;
+               SecretValue *value = NULL;
+               GHashTable *attributes = NULL;
+               const char *username = NULL;
+               const char *password = NULL;
+               char *host = NULL;
+               GtkTreeIter iter;
+
+               attributes = secret_item_get_attributes (item);
+               username = g_hash_table_lookup (attributes, USERNAME_KEY);
+               host = ephy_string_get_host_name (g_hash_table_lookup (attributes, URI_KEY));
+               value = secret_item_get_secret (item);
+               password = secret_value_get (value, NULL);
+
+               gtk_list_store_insert_with_values (GTK_LIST_STORE (dialog->priv->liststore),
+                                                  &iter,
+                                                  -1,
+                                                  COL_PASSWORDS_HOST, host,
+                                                  COL_PASSWORDS_USER, username,
+                                                  COL_PASSWORDS_PASSWORD, password,
+                                                  COL_PASSWORDS_INVISIBLE, "●●●●●●●●",
+                                                  COL_PASSWORDS_DATA, item,
+                                                  -1);
+
+               g_free (host);
+               g_hash_table_unref (attributes);
+       }
+
+       g_list_free_full (matches, g_object_unref);
+}
+
+static void
+populate_model (PasswordsDialog *dialog)
+{
+       GHashTable *attributes;
+
+       g_assert (dialog->priv->filled == FALSE);
+
+       attributes = secret_attributes_build (EPHY_FORM_PASSWORD_SCHEMA, NULL);
+
+       secret_service_search (dialog->priv->ss,
+                              EPHY_FORM_PASSWORD_SCHEMA,
+                              attributes,
+                              SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
+                              dialog->priv->ss_cancellable,
+                              (GAsyncReadyCallback)secrets_search_ready_cb,
+                              dialog);
+
+       g_hash_table_unref (attributes);
+}
+
+static void
+secrets_ready_cb (GObject *source_object,
+                 GAsyncResult *res,
+                 PasswordsDialog *dialog)
+{
+       dialog->priv->ss = secret_service_get_finish (res, NULL);
+       populate_model (dialog);
+}
+
+static gboolean
+row_visible_func (GtkTreeModel *model,
+                 GtkTreeIter  *iter,
+                 PasswordsDialog *dialog)
+{
+       char *username;
+       char *host;
+       gboolean visible = FALSE;
+
+       if (dialog->priv->search_text == NULL)
+               return TRUE;
+
+       gtk_tree_model_get (model, iter,
+                           COL_PASSWORDS_HOST, &host,
+                           COL_PASSWORDS_USER, &username,
+                           -1);
+
+       if (host != NULL && g_strrstr (host, dialog->priv->search_text) != NULL)
+               visible = TRUE;
+       else if (username != NULL && g_strrstr (username, dialog->priv->search_text) != NULL)
+               visible = TRUE;
+
+       g_free (host);
+       g_free (username);
+
+       return visible;
+}
+
+static void
+passwords_dialog_init (PasswordsDialog *dialog)
+{
+       dialog->priv = passwords_dialog_get_instance_private (dialog);
+       gtk_widget_init_template (GTK_WIDGET (dialog));
+
+       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (dialog->priv->treemodelfilter),
+                                               (GtkTreeModelFilterVisibleFunc)row_visible_func,
+                                               dialog,
+                                               NULL);
+
+       dialog->priv->ss_cancellable = g_cancellable_new ();
+       secret_service_get (SECRET_SERVICE_OPEN_SESSION | SECRET_SERVICE_LOAD_COLLECTIONS,
+                           dialog->priv->ss_cancellable,
+                           (GAsyncReadyCallback)secrets_ready_cb,
+                           dialog);
+
+       g_signal_connect (dialog, "response",
+                         G_CALLBACK (passwords_dialog_response_cb), dialog);
+}
diff --git a/src/passwords-dialog.h b/src/passwords-dialog.h
new file mode 100644
index 0000000..c3880d4
--- /dev/null
+++ b/src/passwords-dialog.h
@@ -0,0 +1,56 @@
+/*
+ *  Copyright © 2013 Red Hat, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef PASSWORDS_DIALOG_H
+#define PASSWORDS_DIALOG_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_PASSWORDS_DIALOG             (passwords_dialog_get_type ())
+#define EPHY_PASSWORDS_DIALOG(o)               (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_PASSWORDS_DIALOG, 
PasswordsDialog))
+#define EPHY_PASSWORDS_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_PASSWORDS_DIALOG, 
PasswordsDialogClass))
+#define EPHY_IS_PASSWORDS_DIALOG(o)            (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_PASSWORDS_DIALOG))
+#define EPHY_IS_PASSWORDS_DIALOG_CLASS(k)      (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_PASSWORDS_DIALOG))
+#define EPHY_PASSWORDS_DIALOG_GET_CLASS(o)     (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_PASSWORDS_DIALOG, 
PasswordsDialogClass))
+
+typedef struct PasswordsDialog         PasswordsDialog;
+typedef struct PasswordsDialogClass            PasswordsDialogClass;
+typedef struct PasswordsDialogPrivate  PasswordsDialogPrivate;
+
+struct PasswordsDialog
+{
+        GtkDialog parent;
+
+       /*< private >*/
+        PasswordsDialogPrivate *priv;
+};
+
+struct PasswordsDialogClass
+{
+        GtkDialogClass parent_class;
+};
+
+GType         passwords_dialog_get_type  (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/prefs-dialog.c b/src/prefs-dialog.c
index 5564c7c..968e9dd 100644
--- a/src/prefs-dialog.c
+++ b/src/prefs-dialog.c
@@ -39,6 +39,7 @@
 #include "ephy-shell.h"
 #include "clear-data-dialog.h"
 #include "cookies-dialog.h"
+#include "passwords-dialog.h"
 
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
@@ -144,6 +145,18 @@ on_manage_cookies_button_clicked (GtkWidget *button,
 }
 
 static void
+on_manage_passwords_button_clicked (GtkWidget *button,
+                                   PrefsDialog *dialog)
+{
+       PasswordsDialog *passwords_dialog;
+
+       passwords_dialog = g_object_new (EPHY_TYPE_PASSWORDS_DIALOG, NULL);
+       gtk_window_set_transient_for (GTK_WINDOW (passwords_dialog), GTK_WINDOW (dialog));
+       gtk_window_set_modal (GTK_WINDOW (passwords_dialog), TRUE);
+       gtk_window_present (GTK_WINDOW (passwords_dialog));
+}
+
+static void
 prefs_dialog_class_init (PrefsDialogClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -189,6 +202,7 @@ prefs_dialog_class_init (PrefsDialogClass *klass)
        gtk_widget_class_bind_template_child_private (widget_class, PrefsDialog, 
enable_spell_checking_checkbutton);
 
        gtk_widget_class_bind_template_callback (widget_class, on_manage_cookies_button_clicked);
+       gtk_widget_class_bind_template_callback (widget_class, on_manage_passwords_button_clicked);
 }
 
 static void
diff --git a/src/resources/passwords-dialog.ui b/src/resources/passwords-dialog.ui
new file mode 100644
index 0000000..ddcac15
--- /dev/null
+++ b/src/resources/passwords-dialog.ui
@@ -0,0 +1,266 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.16.0 on Tue Dec 10 17:15:21 2013 -->
+<interface>
+  <!-- interface-requires gtk+ 3.10 -->
+  <object class="GtkListStore" id="liststore">
+    <columns>
+      <!-- column-name HOST -->
+      <column type="gchararray"/>
+      <!-- column-name USER -->
+      <column type="gchararray"/>
+      <!-- column-name PASSWORD -->
+      <column type="gchararray"/>
+      <!-- column-name INVISIBLE -->
+      <column type="gchararray"/>
+      <!-- column-name DATA -->
+      <column type="SecretItem"/>
+    </columns>
+  </object>
+  <object class="GtkTreeModelFilter" id="treemodelfilter">
+    <property name="child_model">liststore</property>
+  </object>
+  <object class="GtkTreeModelSort" id="treemodelsort">
+    <property name="model">treemodelfilter</property>
+  </object>
+  <template class="PasswordsDialog" parent="GtkDialog">
+    <property name="height_request">500</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Passwords</property>
+    <property name="modal">True</property>
+    <property name="window_position">center</property>
+    <property name="default_width">300</property>
+    <property name="default_height">600</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="clear_button">
+                <property name="label" translatable="yes">C_lear All</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label" translatable="yes">_Close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkSearchEntry" id="searchentry1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="primary_icon_name">edit-find-symbolic</property>
+                <property name="primary_icon_activatable">False</property>
+                <property name="primary_icon_sensitive">False</property>
+                <property name="placeholder_text" translatable="yes">Search passwords</property>
+                <signal name="search-changed" handler="on_search_entry_changed" object="PasswordsDialog" 
swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="border_width">0</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow1">
+                    <property name="width_request">400</property>
+                    <property name="height_request">300</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">never</property>
+                    <property name="shadow_type">in</property>
+                    <property name="min_content_width">300</property>
+                    <property name="min_content_height">300</property>
+                    <child>
+                      <object class="GtkTreeView" id="passwords_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="model">treemodelsort</property>
+                        <property name="enable_search">False</property>
+                        <property name="search_column">0</property>
+                        <signal name="key-press-event" handler="on_passwords_treeview_key_press_event" 
object="PasswordsDialog" swapped="no"/>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection" id="treeview-selection">
+                            <property name="mode">multiple</property>
+                            <signal name="changed" handler="on_treeview_selection_changed" 
object="PasswordsDialog" swapped="no"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                            <property name="sizing">autosize</property>
+                            <property name="title" translatable="yes">Site</property>
+                            <property name="clickable">True</property>
+                            <property name="reorderable">True</property>
+                            <property name="sort_column_id">0</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                              <attributes>
+                                <attribute name="text">0</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+                            <property name="sizing">autosize</property>
+                            <property name="title" translatable="yes">User Name</property>
+                            <property name="clickable">True</property>
+                            <property name="reorderable">True</property>
+                            <property name="sort_column_id">1</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                              <attributes>
+                                <attribute name="text">1</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn" id="password_column">
+                            <property name="title" translatable="yes">Password</property>
+                            <child>
+                              <object class="GtkCellRendererText" id="password_renderer"/>
+                              <attributes>
+                                <attribute name="text">3</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolbar" id="toolbar1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <style>
+                      <class name="inline-toolbar"/>
+                    </style>
+                    <property name="icon_size">1</property>
+                    <child>
+                      <object class="GtkToolButton" id="remove_toolbutton">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Remove</property>
+                        <property name="use_underline">True</property>
+                        <property name="icon_name">list-remove-symbolic</property>
+                        <signal name="clicked" handler="on_remove_toolbutton_clicked" 
object="PasswordsDialog" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorToolItem" id="toolbutton1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="draw">False</property>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToggleToolButton" id="show_passwords_toolbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">Show Passwords</property>
+                        <property name="use_underline">True</property>
+                        <property name="icon_name">dialog-password-symbolic</property>
+                        <signal name="toggled" handler="on_show_passwords_toolbutton_toggled" 
object="PasswordsDialog" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="pack_type">end</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-2">clear_button</action-widget>
+      <action-widget response="-7">close_button</action-widget>
+    </action-widgets>
+  </template>
+</interface>
diff --git a/src/resources/prefs-dialog.ui b/src/resources/prefs-dialog.ui
index 9cd31ce..603d226 100644
--- a/src/resources/prefs-dialog.ui
+++ b/src/resources/prefs-dialog.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.16.0 on Mon Dec  9 17:35:18 2013 -->
+<!-- Generated with glade 3.16.0 on Tue Dec 10 15:14:46 2013 -->
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <object class="GtkAdjustment" id="adjustment1">
@@ -380,7 +380,7 @@
                                         <property name="visible">True</property>
                                         <property name="can_focus">True</property>
                                         <property name="receives_default">True</property>
-                                        <property name="font">Serif 12</property>
+                                        <property name="font">Sans 12</property>
                                         <property name="use_font">True</property>
                                       </object>
                                       <packing>
@@ -396,7 +396,7 @@
                                         <property name="visible">True</property>
                                         <property name="can_focus">True</property>
                                         <property name="receives_default">True</property>
-                                        <property name="font">Monospace 12</property>
+                                        <property name="font">Sans 12</property>
                                         <property name="use_font">True</property>
                                       </object>
                                       <packing>
@@ -785,18 +785,45 @@
                     <property name="can_focus">False</property>
                     <property name="spacing">6</property>
                     <child>
-                      <object class="GtkLabel" id="label1313">
+                      <object class="GtkBox" id="box2">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">Passwords</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
+                        <child>
+                          <object class="GtkLabel" id="label1313">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Passwords</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="manage_passwords_button">
+                            <property name="label" translatable="yes">Manage Passwords</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="use_underline">True</property>
+                            <signal name="clicked" handler="on_manage_passwords_button_clicked" 
object="PrefsDialog" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">False</property>
+                            <property name="pack_type">end</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">False</property>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
                         <property name="position">0</property>
                       </packing>
                     </child>



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