X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmoonshot-identity-management-view.vala;h=8033d75bf35dba463fbac4c3c86833ec54390422;hb=53078345094f06a685445bcf67181c7e25736a76;hp=beb69305993de2a505b0d0f911e41386ac6ff2ea;hpb=06af9a5dba8a5ee3d09577d513f016061853f257;p=moonshot-ui.git diff --git a/src/moonshot-identity-management-view.vala b/src/moonshot-identity-management-view.vala index beb6930..8033d75 100644 --- a/src/moonshot-identity-management-view.vala +++ b/src/moonshot-identity-management-view.vala @@ -1,33 +1,65 @@ +/* + * Copyright (c) 2011-2016, JANET(UK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of JANET(UK) nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. +*/ using Gee; using Gtk; -class IdentityManagerView : Window { - private const int WINDOW_WIDTH = 400; +public class IdentityManagerView : Window { + static MoonshotLogger logger = get_logger("IdentityManagerView"); + + private const int WINDOW_WIDTH = 700; private const int WINDOW_HEIGHT = 500; protected IdentityManagerApp parent_app; -#if OS_MACOS - public OSXApplication osxApp; -#endif + #if OS_MACOS + public OSXApplication osxApp; + #endif private UIManager ui_manager = new UIManager(); private Entry search_entry; private VBox vbox_right; private CustomVBox custom_vbox; - private VBox services_internal_vbox; + private VBox service_prompt_vbox; + private Label no_identity_title; + private Button edit_button; + private Button remove_button; - private Entry username_entry; - private Entry password_entry; - - private ListStore* listmodel; + private Button send_button; + + private Gtk.ListStore* listmodel; private TreeModelFilter filter; - public IdentityManagerModel identities_manager; - private SList candidates; + internal IdentityManagerModel identities_manager; + private unowned SList candidates; - private IdCard default_id_card; public GLib.Queue request_queue; - private HashTable service_button_map; - private enum Columns { IDCARD_COL, @@ -38,54 +70,39 @@ class IdentityManagerView : Window { N_COLUMNS } - private const string layout = -"" + -" " + -" " + -" " + -" " + -" " + -"" + -" " + -" " + -" " + -""; + private const string menu_layout = + "" + + " " + + " " + + " " + + ""; public IdentityManagerView(IdentityManagerApp app) { - parent_app = app; -#if OS_MACOS - osxApp = OSXApplication.get_instance(); -#endif - identities_manager = parent_app.model; - request_queue = new GLib.Queue(); - service_button_map = new HashTable (direct_hash, direct_equal); - this.title = "Moonshoot"; - this.set_position (WindowPosition.CENTER); - set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT); - build_ui(); - setup_list_model(); + parent_app = app; + #if OS_MACOS + osxApp = OSXApplication.get_instance(); + #endif + identities_manager = parent_app.model; + request_queue = new GLib.Queue(); + this.title = "Moonshot Identity Selector"; + this.set_position(WindowPosition.CENTER); + set_default_size(WINDOW_WIDTH, WINDOW_HEIGHT); + build_ui(); + setup_list_model(); load_id_cards(); -#if OS_MACOS - osxApp = app.osxApp; -#endif - connect_signals(); + connect_signals(); } - public void on_card_list_changed () { + public void on_card_list_changed() { load_id_cards(); } - public void add_candidate (IdCard idcard) - { - candidates.append (idcard); - } - - private bool visible_func (TreeModel model, TreeIter iter) + private bool visible_func(TreeModel model, TreeIter iter) { IdCard id_card; - model.get (iter, - Columns.IDCARD_COL, out id_card); + model.get(iter, + Columns.IDCARD_COL, out id_card); if (id_card == null) return false; @@ -102,7 +119,7 @@ class IdentityManagerView : Window { return false; } - string entry_text = search_entry.get_text (); + string entry_text = search_entry.get_text(); if (entry_text == null || entry_text == "") { return true; @@ -114,21 +131,21 @@ class IdentityManagerView : Window { continue; - string search_text_casefold = search_text.casefold (); + string search_text_casefold = search_text.casefold(); if (id_card.issuer != null) { - string issuer_casefold = id_card.issuer; + string issuer_casefold = id_card.issuer; - if (issuer_casefold.contains (search_text_casefold)) - return true; + if (issuer_casefold.contains(search_text_casefold)) + return true; } if (id_card.display_name != null) { - string display_name_casefold = id_card.display_name.casefold (); + string display_name_casefold = id_card.display_name.casefold(); - if (display_name_casefold.contains (search_text_casefold)) + if (display_name_casefold.contains(search_text_casefold)) return true; } @@ -136,9 +153,9 @@ class IdentityManagerView : Window { { foreach (string service in id_card.services) { - string service_casefold = service.casefold (); + string service_casefold = service.casefold(); - if (service_casefold.contains (search_text_casefold)) + if (service_casefold.contains(search_text_casefold)) return true; } } @@ -146,126 +163,96 @@ class IdentityManagerView : Window { return false; } - private void setup_list_model () + private void setup_list_model() { - this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard), - typeof (Gdk.Pixbuf), - typeof (string), - typeof (string), - typeof (string)); - this.filter = new TreeModelFilter (listmodel, null); + this.listmodel = new Gtk.ListStore(Columns.N_COLUMNS, typeof(IdCard), + typeof(Gdk.Pixbuf), + typeof(string), + typeof(string), + typeof(string)); + this.filter = new TreeModelFilter(listmodel, null); - filter.set_visible_func (visible_func); + filter.set_visible_func(visible_func); } - private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event) + private void search_entry_icon_press_cb(EntryIconPosition pos, Gdk.Event event) { if (pos == EntryIconPosition.PRIMARY) { - print ("Search entry icon pressed\n"); + print("Search entry icon pressed\n"); } else { - this.search_entry.set_text (""); + this.search_entry.set_text(""); } } - private void search_entry_text_changed_cb () + private void search_entry_text_changed_cb() { - this.filter.refilter (); - redraw_id_card_widgets (); - - var has_text = this.search_entry.get_text_length () > 0; - this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text); - this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text); + this.filter.refilter(); + redraw_id_card_widgets(); - this.vbox_right.set_visible (false); + var has_text = this.search_entry.get_text_length() > 0; + this.search_entry.set_icon_sensitive(EntryIconPosition.PRIMARY, has_text); + this.search_entry.set_icon_sensitive(EntryIconPosition.SECONDARY, has_text); } - private bool search_entry_key_press_event_cb (Gdk.EventKey e) + private bool search_entry_key_press_event_cb(Gdk.EventKey e) { if(Gdk.keyval_name(e.keyval) == "Escape") - this.search_entry.set_text(""); + this.search_entry.set_text(""); // Continue processing this event, since the // text entry functionality needs to see it too. return false; } - private void load_id_cards () { - var children = this.custom_vbox.get_children (); - foreach (var id_card_widget in children) { - remove_id_card_widget((IdCardWidget)id_card_widget); - } + private void load_id_cards() { + logger.trace("load_id_cards"); - this.default_id_card = null; + string current_idcard_nai = null; + if (this.custom_vbox.current_idcard != null) { + current_idcard_nai = custom_vbox.current_idcard.id_card.nai; + custom_vbox.current_idcard = null; + } + var children = this.custom_vbox.get_children(); + + custom_vbox.clear(); + this.listmodel->clear(); LinkedList card_list = identities_manager.get_card_list() ; if (card_list == null) { return; } foreach (IdCard id_card in card_list) { - add_id_card_data (id_card); - add_id_card_widget (id_card); - } - - if (card_list.size > 0){ - this.default_id_card = card_list.first(); + add_id_card_data(id_card); + IdCardWidget id_card_widget = add_id_card_widget(id_card); + if (id_card_widget.id_card.nai == current_idcard_nai) { + // fill_details(id_card_widget.id_card); + id_card_widget.expand(); + } } } - private void fill_details (IdCardWidget id_card_widget) - { - var id_card = id_card_widget.id_card; - this.username_entry.set_text (id_card.username); - this.password_entry.set_text (id_card.password ?? ""); - - var children = this.services_internal_vbox.get_children (); - foreach (var hbox in children) - hbox.destroy(); - fill_services_vbox (id_card_widget.id_card); -// identities_manager.store_id_cards(); - } - - private void show_details (IdCard id_card) + private IdCard update_id_card_data(IdentityDialog dialog, IdCard id_card) { - this.vbox_right.set_visible (!vbox_right.get_visible ()); - - if (this.vbox_right.get_visible () == false) - { - this.resize (WINDOW_WIDTH, WINDOW_HEIGHT); - } - } - - private void details_identity_cb (IdCardWidget id_card_widget) - { - fill_details (id_card_widget); - show_details (id_card_widget.id_card); - } - - private IdCard get_id_card_data (AddIdentityDialog dialog) - { - var id_card = new IdCard (); - id_card.display_name = dialog.display_name; id_card.issuer = dialog.issuer; - if (id_card.issuer == "") - id_card.issuer = "Issuer"; id_card.username = dialog.username; id_card.password = dialog.password; - id_card.services = {}; - id_card.set_data("pixbuf", find_icon ("avatar-default", 48)); + id_card.store_password = dialog.store_password; + id_card.services = dialog.get_services(); return id_card; } - private void add_id_card_data (IdCard id_card) + private void add_id_card_data(IdCard id_card) { TreeIter iter; Gdk.Pixbuf pixbuf; - this.listmodel->append (out iter); - pixbuf = id_card.get_data("pixbuf"); - listmodel->set (iter, + this.listmodel->append(out iter); + pixbuf = get_pixbuf(id_card); + listmodel->set(iter, Columns.IDCARD_COL, id_card, Columns.LOGO_COL, pixbuf, Columns.ISSUER_COL, id_card.issuer, @@ -273,465 +260,324 @@ class IdentityManagerView : Window { Columns.PASSWORD_COL, id_card.password); } - private void remove_id_card_data (IdCard id_card) + private void remove_id_card_data(IdCard id_card) { TreeIter iter; string issuer; - if (listmodel->get_iter_first (out iter)) + if (listmodel->get_iter_first(out iter)) { do { - listmodel->get (iter, + listmodel->get(iter, Columns.ISSUER_COL, out issuer); if (id_card.issuer == issuer) { - listmodel->remove (iter); + listmodel->remove(iter); break; } } - while (listmodel->iter_next (ref iter)); + while (listmodel->iter_next(ref iter)); } } - private void add_id_card_widget (IdCard id_card) - { - var id_card_widget = new IdCardWidget (id_card); - this.custom_vbox.add_id_card_widget (id_card_widget); - id_card_widget.details_id.connect (details_identity_cb); - id_card_widget.remove_id.connect (remove_identity_cb); - id_card_widget.send_id.connect ((w) => send_identity_cb (w.id_card)); - id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event); - id_card_widget.expanded.connect (fill_details); - } - - /* This method finds a valid display name */ - public bool display_name_is_valid (string name, - out string? candidate) + private IdCardWidget add_id_card_widget(IdCard id_card) { - foreach (IdCard id_card in identities_manager.get_card_list()) - { - if (id_card.display_name == name) - { - if (&candidate != null) - { - for (int i=0; i<1000; i++) - { - string tmp = "%s %d".printf (name, i); - if (display_name_is_valid (tmp, null)) - { - candidate = tmp; - break; - } - } - } - return false; - } - } - - return true; - } - - public void insert_id_card (IdCard id_card) - { - string candidate; - - if (!display_name_is_valid (id_card.display_name, out candidate)) - { - id_card.display_name = candidate; - } - - this.identities_manager.add_card(id_card); + var id_card_widget = new IdCardWidget(id_card); + this.custom_vbox.add_id_card_widget(id_card_widget); + id_card_widget.expanded.connect(this.widget_selected_cb); + return id_card_widget; } - public bool add_identity (IdCard id_card) + private void widget_selected_cb(IdCardWidget id_card_widget) { - /* TODO: Check if display name already exists */ + this.remove_button.set_sensitive(true); + this.edit_button.set_sensitive(true); + this.custom_vbox.receive_expanded_event(id_card_widget); - var dialog = new Gtk.MessageDialog (this, - Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.QUESTION, - Gtk.ButtonsType.YES_NO, - _("Would you like to add '%s' ID Card to the ID Card Organizer?"), - id_card.display_name); + if (this.request_queue.length > 0) + this.send_button.set_sensitive(true); + } - dialog.show_all (); - var ret = dialog.run (); - dialog.hide (); + public bool add_identity(IdCard id_card, bool force_flat_file_store) + { + #if OS_MACOS + /* + * TODO: We should have a confirmation dialog, but currently it will crash on Mac OS + * so for now we will install silently + */ + var ret = Gtk.ResponseType.YES; + #else + Gtk.MessageDialog dialog; + IdCard? prev_id = identities_manager.find_id_card(id_card.nai, force_flat_file_store); + logger.trace("add_identity: find_id_card returned " + (prev_id != null ? "non-null" : "null")); + if (prev_id!=null) { + int flags = prev_id.Compare(id_card); + logger.trace("add_identity: compare returned " + flags.to_string()); + if (flags == 0) { + return false; // no changes, no need to update + } else if ((flags & (1 << IdCard.DiffFlags.DISPLAY_NAME)) != 0) { + dialog = new Gtk.MessageDialog(this, + Gtk.DialogFlags.DESTROY_WITH_PARENT, + Gtk.MessageType.QUESTION, + Gtk.ButtonsType.YES_NO, + _("Would you like to replace ID Card '%s' using nai '%s' with the new ID Card '%s'?"), + prev_id.display_name, + prev_id.nai, + id_card.display_name); + } else { + dialog = new Gtk.MessageDialog(this, + Gtk.DialogFlags.DESTROY_WITH_PARENT, + Gtk.MessageType.QUESTION, + Gtk.ButtonsType.YES_NO, + _("Would you like to update ID Card '%s' using nai '%s'?"), + id_card.display_name, + id_card.nai); + } + } else { + dialog = new Gtk.MessageDialog(this, + Gtk.DialogFlags.DESTROY_WITH_PARENT, + Gtk.MessageType.QUESTION, + Gtk.ButtonsType.YES_NO, + _("Would you like to add '%s' ID Card to the ID Card Organizer?"), + id_card.display_name); + } + var ret = dialog.run(); + dialog.destroy(); + #endif if (ret == Gtk.ResponseType.YES) { - id_card.set_data ("pixbuf", find_icon ("avatar-default", 48)); - this.insert_id_card (id_card); + this.identities_manager.add_card(id_card, force_flat_file_store); return true; } - return false; } - private void add_identity_manual_cb () + private void add_identity_cb() { - var dialog = new AddIdentityDialog (); - var result = dialog.run (); + var dialog = new IdentityDialog(this); + int result = ResponseType.CANCEL; + while (!dialog.complete) + result = dialog.run(); switch (result) { case ResponseType.OK: - insert_id_card (get_id_card_data (dialog)); + this.identities_manager.add_card(update_id_card_data(dialog, new IdCard()), false); break; default: break; } - dialog.destroy (); + dialog.destroy(); } - private void remove_id_card_widget (IdCardWidget id_card_widget) { - this.custom_vbox.remove_id_card_widget (id_card_widget); + private void edit_identity_cb(IdCard card) + { + var dialog = new IdentityDialog.with_idcard(card, _("Edit Identity"), this); + int result = ResponseType.CANCEL; + while (!dialog.complete) + result = dialog.run(); + + switch (result) { + case ResponseType.OK: + this.identities_manager.update_card(update_id_card_data(dialog, card)); + break; + default: + break; + } + dialog.destroy(); } - private void remove_identity (IdCardWidget id_card_widget) + private void remove_identity(IdCardWidget id_card_widget) { var id_card = id_card_widget.id_card; - remove_id_card_widget (id_card_widget); + this.custom_vbox.remove_id_card_widget(id_card_widget); this.identities_manager.remove_card(id_card); + + // Nothing is selected, so disable buttons + this.edit_button.set_sensitive(false); + this.remove_button.set_sensitive(false); + this.send_button.set_sensitive(false); } - private void redraw_id_card_widgets () + private void redraw_id_card_widgets() { + logger.trace("redraw_id_card_widgets"); + TreeIter iter; IdCard id_card; - var children = this.custom_vbox.get_children (); - foreach (var id_card_widget in children) - id_card_widget.destroy(); + var children = this.custom_vbox.get_children(); + this.custom_vbox.clear(); - if (filter.get_iter_first (out iter)) + if (filter.get_iter_first(out iter)) { do { - filter.get (iter, - Columns.IDCARD_COL, out id_card); + filter.get(iter, + Columns.IDCARD_COL, out id_card); - add_id_card_widget (id_card); + add_id_card_widget(id_card); } - while (filter.iter_next (ref iter)); + while (filter.iter_next(ref iter)); } } - private void remove_identity_cb (IdCardWidget id_card_widget) + private void remove_identity_cb(IdCardWidget id_card_widget) { var id_card = id_card_widget.id_card; - var dialog = new MessageDialog (null, - DialogFlags.DESTROY_WITH_PARENT, - MessageType.QUESTION, - Gtk.ButtonsType.YES_NO, - _("Are you sure you want to delete %s ID Card?"), id_card.issuer); - var result = dialog.run (); - switch (result) { - case ResponseType.YES: - remove_identity (id_card_widget); - break; - default: - break; - } - dialog.destroy (); + bool remove = WarningDialog.confirm(this, + "You are about to remove the identity '%s'." + .printf(id_card.display_name) + + "\n\nAre you sure you want to do this?", + "delete_idcard"); + if (remove) + remove_identity(id_card_widget); } - public void select_identity (IdentityRequest request) + private void set_prompting_service(string service) { - IdCard identity = null; + clear_selection_prompts(); - this.request_queue.push_tail (request); - - if (custom_vbox.current_idcard != null && - custom_vbox.current_idcard.send_button != null) - custom_vbox.current_idcard.send_button.set_sensitive (true); + var prompting_service = new Label(_("Identity requested for service:\n%s").printf(service)); + prompting_service.set_line_wrap(true); - if (request.select_default) - { - identity = default_id_card; - } + // left-align + prompting_service.set_alignment(0, (float )0.5); - if (identity == null) - { - bool has_nai = request.nai != null && request.nai != ""; - bool has_srv = request.service != null && request.service != ""; - bool confirm = false; - IdCard nai_provided = null; + var selection_prompt = new Label(_("Select your identity:")); + selection_prompt.set_alignment(0, 1); - foreach (IdCard id in identities_manager.get_card_list()) - { - /* If NAI matches we add id card to the candidate list */ - if (has_nai && request.nai == id.nai) - { - nai_provided = id; - add_candidate (id); - continue; - } + this.service_prompt_vbox.pack_start(prompting_service, false, false, 12); + this.service_prompt_vbox.pack_start(selection_prompt, false, false, 2); + this.service_prompt_vbox.show_all(); + } - /* If any service matches we add id card to the candidate list */ - if (has_srv) - { - foreach (string srv in id.services) - { - if (request.service == srv) - { - add_candidate (id); - continue; - } - } - } - } + private void clear_selection_prompts() + { + var list = service_prompt_vbox.get_children(); + foreach (Widget w in list) + { + service_prompt_vbox.remove(w); + } + } - /* If more than one candidate we dissasociate service from all ids */ - if (has_srv && candidates.length() > 1) - { - foreach (IdCard id in candidates) - { - int i = 0; - SList services_list = null; - bool has_service = false; - - foreach (string srv in id.services) - { - if (srv == request.service) - { - has_service = true; - continue; - } - services_list.append (srv); - } - - if (!has_service) - continue; - - if (services_list.length () == 0) - { - id.services = {}; - continue; - } - - string[] services = new string[services_list.length ()]; - foreach (string srv in services_list) - { - services[i] = srv; - i++; - } - - id.services = services; - } - } -// identities_manager.store_id_cards (); + public void queue_identity_request(IdentityRequest request) + { + if (this.request_queue.is_empty()) + { /* setup widgets */ + candidates = request.candidates; + filter.refilter(); + redraw_id_card_widgets(); + set_prompting_service(request.service); + make_visible(); + } + this.request_queue.push_tail(request); + } - /* If there are no candidates we use the service matching rules */ - if (candidates.length () == 0) - { - foreach (IdCard id in identities_manager.get_card_list()) - { - foreach (Rule rule in id.rules) - { - if (!match_service_pattern (request.service, rule.pattern)) - continue; - candidates.append (id); + /** Makes the window visible, or at least, notifies the user that the window + * wants to be visible. + * + * This differs from show() in that show() does not guarantee that the + * window will be moved to the foreground. Actually, neither does this + * method, because the user's settings and window manager may affect the + * behavior significantly. + */ + public void make_visible() + { + set_urgency_hint(true); + present(); + } - if (rule.always_confirm == "true") - confirm = true; - } - } - } - - if (candidates.length () > 1) - { - if (has_nai && nai_provided != null) - { - identity = nai_provided; - confirm = false; + public IdCard check_add_password(IdCard identity, IdentityRequest request, IdentityManagerModel model) + { + IdCard retval = identity; + bool idcard_has_pw = (identity.password != null) && (identity.password != ""); + bool request_has_pw = (request.password != null) && (request.password != ""); + if ((!idcard_has_pw) && (!identity.IsNoIdentity())) { + if (request_has_pw) { + identity.password = request.password; + retval = model.update_card(identity); + } else { + var dialog = new AddPasswordDialog(identity, request); + var result = dialog.run(); + + switch (result) { + case ResponseType.OK: + identity.password = dialog.password; + identity.store_password = dialog.remember; + if (dialog.remember) + identity.temporary = false; + retval = model.update_card(identity); + break; + default: + identity = null; + break; } - else - confirm = true; - } - else - identity = candidates.nth_data (0); - - /* TODO: If candidate list empty return fail */ - - if (confirm) - { - filter.refilter(); - redraw_id_card_widgets (); - show (); - return; + dialog.destroy(); } } - // Send back the identity (we can't directly run the - // callback because we may be being called from a 'yield') - Idle.add (() => { send_identity_cb (identity); return false; }); - return; - } - - private bool match_service_pattern (string service, string pattern) - { - var pspec = new PatternSpec (pattern); - return pspec.match_string (service); + return retval; } - public void send_identity_cb (IdCard identity) + private void send_identity_cb(IdCard id) { - return_if_fail (request_queue.length > 0); - - var request = this.request_queue.pop_head (); - bool reset_password = false; - - if (request.service != null && request.service != "") - { - string[] services = new string[identity.services.length + 1]; - - for (int i = 0; i < identity.services.length; i++) - services[i] = identity.services[i]; - - services[identity.services.length] = request.service; + IdCard identity = id; + return_if_fail(request_queue.length > 0); - identity.services = services; - -// identities_manager.store_id_cards(); - } - - if (identity.password == null) + candidates = null; + var request = this.request_queue.pop_head(); + identity = check_add_password(identity, request, identities_manager); + if (this.request_queue.is_empty()) { - var dialog = new AddPasswordDialog (); - var result = dialog.run (); - - switch (result) { - case ResponseType.OK: - identity.password = dialog.password; - reset_password = ! dialog.remember; - break; - default: - identity = null; - break; + candidates = null; + clear_selection_prompts(); + if (!parent_app.explicitly_launched) { +// The following occasionally causes the app to exit without sending the dbus +// reply, so for now we just don't exit +// Gtk.main_quit(); +// just hide instead + this.hide(); } - - dialog.destroy (); + } else { + IdentityRequest next = this.request_queue.peek_head(); + candidates = next.candidates; + set_prompting_service(next.service); } + filter.refilter(); + redraw_id_card_widgets(); - if (this.request_queue.is_empty()) - this.hide (); - - if (identity != null) - this.default_id_card = identity; - - request.return_identity (identity); + if ((identity != null) && (!identity.IsNoIdentity())) + parent_app.default_id_card = identity; - if (reset_password) - identity.password = null; - - candidates = null; + request.return_identity(identity); } - private void label_make_bold (Label label) - { - var font_desc = new Pango.FontDescription (); - - font_desc.set_weight (Pango.Weight.BOLD); - - /* This will only affect the weight of the font, the rest is - * from the current state of the widget, which comes from the - * theme or user prefs, since the font desc only has the - * weight flag turned on. - */ - label.modify_font (font_desc); - } - - private void fill_services_vbox (IdCard id_card) - { - int i = 0; - var n_columns = id_card.services.length; - - var services_table = new Table (n_columns, 2, false); - services_table.set_col_spacings (10); - services_table.set_row_spacings (10); - this.services_internal_vbox.add (services_table); - - service_button_map.remove_all (); - - foreach (string service in id_card.services) - { - var label = new Label (service); - label.set_alignment (0, (float) 0.5); -#if VALA_0_12 - var remove_button = new Button.from_stock (Stock.REMOVE); -#else - var remove_button = new Button.from_stock (STOCK_REMOVE); -#endif + // private void label_make_bold(Label label) + // { + // var font_desc = new Pango.FontDescription(); + // font_desc.set_weight(Pango.Weight.BOLD); - service_button_map.insert (remove_button, service); - - remove_button.clicked.connect ((remove_button) => - { - var dialog = new Gtk.MessageDialog (this, - Gtk.DialogFlags.DESTROY_WITH_PARENT, - Gtk.MessageType.QUESTION, - Gtk.ButtonsType.YES_NO, - _("Are you sure you want to stop '%s' ID Card from being used with %s?"), - custom_vbox.current_idcard.id_card.display_name, - _("this service")); - var ret = dialog.run(); - dialog.hide(); - - if (ret == Gtk.ResponseType.YES) - { - IdCard idcard = custom_vbox.current_idcard.id_card; - var candidate = service_button_map.lookup (remove_button); - - SList services = new SList(); - - foreach (string srv in idcard.services) - { - if (srv == candidate) - continue; - services.append (srv); - } - - idcard.services = new string[services.length()]; - for (int j=0; j", - "Sam Thursfield ", - "Alberto Ruiz ", - null - }; - - string copyright = "Copyright 2011 JANET"; + string copyright = "Copyright 2011, 2016 JANET"; string license = -""" -Copyright (c) 2011, JANET(UK) + """ +Copyright (c) 2011, 2016 JANET(UK) All rights reserved. Redistribution and use in source and binary forms, with or without @@ -762,60 +608,32 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """; - Gtk.show_about_dialog (this, - "comments", _("Moonshot project UI"), - "copyright", copyright, - "website", "http://www.project-moonshot.org/", - "license", license, - "website-label", _("Visit the Moonshot project web site"), - "authors", authors, - "translator-credits", _("translator-credits"), - null - ); + Gtk.show_about_dialog(this, + "comments", _("Moonshot project UI"), + "copyright", copyright, + "website", Config.PACKAGE_URL, + "version", Config.PACKAGE_VERSION, + "license", license, + "website-label", _("Visit the Moonshot project web site"), + "translator-credits", _("translator-credits"), + null + ); } private Gtk.ActionEntry[] create_actions() { Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0]; - Gtk.ActionEntry filemenu = { "FileMenuAction", - null, - N_("_File"), - null, null, null }; - actions += filemenu; - Gtk.ActionEntry add = { "AddIdCardAction", -#if VALA_0_12 - Stock.ADD, -#else - STOCK_ADD, -#endif - N_("Add ID Card"), - null, - N_("Add a new ID Card"), - add_identity_manual_cb }; - actions += add; - Gtk.ActionEntry quit = { "QuitAction", -#if VALA_0_12 - Stock.QUIT, -#else - STOCK_QUIT, -#endif - N_("Quit"), - "Q", - N_("Quit the application"), - Gtk.main_quit }; - actions += quit; - Gtk.ActionEntry helpmenu = { "HelpMenuAction", null, N_("_Help"), null, null, null }; actions += helpmenu; Gtk.ActionEntry about = { "AboutAction", -#if VALA_0_12 + #if VALA_0_12 Stock.ABOUT, -#else + #else STOCK_ABOUT, -#endif + #endif N_("About"), null, N_("About this application"), @@ -826,146 +644,173 @@ SUCH DAMAGE. } - private void create_ui_manager () + private void create_ui_manager() { - Gtk.ActionGroup action_group = new Gtk.ActionGroup ("GeneralActionGroup"); - action_group.add_actions (create_actions (), this); - ui_manager.insert_action_group (action_group, 0); + Gtk.ActionGroup action_group = new Gtk.ActionGroup("GeneralActionGroup"); + action_group.add_actions(create_actions(), this); + ui_manager.insert_action_group(action_group, 0); try { - ui_manager.add_ui_from_string (layout, -1); + ui_manager.add_ui_from_string(menu_layout, -1); } catch (Error e) { - stderr.printf ("%s\n", e.message); + stderr.printf("%s\n", e.message); + logger.error("create_ui_manager: Caught error: " + e.message); } - ui_manager.ensure_update (); + ui_manager.ensure_update(); } private void build_ui() { - create_ui_manager (); + // Note: On Debian7/Gtk+2, the menu bar remains gray. This doesn't happen on Debian8/Gtk+3. + Gdk.Color white = Gdk.Color(); + white.red = white.green = white.blue = 65535; + this.modify_bg(StateType.NORMAL, white); + + create_ui_manager(); this.search_entry = new Entry(); - set_atk_name_description (search_entry, _("Search entry"), _("Search for a specific ID Card")); - this.search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY, - find_icon_sized ("edit-find", Gtk.IconSize.MENU)); -// find_icon_sized ("edit-find-symbolic", Gtk.IconSize.MENU)); - this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY, - _("Search identity or service")); - this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false); + set_atk_name_description(search_entry, _("Search entry"), _("Search for a specific ID Card")); + this.search_entry.set_icon_from_pixbuf(EntryIconPosition.PRIMARY, + find_icon_sized("edit-find", Gtk.IconSize.MENU)); + this.search_entry.set_icon_tooltip_text(EntryIconPosition.PRIMARY, + _("Search for an identity or service")); + this.search_entry.set_icon_sensitive(EntryIconPosition.PRIMARY, false); - this.search_entry.set_icon_from_pixbuf (EntryIconPosition.SECONDARY, - find_icon_sized ("process-stop", Gtk.IconSize.MENU)); -// find_icon_sized ("edit-clear-symbolic", Gtk.IconSize.MENU)); - this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY, - _("Clear the current search")); - this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false); + this.search_entry.set_icon_from_pixbuf(EntryIconPosition.SECONDARY, + find_icon_sized("process-stop", Gtk.IconSize.MENU)); + this.search_entry.set_icon_tooltip_text(EntryIconPosition.SECONDARY, + _("Clear the current search")); + this.search_entry.set_icon_sensitive(EntryIconPosition.SECONDARY, false); - this.search_entry.icon_press.connect (search_entry_icon_press_cb); - this.search_entry.notify["text"].connect (search_entry_text_changed_cb); + this.search_entry.icon_press.connect(search_entry_icon_press_cb); + this.search_entry.notify["text"].connect(search_entry_text_changed_cb); this.search_entry.key_press_event.connect(search_entry_key_press_event_cb); + this.search_entry.set_width_chars(30); + + + this.custom_vbox = new CustomVBox(this, false, 2); + + var viewport = new Viewport(null, null); + viewport.set_border_width(2); + viewport.set_shadow_type(ShadowType.NONE); + viewport.add(custom_vbox); + var id_scrollwin = new ScrolledWindow(null, null); + id_scrollwin.set_policy(PolicyType.NEVER, PolicyType.AUTOMATIC); + id_scrollwin.set_shadow_type(ShadowType.IN); + id_scrollwin.add_with_viewport(viewport); + + service_prompt_vbox = new VBox(false, 0); + + var vbox_left = new VBox(false, 0); + vbox_left.pack_start(service_prompt_vbox, false, false, 12); + + var search_hbox = new HBox(false, 6); + search_hbox.pack_end(search_entry, false, false, 0); + //// var search_label = new Label(_("Search:")); + //// search_label.set_alignment(1, (float) 0.5); + //// set_atk_relation(search_label, search_entry, Atk.RelationType.LABEL_FOR); + //// search_hbox.pack_end(search_label, false, false, 6); + + var full_search_label = new Label(_("Search for an identity or service")); + full_search_label.set_alignment(1, 0); + var search_vbox = new VBox(false, 4); + search_vbox.pack_start(full_search_label, false, false, 0); + search_vbox.pack_start(search_hbox, false, false, 0); + + var inner_left_vbox = new VBox(false, 6); + inner_left_vbox.pack_start(search_vbox, false, false, 6); +// inner_left_vbox.pack_start(selection_prompt, false, false, 6); + inner_left_vbox.pack_start(id_scrollwin, true, true, 0); + + var id_and_button_box = new HBox(false, 6); + id_and_button_box.pack_start(inner_left_vbox, true, true, 6); + vbox_left.pack_start(id_and_button_box, true, true, 0); + // vbox_left.pack_start(prompting_service, false, false, 6); + vbox_left.set_size_request(WINDOW_WIDTH, 0); + + this.no_identity_title = new Label(_("No Identity: Send this identity to services which should not use Moonshot")); + no_identity_title.set_alignment(0, (float ) 0.5); + no_identity_title.set_line_wrap(true); + no_identity_title.show(); + + this.vbox_right = new VBox(false, 6); + + var add_button = new Button.with_label(_("Add")); + add_button.clicked.connect((w) => {add_identity_cb();}); + + this.edit_button = new Button.with_label(_("Edit")); + edit_button.clicked.connect((w) => {edit_identity_cb(custom_vbox.current_idcard.id_card);}); + edit_button.set_sensitive(false); + + this.remove_button = new Button.with_label(_("Remove")); + remove_button.clicked.connect((w) => {remove_identity_cb(custom_vbox.current_idcard);}); + remove_button.set_sensitive(false); + + send_button = new Button.with_label(_("Send")); + send_button.clicked.connect((w) => {send_identity_cb(custom_vbox.current_idcard.id_card);}); + // send_button.set_visible(false); + send_button.set_sensitive(false); + + var empty_box = new VBox(false, 0); + empty_box.set_size_request(0, 0); + vbox_right.pack_start(empty_box, false, false, 14); + vbox_right.pack_start(add_button, false, false, 6); + vbox_right.pack_start(edit_button, false, false, 6); + vbox_right.pack_start(remove_button, false, false, 6); + vbox_right.pack_start(send_button, false, false, 24); + + id_and_button_box.pack_start(vbox_right, false, false, 0); + + var main_vbox = new VBox(false, 0); + + // Note: This places a border above the menubar. Is that what we want? + main_vbox.set_border_width(12); - this.custom_vbox = new CustomVBox (this, false, 6); - - var viewport = new Viewport (null, null); - viewport.set_border_width (6); - viewport.set_shadow_type (ShadowType.NONE); - viewport.add (custom_vbox); - var scroll = new ScrolledWindow (null, null); - scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC); - scroll.set_shadow_type (ShadowType.IN); - scroll.add_with_viewport (viewport); - - var vbox_left = new VBox (false, 0); - vbox_left.pack_start (search_entry, false, false, 6); - vbox_left.pack_start (scroll, true, true, 0); - vbox_left.set_size_request (WINDOW_WIDTH, 0); - - var login_vbox_title = new Label (_("Login: ")); - label_make_bold (login_vbox_title); - login_vbox_title.set_alignment (0, (float) 0.5); - var username_label = new Label (_("Username:")); - username_label.set_alignment (1, (float) 0.5); - this.username_entry = new Entry (); - var password_label = new Label (_("Password:")); - password_label.set_alignment (1, (float) 0.5); - this.password_entry = new Entry (); - password_entry.set_invisible_char ('*'); - password_entry.set_visibility (false); - var remember_checkbutton = new CheckButton.with_label (_("Remember password")); - var login_table = new Table (3, 3, false); - login_table.set_col_spacings (10); - login_table.set_row_spacings (10); - login_table.attach_defaults (username_label, 0, 1, 0, 1); - login_table.attach_defaults (username_entry, 1, 2, 0, 1); - login_table.attach_defaults (password_label, 0, 1, 1, 2); - login_table.attach_defaults (password_entry, 1, 2, 1, 2); - login_table.attach_defaults (remember_checkbutton, 1, 2, 2, 3); - var login_vbox_alignment = new Alignment (0, 0, 0, 0); - login_vbox_alignment.set_padding (0, 0, 12, 0); - login_vbox_alignment.add (login_table); - var login_vbox = new VBox (false, 6); - login_vbox.pack_start (login_vbox_title, false, true, 0); - login_vbox.pack_start (login_vbox_alignment, false, true, 0); - - var services_vbox_title = new Label (_("Services:")); - label_make_bold (services_vbox_title); - services_vbox_title.set_alignment (0, (float) 0.5); - var services_vbox_alignment = new Alignment (0, 0, 0, 0); - services_vbox_alignment.set_padding (0, 0, 12, 0); - this.services_internal_vbox = new VBox (true, 6); - services_vbox_alignment.add (services_internal_vbox); - var services_vbox = new VBox (false, 6); - services_vbox.pack_start (services_vbox_title, false, true, 0); - services_vbox.pack_start (services_vbox_alignment, false, true, 0); - - this.vbox_right = new VBox (false, 18); - vbox_right.pack_start (login_vbox, false, true, 0); - vbox_right.pack_start (services_vbox, false, true, 0); - - var hbox = new HBox (false, 12); - hbox.pack_start (vbox_left, true, true, 0); - hbox.pack_start (vbox_right, false, false, 0); - - var main_vbox = new VBox (false, 0); - main_vbox.set_border_width (12); - #if OS_MACOS // hide the File | Quit menu item which is now on the Mac Menu - Gtk.Widget quit_item = this.ui_manager.get_widget("/MenuBar/FileMenu/Quit"); - quit_item.hide(); +// Gtk.Widget quit_item = this.ui_manager.get_widget("/MenuBar/FileMenu/Quit"); +// quit_item.hide(); - Gtk.MenuShell menushell = this.ui_manager.get_widget("/MenuBar") as Gtk.MenuShell; - osxApp.set_menu_bar(menushell); - osxApp.set_use_quartz_accelerators(true); - osxApp.sync_menu_bar(); - osxApp.ready(); + Gtk.MenuShell menushell = this.ui_manager.get_widget("/MenuBar") as Gtk.MenuShell; + menushell.modify_bg(StateType.NORMAL, white); + + osxApp.set_menu_bar(menushell); + osxApp.set_use_quartz_accelerators(true); + osxApp.sync_menu_bar(); + osxApp.ready(); #else - var menubar = this.ui_manager.get_widget ("/MenuBar"); - main_vbox.pack_start (menubar, false, false, 0); + var menubar = this.ui_manager.get_widget("/MenuBar"); + main_vbox.pack_start(menubar, false, false, 0); + menubar.modify_bg(StateType.NORMAL, white); #endif - main_vbox.pack_start (hbox, true, true, 0); - add (main_vbox); + main_vbox.pack_start(vbox_left, true, true, 0); + add(main_vbox); main_vbox.show_all(); - this.vbox_right.hide (); - } + } - private void set_atk_name_description (Widget widget, string name, string description) + private void set_atk_name_description(Widget widget, string name, string description) { - var atk_widget = widget.get_accessible (); + var atk_widget = widget.get_accessible(); - atk_widget.set_name (name); - atk_widget.set_description (description); + atk_widget.set_name(name); + atk_widget.set_description(description); } private void connect_signals() { - this.destroy.connect (Gtk.main_quit); + this.destroy.connect(Gtk.main_quit); this.identities_manager.card_list_changed.connect(this.on_card_list_changed); } -} + private static void set_atk_relation(Widget widget, Widget target_widget, Atk.RelationType relationship) + { + var atk_widget = widget.get_accessible(); + var atk_target_widget = target_widget.get_accessible(); + atk_widget.add_relationship(relationship, atk_target_widget); + } +}