[seahorse] Gkr: clean up Gkr.ItemProperties dialog.



commit a0a5f533ba3974ebe194e61d4cb3ac6e52169b3c
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Sat Mar 10 20:30:23 2018 +0100

    Gkr: clean up Gkr.ItemProperties dialog.
    
    Also fixes [bug 712348](https://bugzilla.gnome.org/show_bug.cgi?id=712348),
    by only saving a change to item after a manual confirmation.

 gkr/gkr-item-properties.vala |  408 ++++++++++++++++++++----------------------
 gkr/gkr-password-entry.vala  |   15 ++-
 2 files changed, 207 insertions(+), 216 deletions(-)
---
diff --git a/gkr/gkr-item-properties.vala b/gkr/gkr-item-properties.vala
index 11af602..24a989c 100644
--- a/gkr/gkr-item-properties.vala
+++ b/gkr/gkr-item-properties.vala
@@ -19,237 +19,215 @@
  */
 
 public class Seahorse.Gkr.ItemProperties : Gtk.Dialog {
-       public Item item { construct; get; }
-
-       private Gtk.Builder _builder;
-       private Gtk.Entry _password_entry;
-       private bool _password_changed;
-       private bool _updating_password;
-       private bool _updating_description;
-
-       construct {
-               this._builder = new Gtk.Builder();
-               try {
-                       string path = "/org/gnome/Seahorse/seahorse-gkr-item-properties.ui";
-                       this._builder.add_from_resource(path);
-               } catch (GLib.Error err) {
-                       GLib.critical ("%s", err.message);
-               }
-
-               var content = (Gtk.Widget)this._builder.get_object("gkr-item-properties");
-               ((Gtk.Container)this.get_content_area()).add(content);
-               content.show();
-
-               this.response.connect((response) => {
-                       this.destroy();
-               });
-
-               /* Setup the image properly */
-               this.item.bind_property ("icon", this._builder.get_object("key-image"), "gicon",
-                                        GLib.BindingFlags.SYNC_CREATE);
-
-               /* Setup the label properly */
-               Gtk.Entry description = (Gtk.Entry)this._builder.get_object("description-field");
-               this.item.bind_property("label", description, "text", GLib.BindingFlags.SYNC_CREATE);
-               description.activate.connect(() => {
-                       description_activate(description);
-               });
-               description.focus_out_event.connect_after(() => {
-                       description_activate(description);
-                       return false;
-               });
-
-               /* Window title */
-               this.item.bind_property ("label", this, "title", GLib.BindingFlags.SYNC_CREATE);
-
-               /* Update as appropriate */
-               this.item.notify.connect((pspec) => {
-                       switch(pspec.name) {
-                       case "use":
-                               update_use();
-                               update_type();
-                               update_visibility();
-                               break;
-                       case "attributes":
-                               update_details();
-                               update_server();
-                               update_user();
-                               break;
-                       case "has-secret":
-                               password_display();
-                               break;
-                       }
-               });
+    public Item item { construct; get; }
+
+    private Gtk.Builder _builder;
+    private PasswordEntry password_entry;
+    private Gtk.Entry description;
+    private bool description_has_changed;
+
+    construct {
+        this._builder = new Gtk.Builder();
+        try {
+            string path = "/org/gnome/Seahorse/seahorse-gkr-item-properties.ui";
+            this._builder.add_from_resource(path);
+        } catch (GLib.Error err) {
+            GLib.critical ("%s", err.message);
+        }
+
+        var content = (Gtk.Widget)this._builder.get_object("gkr-item-properties");
+        ((Gtk.Container)this.get_content_area()).add(content);
+        content.show();
+
+        // Setup the image properly
+        this.item.bind_property ("icon", this._builder.get_object("key-image"), "gicon",
+                                 GLib.BindingFlags.SYNC_CREATE);
+
+        // Setup the label properly
+        this.description = (Gtk.Entry)this._builder.get_object("description-field");
+        this.item.bind_property("label", this.description, "text", GLib.BindingFlags.SYNC_CREATE);
+        this.description.changed.connect(() => {
+            this.description_has_changed = true;
+        });
+
+        /* Window title */
+        this.item.bind_property("label", this, "title", GLib.BindingFlags.SYNC_CREATE);
+
+        /* Update as appropriate */
+        this.item.notify.connect((pspec) => {
+            switch(pspec.name) {
+            case "use":
+                update_use();
+                update_type();
+                update_visibility();
+                break;
+            case "attributes":
+                update_details();
+                update_server();
+                update_user();
+                break;
+            case "has-secret":
+                fetch_password();
+                break;
+            }
+        });
 
         // Create the password entry
-        this._password_entry = new PasswordEntry();
+        this.password_entry = new PasswordEntry();
         Gtk.Box box = (Gtk.Box)this._builder.get_object("password-box-area");
-        box.add(this._password_entry);
-        this._password_changed = false;
-        this._updating_password = false;
-        password_display();
-
-               /* Now watch for changes in the password */
-               this._password_entry.activate.connect(password_activate);
-               this._password_entry.changed.connect(() => {
-                       this._password_changed = true;
-               });
-               this._password_entry.focus_out_event.connect_after(() => {
-                       password_activate ();
-                       return false;
-               });
-
-               /* Sensitivity of the password entry */
-               this.item.bind_property("has-secret", this._password_entry, "sensitive");
-       }
-
-       public ItemProperties(Item item,
-                             Gtk.Window? parent) {
-               GLib.Object (
-                       item: item,
-                       transient_for: parent
-               );
-
-               item.refresh();
-       }
-
-       private void update_use() {
-               Gtk.Label use = (Gtk.Label)this._builder.get_object("use-field");
-               switch (this.item.use) {
-               case Use.NETWORK:
-                       use.label = _("Access a network share or resource");
-                       break;
-               case Use.WEB:
-                       use.label = _("Access a website");
-                       break;
-               case Use.PGP:
-                       use.label = _("Unlocks a PGP key");
-                       break;
-               case Use.SSH:
-                       use.label = _("Unlocks a Secure Shell key");
-                       break;
-               case Use.OTHER:
-                       use.label = _("Saved password or login");
-                       break;
-               default:
-                       use.label = "";
-                       break;
-               };
-       }
-
-       private void update_type() {
-               Gtk.Label type = (Gtk.Label)this._builder.get_object("type-field");
-               switch (item.use) {
-               case Use.NETWORK:
-               case Use.WEB:
-                       type.label = _("Network Credentials");
-                       break;
-               case Use.PGP:
-               case Use.SSH:
-               case Use.OTHER:
-                       type.label = _("Password");
-                       break;
-               default:
-                       type.label = "";
-                       break;
-               };
-       }
-
-       private void update_visibility() {
-               var use = this.item.use;
-               bool visible = use == Use.NETWORK || use == Use.WEB;
-               this._builder.get_object("server-label").set("visible", visible);
-               this._builder.get_object("server-field").set("visible", visible);
-               this._builder.get_object("login-label").set("visible", visible);
-               this._builder.get_object("login-field").set("visible", visible);
-       }
-
-       private void update_server() {
-               Gtk.Label server = (Gtk.Label)this._builder.get_object("server-label");
-               var value = this.item.get_attribute("server");
-               if (value == null)
-                       value = "";
-               server.label = value;
-       }
-
-       private void update_user() {
-               Gtk.Label login = (Gtk.Label)this._builder.get_object("login-label");
-               var value = this.item.get_attribute("user");
-               if (value == null)
-                       value = "";
-               login.label = value;
-       }
-
-       private void update_details() {
-               var contents = new GLib.StringBuilder();
-               var attrs = this.item.attributes;
-               var iter = GLib.HashTableIter<string, string>(attrs);
-               string key, value;
-               while (iter.next(out key, out value)) {
-                       if (key.has_prefix("gkr:") || key.has_prefix("xdg:"))
-                               continue;
-                       contents.append_printf("<b>%s</b>: %s\n",
-                                              GLib.Markup.escape_text(key),
-                                              GLib.Markup.escape_text(value));
-               }
-               Gtk.Label details = (Gtk.Label)this._builder.get_object("details-box");
-               details.use_markup = true;
-               details.label = contents.str;
-       }
-
-    private void password_activate() {
-        if (!this._password_changed || this._updating_password)
+        box.add(this.password_entry);
+        fetch_password();
+
+        // Sensitivity of the password entry
+        this.item.bind_property("has-secret", this.password_entry, "sensitive");
+    }
+
+    public ItemProperties(Item item, Gtk.Window? parent) {
+        GLib.Object (
+            item: item,
+            transient_for: parent
+        );
+
+        item.refresh();
+    }
+
+    public override void response(int response) {
+        // In case of changes: ask for confirmation
+        if (!this.password_entry.has_changed && !this.description_has_changed) {
+            destroy();
             return;
+        }
+
+        var dialog = new Gtk.MessageDialog(this, Gtk.DialogFlags.MODAL, Gtk.MessageType.WARNING,
+                                           Gtk.ButtonsType.OK_CANCEL, _("Save changes for this item?"));
+        dialog.response.connect((resp) => {
+            if (resp == Gtk.ResponseType.OK) {
+                if (this.password_entry.has_changed)
+                    save_password.begin();
+                if (this.description_has_changed)
+                    save_description.begin();
+            }
 
-        this._updating_password = true;
+            dialog.destroy();
+        });
+        dialog.run();
+    }
 
-               var value = new Secret.Value(this._password_entry.text, -1, "text/plain");
-               this.item.set_secret.begin(value, null, (obj, res) => {
-                       try {
-                               this.item.set_secret.end(res);
-                               password_display();
-                       } catch (GLib.Error err) {
-                               DBusError.strip_remote_error(err);
-                               Util.show_error (this, _("Couldn’t change password."), err.message);
-                       }
+    private void update_use() {
+        Gtk.Label use = (Gtk.Label)this._builder.get_object("use-field");
+        switch (this.item.use) {
+        case Use.NETWORK:
+            use.label = _("Access a network share or resource");
+            break;
+        case Use.WEB:
+            use.label = _("Access a website");
+            break;
+        case Use.PGP:
+            use.label = _("Unlocks a PGP key");
+            break;
+        case Use.SSH:
+            use.label = _("Unlocks a Secure Shell key");
+            break;
+        case Use.OTHER:
+            use.label = _("Saved password or login");
+            break;
+        default:
+            use.label = "";
+            break;
+        };
+    }
 
-                       this._updating_password = false;
-               });
-       }
+    private void update_type() {
+        Gtk.Label type = (Gtk.Label)this._builder.get_object("type-field");
+        switch (item.use) {
+        case Use.NETWORK:
+        case Use.WEB:
+            type.label = _("Network Credentials");
+            break;
+        case Use.PGP:
+        case Use.SSH:
+        case Use.OTHER:
+            type.label = _("Password");
+            break;
+        default:
+            type.label = "";
+            break;
+        };
+    }
 
-    private void password_display() {
+    private void update_visibility() {
+        var use = this.item.use;
+        bool visible = use == Use.NETWORK || use == Use.WEB;
+        this._builder.get_object("server-label").set("visible", visible);
+        this._builder.get_object("server-field").set("visible", visible);
+        this._builder.get_object("login-label").set("visible", visible);
+        this._builder.get_object("login-field").set("visible", visible);
+    }
+
+    private void update_server() {
+        Gtk.Label server = (Gtk.Label)this._builder.get_object("server-label");
+        var value = this.item.get_attribute("server");
+        if (value == null)
+            value = "";
+        server.label = value;
+    }
+
+    private void update_user() {
+        Gtk.Label login = (Gtk.Label)this._builder.get_object("login-label");
+        var value = this.item.get_attribute("user");
+        if (value == null)
+            value = "";
+        login.label = value;
+    }
+
+    private void update_details() {
+        var contents = new GLib.StringBuilder();
+        var attrs = this.item.attributes;
+        var iter = GLib.HashTableIter<string, string>(attrs);
+        string key, value;
+        while (iter.next(out key, out value)) {
+            if (key.has_prefix("gkr:") || key.has_prefix("xdg:"))
+                continue;
+            contents.append_printf("<b>%s</b>: %s\n",
+                                   GLib.Markup.escape_text(key),
+                                   GLib.Markup.escape_text(value));
+        }
+        Gtk.Label details = (Gtk.Label)this._builder.get_object("details-box");
+        details.use_markup = true;
+        details.label = contents.str;
+    }
+
+    private async void save_password() {
+        var pw = new Secret.Value(this.password_entry.text, -1, "text/plain");
+        try {
+            yield this.item.set_secret(pw, null);
+        } catch (GLib.Error err) {
+            DBusError.strip_remote_error(err);
+            Util.show_error (this, _("Couldn’t change password."), err.message);
+        }
+        fetch_password();
+    }
+
+    private void fetch_password() {
         var secret = this.item.get_secret();
         if (secret != null) {
             unowned string? password = secret.get_text();
             if (password != null) {
-                this._password_entry.set_text(password);
-                this._password_changed = false;
+                this.password_entry.set_initial_password(password);
                 return;
             }
         }
 
-        this._password_entry.set_text("");
-        this._password_changed = false;
+        this.password_entry.set_initial_password("");
     }
 
-    private void description_activate(Gtk.Entry description) {
-        if (this._updating_description || this.item.label == description.text)
-            return;
-
-               this._updating_description = true;
-               description.sensitive = false;
-
-               this.item.set_label.begin(description.text, null, (obj, res) => {
-                       try {
-                               this.item.set_label.end(res);
-                       } catch (GLib.Error err) {
-                               description.text = this.item.label;
-                               DBusError.strip_remote_error(err);
-                               Util.show_error (this, _("Couldn’t set description."), err.message);
-                       }
-
-                       description.sensitive = true;
-                       this._updating_description = false;
-               });
-       }
+    private async void save_description() {
+        try {
+            yield this.item.set_label(this.description.text, null);
+        } catch (GLib.Error err) {
+            this.description.text = this.item.label;
+            DBusError.strip_remote_error(err);
+            Util.show_error (this, _("Couldn’t set description."), err.message);
+        }
+    }
 }
diff --git a/gkr/gkr-password-entry.vala b/gkr/gkr-password-entry.vala
index 0a612f1..aba8c2a 100644
--- a/gkr/gkr-password-entry.vala
+++ b/gkr/gkr-password-entry.vala
@@ -16,8 +16,15 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+/**
+ * An entry specifically for password: the buffer is saved in a
+ * {@link Gcr.SecureEntryBuffer}, a secondary icon allows the user to hide or
+ * show the password.
+ */
 public class Seahorse.Gkr.PasswordEntry : Gtk.Entry {
 
+    public bool has_changed { get; set; default = false; }
+
     public PasswordEntry() {
         GLib.Object (
             buffer: new Gcr.SecureEntryBuffer(),
@@ -32,7 +39,13 @@ public class Seahorse.Gkr.PasswordEntry : Gtk.Entry {
         this.icon_press.connect((pos, event) => {
             this.visibility = !this.visibility;
         });
+        this.changed.connect(() => {
+            this.has_changed = true;
+        });
     }
 
-
+    public void set_initial_password (string? password) {
+        this.text = password;
+        this.has_changed = false;
+    }
 }


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