2 * Copyright (c) 2011-2016, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 using WebProvisioning;
36 public class IdentityManagerView : Window {
37 static MoonshotLogger logger = get_logger("IdentityManagerView");
39 bool use_flat_file_store = false;
41 // The latest year in which Moonshot sources were modified.
42 private static int LATEST_EDIT_YEAR = 2016;
44 public static Gdk.Color white = make_color(65535, 65535, 65535);
46 private const int WINDOW_WIDTH = 700;
47 private const int WINDOW_HEIGHT = 500;
48 protected IdentityManagerApp parent_app;
50 public OSXApplication osxApp;
52 private UIManager ui_manager = new UIManager();
53 private Entry search_entry;
54 private CustomVBox custom_vbox;
55 private VBox service_prompt_vbox;
56 private Button edit_button;
57 private Button remove_button;
59 private Button send_button;
61 private Gtk.ListStore* listmodel;
62 private TreeModelFilter filter;
64 internal IdentityManagerModel identities_manager;
65 private unowned SList<IdCard> candidates;
67 private GLib.Queue<IdentityRequest> request_queue;
69 internal CheckButton remember_identity_binding = null;
71 private IdCard selected_idcard = null;
73 private string import_directory = null;
85 private const string menu_layout =
86 "<menubar name='MenuBar'>" +
87 " <menu name='HelpMenu' action='HelpMenuAction'>" +
88 " <menuitem name='About' action='AboutAction' />" +
92 public IdentityManagerView(IdentityManagerApp app, bool use_flat_file_store) {
94 this.use_flat_file_store = use_flat_file_store;
97 osxApp = OSXApplication.get_instance();
99 identities_manager = parent_app.model;
100 request_queue = new GLib.Queue<IdentityRequest>();
101 this.title = _("Moonshot Identity Selector");
102 this.set_position(WindowPosition.CENTER);
103 set_default_size(WINDOW_WIDTH, WINDOW_HEIGHT);
110 private void on_card_list_changed() {
111 logger.trace("on_card_list_changed");
115 private bool visible_func(TreeModel model, TreeIter iter)
120 Columns.IDCARD_COL, out id_card);
125 if (candidates != null)
127 bool is_candidate = false;
128 foreach (IdCard candidate in candidates)
130 if (candidate == id_card)
137 string entry_text = search_entry.get_text();
138 if (entry_text == null || entry_text == "")
143 foreach (string search_text in entry_text.split(" "))
145 if (search_text == "")
149 string search_text_casefold = search_text.casefold();
151 if (id_card.issuer != null)
153 string issuer_casefold = id_card.issuer;
155 if (issuer_casefold.contains(search_text_casefold))
159 if (id_card.display_name != null)
161 string display_name_casefold = id_card.display_name.casefold();
163 if (display_name_casefold.contains(search_text_casefold))
167 if (id_card.services.size > 0)
169 foreach (string service in id_card.services)
171 string service_casefold = service.casefold();
173 if (service_casefold.contains(search_text_casefold))
181 private void setup_list_model()
183 this.listmodel = new Gtk.ListStore(Columns.N_COLUMNS, typeof(IdCard),
188 this.filter = new TreeModelFilter(listmodel, null);
190 filter.set_visible_func(visible_func);
193 private void search_entry_text_changed_cb()
195 this.filter.refilter();
196 redraw_id_card_widgets();
199 private bool search_entry_key_press_event_cb(Gdk.EventKey e)
201 if(Gdk.keyval_name(e.keyval) == "Escape")
202 this.search_entry.set_text("");
204 // Continue processing this event, since the
205 // text entry functionality needs to see it too.
209 private void load_id_cards() {
210 logger.trace("load_id_cards");
213 this.listmodel->clear();
214 LinkedList<IdCard> card_list = identities_manager.get_card_list() ;
215 if (card_list == null) {
219 foreach (IdCard id_card in card_list) {
220 logger.trace(@"load_id_cards: Loading card with display name '$(id_card.display_name)'");
221 add_id_card_data(id_card);
222 add_id_card_widget(id_card);
226 private IdCard update_id_card_data(IdentityDialog dialog, IdCard id_card)
228 id_card.display_name = dialog.display_name;
229 id_card.issuer = dialog.issuer;
230 id_card.username = dialog.username;
231 id_card.password = dialog.password;
232 id_card.store_password = dialog.store_password;
234 id_card.update_services_from_list(dialog.get_services());
236 if (dialog.clear_trust_anchor) {
237 id_card.clear_trust_anchor();
243 private void add_id_card_data(IdCard id_card)
247 this.listmodel->append(out iter);
248 pixbuf = get_pixbuf(id_card);
250 Columns.IDCARD_COL, id_card,
251 Columns.LOGO_COL, pixbuf,
252 Columns.ISSUER_COL, id_card.issuer,
253 Columns.USERNAME_COL, id_card.username,
254 Columns.PASSWORD_COL, id_card.password);
257 private IdCardWidget add_id_card_widget(IdCard id_card)
259 logger.trace("add_id_card_widget: id_card.nai='%s'; selected nai='%s'"
261 this.selected_idcard == null ? "[null selection]" : this.selected_idcard.nai));
264 var id_card_widget = new IdCardWidget(id_card, this);
265 this.custom_vbox.add_id_card_widget(id_card_widget);
266 id_card_widget.expanded.connect(this.widget_selected_cb);
267 id_card_widget.collapsed.connect(this.widget_unselected_cb);
269 if (this.selected_idcard != null && this.selected_idcard.nai == id_card.nai) {
270 logger.trace(@"add_id_card_widget: Expanding selected idcard widget");
271 id_card_widget.expand();
273 return id_card_widget;
276 private void widget_selected_cb(IdCardWidget id_card_widget)
278 logger.trace(@"widget_selected_cb: id_card_widget.id_card.display_name='$(id_card_widget.id_card.display_name)'");
280 this.selected_idcard = id_card_widget.id_card;
281 bool allow_removes = !id_card_widget.id_card.is_no_identity();
282 this.remove_button.set_sensitive(allow_removes);
283 this.edit_button.set_sensitive(true);
284 this.custom_vbox.receive_expanded_event(id_card_widget);
286 if (this.selection_in_progress())
287 this.send_button.set_sensitive(true);
290 private void widget_unselected_cb(IdCardWidget id_card_widget)
292 logger.trace(@"widget_unselected_cb: id_card_widget.id_card.display_name='$(id_card_widget.id_card.display_name)'");
294 this.selected_idcard = null;
295 this.remove_button.set_sensitive(false);
296 this.edit_button.set_sensitive(false);
297 this.custom_vbox.receive_collapsed_event(id_card_widget);
299 this.send_button.set_sensitive(false);
302 public bool add_identity(IdCard id_card, bool force_flat_file_store, out ArrayList<IdCard>? old_duplicates=null)
306 * TODO: We should have a confirmation dialog, but currently it will crash on Mac OS
307 * so for now we will install silently
309 var ret = Gtk.ResponseType.YES;
311 Gtk.MessageDialog dialog;
312 IdCard? prev_id = identities_manager.find_id_card(id_card.nai, force_flat_file_store);
313 logger.trace("add_identity(flat=%s, card='%s'): find_id_card returned %s"
314 .printf(force_flat_file_store.to_string(), id_card.display_name, (prev_id != null ? prev_id.display_name : "null")));
316 int flags = prev_id.Compare(id_card);
317 logger.trace("add_identity: compare returned " + flags.to_string());
319 if (&old_duplicates != null) {
320 old_duplicates = new ArrayList<IdCard>();
323 return false; // no changes, no need to update
324 } else if ((flags & (1 << IdCard.DiffFlags.DISPLAY_NAME)) != 0) {
325 dialog = new Gtk.MessageDialog(this,
326 Gtk.DialogFlags.DESTROY_WITH_PARENT,
327 Gtk.MessageType.QUESTION,
328 Gtk.ButtonsType.YES_NO,
329 _("Would you like to replace ID Card '%s' using nai '%s' with the new ID Card '%s'?"),
330 prev_id.display_name,
332 id_card.display_name);
334 dialog = new Gtk.MessageDialog(this,
335 Gtk.DialogFlags.DESTROY_WITH_PARENT,
336 Gtk.MessageType.QUESTION,
337 Gtk.ButtonsType.YES_NO,
338 _("Would you like to update ID Card '%s' using nai '%s'?"),
339 id_card.display_name,
343 dialog = new Gtk.MessageDialog(this,
344 Gtk.DialogFlags.DESTROY_WITH_PARENT,
345 Gtk.MessageType.QUESTION,
346 Gtk.ButtonsType.YES_NO,
347 _("Would you like to add '%s' ID Card to the ID Card Organizer?"),
348 id_card.display_name);
350 var ret = dialog.run();
354 if (ret == Gtk.ResponseType.YES) {
355 this.identities_manager.add_card(id_card, force_flat_file_store, out old_duplicates);
359 if (&old_duplicates != null) {
360 old_duplicates = new ArrayList<IdCard>();
366 private void add_identity_cb()
368 var dialog = new IdentityDialog(this);
369 int result = ResponseType.CANCEL;
370 while (!dialog.complete)
371 result = dialog.run();
374 case ResponseType.OK:
375 this.identities_manager.add_card(update_id_card_data(dialog, new IdCard()), false);
383 private void edit_identity_cb(IdCard card)
385 var dialog = new IdentityDialog.with_idcard(card, _("Edit Identity"), this);
386 int result = ResponseType.CANCEL;
387 while (!dialog.complete)
388 result = dialog.run();
391 case ResponseType.OK:
392 this.identities_manager.update_card(update_id_card_data(dialog, card));
400 private void remove_identity(IdCard id_card)
402 logger.trace(@"remove_identity: id_card.display_name='$(id_card.display_name)'");
403 if (id_card != this.selected_idcard) {
404 logger.error("remove_identity: id_card != this.selected_idcard!");
407 this.selected_idcard = null;
408 this.identities_manager.remove_card(id_card);
410 // Nothing is selected, so disable buttons
411 this.edit_button.set_sensitive(false);
412 this.remove_button.set_sensitive(false);
413 this.send_button.set_sensitive(false);
416 private void redraw_id_card_widgets()
421 this.custom_vbox.clear();
423 if (filter.get_iter_first(out iter))
428 Columns.IDCARD_COL, out id_card);
430 add_id_card_widget(id_card);
432 while (filter.iter_next(ref iter));
436 private void remove_identity_cb(IdCard id_card)
438 bool remove = WarningDialog.confirm(this,
439 Markup.printf_escaped(
440 "<span font-weight='heavy'>" + _("You are about to remove the identity '%s'.") + "</span>",
441 id_card.display_name)
442 + "\n\n" + _("Are you sure you want to do this?"),
445 remove_identity(id_card);
448 private void set_prompting_service(string service)
450 clear_selection_prompts();
452 var prompting_service = new Label(_("Identity requested for service:\n%s").printf(service));
453 prompting_service.set_line_wrap(true);
456 prompting_service.set_alignment(0, (float )0.5);
458 var selection_prompt = new Label(_("Select your identity:"));
459 selection_prompt.set_alignment(0, 1);
461 this.service_prompt_vbox.pack_start(prompting_service, false, false, 12);
462 this.service_prompt_vbox.pack_start(selection_prompt, false, false, 2);
463 this.service_prompt_vbox.show_all();
466 private void clear_selection_prompts()
468 var list = service_prompt_vbox.get_children();
469 foreach (Widget w in list)
471 service_prompt_vbox.remove(w);
476 public void queue_identity_request(IdentityRequest request)
478 bool queue_was_empty = !this.selection_in_progress();
479 this.request_queue.push_tail(request);
482 { /* setup widgets */
483 candidates = request.candidates;
485 redraw_id_card_widgets();
486 set_prompting_service(request.service);
487 remember_identity_binding.show();
489 if (this.selected_idcard != null
490 && this.custom_vbox.find_idcard_widget(this.selected_idcard) != null) {
491 // A widget is already selected, and has not been filtered out of the display via search
492 send_button.set_sensitive(true);
500 /** Makes the window visible, or at least, notifies the user that the window
501 * wants to be visible.
503 * This differs from show() in that show() does not guarantee that the
504 * window will be moved to the foreground. Actually, neither does this
505 * method, because the user's settings and window manager may affect the
506 * behavior significantly.
508 public void make_visible()
510 set_urgency_hint(true);
514 public IdCard check_add_password(IdCard identity, IdentityRequest request, IdentityManagerModel model)
516 logger.trace(@"check_add_password");
517 IdCard retval = identity;
518 bool idcard_has_pw = (identity.password != null) && (identity.password != "");
519 bool request_has_pw = (request.password != null) && (request.password != "");
520 if ((!idcard_has_pw) && (!identity.is_no_identity())) {
521 if (request_has_pw) {
522 identity.password = request.password;
523 retval = model.update_card(identity);
525 var dialog = new AddPasswordDialog(identity, request);
526 var result = dialog.run();
529 case ResponseType.OK:
530 identity.password = dialog.password;
531 identity.store_password = dialog.remember;
533 identity.temporary = false;
534 retval = model.update_card(identity);
546 private void send_identity_cb(IdCard id)
548 return_if_fail(this.selection_in_progress());
550 if (!check_and_confirm_trust_anchor(id)) {
551 // Allow user to pick again
555 var request = this.request_queue.pop_head();
556 var identity = check_add_password(id, request, identities_manager);
557 send_button.set_sensitive(false);
561 if (!this.selection_in_progress())
564 clear_selection_prompts();
565 if (!parent_app.explicitly_launched) {
566 // The following occasionally causes the app to exit without sending the dbus
567 // reply, so for now we just don't exit
573 IdentityRequest next = this.request_queue.peek_head();
574 candidates = next.candidates;
575 set_prompting_service(next.service);
578 redraw_id_card_widgets();
580 if ((identity != null) && (!identity.is_no_identity()))
581 parent_app.default_id_card = identity;
583 request.return_identity(identity, remember_identity_binding.active);
585 remember_identity_binding.active = false;
586 remember_identity_binding.hide();
589 private bool check_and_confirm_trust_anchor(IdCard id)
591 if (!id.trust_anchor.is_empty() && id.trust_anchor.get_anchor_type() == TrustAnchor.TrustAnchorType.SERVER_CERT) {
592 if (!id.trust_anchor.user_verified) {
595 int result = ResponseType.CANCEL;
596 var dialog = new TrustAnchorDialog(id, this);
597 while (!dialog.complete)
598 result = dialog.run();
601 case ResponseType.OK:
602 id.trust_anchor.user_verified = true;
616 private void on_about_action()
618 string copyright = "Copyright (c) 2011, %d JANET".printf(LATEST_EDIT_YEAR);
622 Copyright (c) 2011, %d JANET(UK)
625 Redistribution and use in source and binary forms, with or without
626 modification, are permitted provided that the following conditions
629 1. Redistributions of source code must retain the above copyright
630 notice, this list of conditions and the following disclaimer.
632 2. Redistributions in binary form must reproduce the above copyright
633 notice, this list of conditions and the following disclaimer in the
634 documentation and/or other materials provided with the distribution.
636 3. Neither the name of JANET(UK) nor the names of its contributors
637 may be used to endorse or promote products derived from this software
638 without specific prior written permission.
640 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
641 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
642 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
643 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
644 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
645 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
646 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
647 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
648 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
649 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
651 """.printf(LATEST_EDIT_YEAR);
653 AboutDialog about = new AboutDialog();
655 about.set_comments(_("Moonshot project UI"));
656 about.set_copyright(copyright);
657 about.set_website(Config.PACKAGE_URL);
658 about.set_website_label(_("Visit the Moonshot project web site"));
660 // Note: The package version is configured at the top of moonshot/ui/configure.ac
661 about.set_version(Config.PACKAGE_VERSION);
662 about.set_license(license);
663 about.set_modal(true);
664 about.set_transient_for(this);
665 about.response.connect((a, b) => {about.destroy();});
666 about.modify_bg(StateType.NORMAL, white);
671 private Gtk.ActionEntry[] create_actions() {
672 Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
674 Gtk.ActionEntry helpmenu = { "HelpMenuAction",
679 Gtk.ActionEntry about = { "AboutAction",
687 N_("About this application"),
695 private void create_ui_manager()
697 Gtk.ActionGroup action_group = new Gtk.ActionGroup("GeneralActionGroup");
698 action_group.add_actions(create_actions(), this);
699 ui_manager.insert_action_group(action_group, 0);
702 ui_manager.add_ui_from_string(menu_layout, -1);
706 stderr.printf("%s\n", e.message);
707 logger.error("create_ui_manager: Caught error: " + e.message);
709 ui_manager.ensure_update();
712 private void build_ui()
714 // Note: On Debian7/Gtk+2, the menu bar remains gray. This doesn't happen on Debian8/Gtk+3.
715 this.modify_bg(StateType.NORMAL, white);
721 int button_width = 1;
723 Table top_table = new Table(num_rows, 10, false);
724 top_table.set_border_width(12);
726 AttachOptions fill_and_expand = AttachOptions.EXPAND | AttachOptions.FILL;
727 AttachOptions fill = AttachOptions.FILL;
730 service_prompt_vbox = new VBox(false, 0);
731 top_table.attach(service_prompt_vbox, 0, 1, row, row + 1, fill_and_expand, fill_and_expand, 12, 0);
734 string search_tooltip_text = _("Search for an identity or service");
735 this.search_entry = new Entry();
737 set_atk_name_description(search_entry, _("Search entry"), _("Search for a specific ID Card"));
738 this.search_entry.set_icon_from_pixbuf(EntryIconPosition.SECONDARY,
739 find_icon_sized("edit-find", Gtk.IconSize.MENU));
740 this.search_entry.set_icon_tooltip_text(EntryIconPosition.SECONDARY,
741 search_tooltip_text);
743 this.search_entry.set_tooltip_text(search_tooltip_text);
745 this.search_entry.set_icon_sensitive(EntryIconPosition.SECONDARY, false);
747 this.search_entry.notify["text"].connect(search_entry_text_changed_cb);
748 this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
749 this.search_entry.set_width_chars(24);
751 var search_label_markup ="<small>" + search_tooltip_text + "</small>";
752 var full_search_label = new Label(null);
753 full_search_label.set_markup(search_label_markup);
754 full_search_label.set_alignment(1, 0);
756 var search_vbox = new VBox(false, 0);
757 search_vbox.pack_start(search_entry, false, false, 0);
758 var search_spacer = new Alignment(0, 0, 0, 0);
759 search_spacer.set_size_request(0, 2);
760 search_vbox.pack_start(search_spacer, false, false, 0);
761 search_vbox.pack_start(full_search_label, false, false, 0);
763 // Overlap with the service_prompt_box
764 top_table.attach(search_vbox, 5, num_cols - button_width, row - 1, row + 1, fill_and_expand, fill, 0, 12);
767 this.custom_vbox = new CustomVBox(this, false, 2);
769 var viewport = new Viewport(null, null);
770 viewport.set_border_width(2);
771 viewport.set_shadow_type(ShadowType.NONE);
772 viewport.add(custom_vbox);
773 var id_scrollwin = new ScrolledWindow(null, null);
774 id_scrollwin.set_policy(PolicyType.NEVER, PolicyType.AUTOMATIC);
775 id_scrollwin.set_shadow_type(ShadowType.IN);
776 id_scrollwin.add_with_viewport(viewport);
777 top_table.attach(id_scrollwin, 0, num_cols - 1, row, num_rows - 1, fill_and_expand, fill_and_expand, 6, 0);
779 // Right below id_scrollwin:
780 remember_identity_binding = new CheckButton.with_label(_("Remember my identity choice for this service"));
781 remember_identity_binding.active = false;
782 top_table.attach(remember_identity_binding, 0, num_cols / 2, num_rows - 1, num_rows, fill_and_expand, fill_and_expand, 3, 0);
784 var add_button = new Button.with_label(_("Add"));
785 add_button.clicked.connect((w) => {add_identity_cb();});
786 top_table.attach(make_rigid(add_button), num_cols - button_width, num_cols, row, row + 1, fill, fill, 0, 0);
789 var import_button = new Button.with_label(_("Import"));
790 import_button.clicked.connect((w) => {import_identities_cb();});
791 top_table.attach(make_rigid(import_button), num_cols - button_width, num_cols, row, row + 1, fill, fill, 0, 0);
794 this.edit_button = new Button.with_label(_("Edit"));
795 edit_button.clicked.connect((w) => {edit_identity_cb(this.selected_idcard);});
796 edit_button.set_sensitive(false);
797 top_table.attach(make_rigid(edit_button), num_cols - button_width, num_cols, row, row + 1, fill, fill, 0, 0);
800 this.remove_button = new Button.with_label(_("Remove"));
801 remove_button.clicked.connect((w) => {remove_identity_cb(this.selected_idcard);});
802 remove_button.set_sensitive(false);
803 top_table.attach(make_rigid(remove_button), num_cols - button_width, num_cols, row, row + 1, fill, fill, 0, 0);
806 // push the send button down another row.
808 this.send_button = new Button.with_label(_("Send"));
809 send_button.clicked.connect((w) => {send_identity_cb(this.selected_idcard);});
810 // send_button.set_visible(false);
811 send_button.set_sensitive(false);
812 top_table.attach(make_rigid(send_button), num_cols - button_width, num_cols, row, row + 1, fill, fill, 0, 0);
815 var main_vbox = new VBox(false, 0);
818 // hide the File | Quit menu item which is now on the Mac Menu
819 // Gtk.Widget quit_item = this.ui_manager.get_widget("/MenuBar/FileMenu/Quit");
822 Gtk.MenuShell menushell = this.ui_manager.get_widget("/MenuBar") as Gtk.MenuShell;
823 menushell.modify_bg(StateType.NORMAL, white);
825 osxApp.set_menu_bar(menushell);
826 osxApp.set_use_quartz_accelerators(true);
827 osxApp.sync_menu_bar();
830 var menubar = this.ui_manager.get_widget("/MenuBar");
831 main_vbox.pack_start(menubar, false, false, 0);
832 menubar.modify_bg(StateType.NORMAL, white);
834 main_vbox.pack_start(top_table, true, true, 6);
837 main_vbox.show_all();
839 if (!this.selection_in_progress())
840 remember_identity_binding.hide();
843 internal bool selection_in_progress() {
844 return !this.request_queue.is_empty();
847 private void set_atk_name_description(Widget widget, string name, string description)
849 var atk_widget = widget.get_accessible();
851 atk_widget.set_name(name);
852 atk_widget.set_description(description);
855 private void connect_signals()
857 this.destroy.connect(() => {
858 logger.trace("Destroy event; calling Gtk.main_quit()");
861 this.identities_manager.card_list_changed.connect(this.on_card_list_changed);
862 this.delete_event.connect(() => {return confirm_quit();});
865 private bool confirm_quit() {
866 logger.trace("delete_event intercepted; selection_in_progress()=" + selection_in_progress().to_string());
868 if (selection_in_progress()) {
869 var result = WarningDialog.confirm(this,
870 Markup.printf_escaped(
871 "<span font-weight='heavy'>" + _("Do you wish to use the %s service?") + "</span>",
872 this.request_queue.peek_head().service)
873 + "\n\n" + _("Select Yes to select an ID for this service, or No to cancel"),
874 "close_moonshot_window");
876 // Prevent other handlers from handling this event; this keeps the window open.
881 // Allow the window deletion to proceed.
885 private static Widget make_rigid(Button button)
887 // Hack to prevent the button from growing vertically
888 VBox fixed_height = new VBox(false, 0);
889 fixed_height.pack_start(button, false, false, 0);
894 private void import_identities_cb() {
895 var dialog = new FileChooserDialog(_("Import File"),
897 FileChooserAction.OPEN,
898 _("Cancel"),ResponseType.CANCEL,
899 _("Open"), ResponseType.ACCEPT,
902 if (import_directory != null) {
903 dialog.set_current_folder(import_directory);
906 if (dialog.run() == ResponseType.ACCEPT)
908 // Save the parent directory to use as default for next save
909 string filename = dialog.get_filename();
910 var file = File.new_for_path(filename);
911 import_directory = file.get_parent().get_path();
913 int import_count = 0;
915 var webp = new Parser(filename);
918 logger.trace(@"import_identities_cb: Have $(webp.cards.length) IdCards");
919 foreach (IdCard card in webp.cards)
923 logger.trace(@"import_identities_cb: Skipping null IdCard");
927 if (!card.trust_anchor.is_empty()) {
928 string ta_datetime_added = TrustAnchor.format_datetime_now();
929 card.trust_anchor.set_datetime_added(ta_datetime_added);
930 logger.trace("import_identities_cb : Set ta_datetime_added for '%s' to '%s'; ca_cert='%s'; server_cert='%s'"
931 .printf(card.display_name, ta_datetime_added, card.trust_anchor.ca_cert, card.trust_anchor.server_cert));
935 bool result = add_identity(card, use_flat_file_store);
937 logger.trace(@"import_identities_cb: Added or updated '$(card.display_name)'");
941 logger.trace(@"import_identities_cb: Did not add or update '$(card.display_name)'");
944 var msg_dialog = new Gtk.MessageDialog(this,
945 Gtk.DialogFlags.DESTROY_WITH_PARENT,
946 Gtk.MessageType.INFO,
948 _("Import completed. %d Identities were added or updated."),
951 msg_dialog.destroy();