3 class MainWindow : Window
5 private const int WINDOW_WIDTH = 400;
6 private const int WINDOW_HEIGHT = 500;
8 private UIManager ui_manager = new UIManager();
9 private Entry search_entry;
10 private VBox vbox_right;
11 private CustomVBox custom_vbox;
12 private VBox services_internal_vbox;
14 private Entry username_entry;
15 private Entry password_entry;
17 private ListStore listmodel;
18 private TreeModelFilter filter;
20 public IdentitiesManager identities_manager;
22 private MoonshotServer ipc_server;
24 private IdCard default_id_card;
25 private Queue<IdentityRequest> request_queue;
37 private const string layout =
39 <menubar name='MenuBar'>
40 <menu name='FileMenu' action='FileMenuAction'>
41 <menuitem name='AddIdCard' action='AddIdCardAction' />
43 <menuitem name='Quit' action='QuitAction' />
46 <menu name='HelpMenu' action='HelpMenuAction'>
47 <menuitem name='About' action='AboutAction' />
54 request_queue = new Queue<IdentityRequest>();
56 this.title = "Moonshoot";
57 this.set_position (WindowPosition.CENTER);
58 set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
61 setup_identities_list();
67 private bool visible_func (TreeModel model, TreeIter iter)
71 string issuer_casefold;
72 string search_text_casefold;
75 Columns.ISSUER_COL, out issuer);
76 search_text = this.search_entry.get_text ();
78 if (issuer == null || search_text == null)
81 issuer_casefold = issuer.casefold ();
82 search_text_casefold = search_text.casefold ();
84 if (issuer_casefold.contains (search_text_casefold))
90 private void setup_identities_list ()
92 this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
97 this.filter = new TreeModelFilter (listmodel, null);
99 filter.set_visible_func (visible_func);
102 private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
104 if (pos == EntryIconPosition.PRIMARY)
106 print ("Search entry icon pressed\n");
110 this.search_entry.set_text ("");
114 private void search_entry_text_changed_cb ()
116 this.filter.refilter ();
117 redraw_id_card_widgets ();
119 var has_text = this.search_entry.get_text_length () > 0;
120 this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
121 this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
123 this.vbox_right.set_visible (false);
124 this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
127 private bool search_entry_key_press_event_cb (Gdk.EventKey e)
129 if(Gdk.keyval_name(e.keyval) == "Escape")
130 this.search_entry.set_text("");
132 // Continue processing this event, since the
133 // text entry functionality needs to see it too.
137 private void load_id_cards ()
139 this.identities_manager = new IdentitiesManager ();
141 foreach (IdCard id_card in identities_manager.id_card_list)
143 add_id_card_data (id_card);
144 add_id_card_widget (id_card);
147 this.default_id_card = identities_manager.id_card_list.data;
150 private void fill_details (IdCardWidget id_card_widget)
152 var id_card = id_card_widget.id_card;
153 this.username_entry.set_text (id_card.username);
154 this.password_entry.set_text (id_card.password ?? "");
156 var children = this.services_internal_vbox.get_children ();
157 foreach (var hbox in children)
159 fill_services_vbox (id_card_widget.id_card);
162 private void show_details (IdCard id_card)
164 this.vbox_right.set_visible (!vbox_right.get_visible ());
166 if (this.vbox_right.get_visible () == false)
168 this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
172 private void details_identity_cb (IdCardWidget id_card_widget)
174 fill_details (id_card_widget);
175 show_details (id_card_widget.id_card);
178 private IdCard get_id_card_data (AddIdentityDialog dialog)
180 var id_card = new IdCard ();
182 id_card.issuer = dialog.issuer;
183 if (id_card.issuer == "")
184 id_card.issuer = "Issuer";
185 id_card.username = dialog.username;
186 id_card.password = dialog.password;
187 id_card.pixbuf = find_icon ("avatar-default", 48);
188 id_card.services = {};
193 private void add_id_card_data (IdCard id_card)
197 this.listmodel.append (out iter);
199 Columns.IDCARD_COL, id_card,
200 Columns.LOGO_COL, id_card.pixbuf,
201 Columns.ISSUER_COL, id_card.issuer,
202 Columns.USERNAME_COL, id_card.username,
203 Columns.PASSWORD_COL, id_card.password);
206 private void remove_id_card_data (IdCard id_card)
211 if (listmodel.get_iter_first (out iter))
216 Columns.ISSUER_COL, out issuer);
218 if (id_card.issuer == issuer)
220 listmodel.remove (iter);
224 while (listmodel.iter_next (ref iter));
228 private void add_id_card_widget (IdCard id_card)
230 var id_card_widget = new IdCardWidget (id_card);
232 this.custom_vbox.add_id_card_widget (id_card_widget);
234 id_card_widget.details_id.connect (details_identity_cb);
235 id_card_widget.remove_id.connect (remove_identity_cb);
236 id_card_widget.send_id.connect ((w) => send_identity_cb (w.id_card));
237 id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
238 id_card_widget.expanded.connect (fill_details);
241 private void add_identity (AddIdentityDialog dialog)
243 insert_id_card (get_id_card_data (dialog));
246 public void insert_id_card (IdCard id_card)
248 this.identities_manager.id_card_list.prepend (id_card);
249 this.identities_manager.store_id_cards ();
251 add_id_card_data (id_card);
252 add_id_card_widget (id_card);
255 private void add_identity_cb ()
257 var dialog = new AddIdentityDialog ();
258 var result = dialog.run ();
261 case ResponseType.OK:
262 add_identity (dialog);
270 private void remove_id_card_widget (IdCardWidget id_card_widget)
272 remove_id_card_data (id_card_widget.id_card);
274 this.custom_vbox.remove_id_card_widget (id_card_widget);
277 private void remove_identity (IdCardWidget id_card_widget)
279 var id_card = id_card_widget.id_card;
281 this.identities_manager.id_card_list.remove (id_card);
282 this.identities_manager.store_id_cards ();
284 remove_id_card_widget (id_card_widget);
287 private void redraw_id_card_widgets ()
292 var children = this.custom_vbox.get_children ();
293 foreach (var id_card_widget in children)
294 id_card_widget.destroy();
296 if (filter.get_iter_first (out iter))
301 Columns.IDCARD_COL, out id_card);
303 add_id_card_widget (id_card);
305 while (filter.iter_next (ref iter));
309 private void remove_identity_cb (IdCardWidget id_card_widget)
311 var id_card = id_card_widget.id_card;
313 var dialog = new MessageDialog (null,
314 DialogFlags.DESTROY_WITH_PARENT,
316 Gtk.ButtonsType.YES_NO,
317 _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
318 var result = dialog.run ();
320 case ResponseType.YES:
321 remove_identity (id_card_widget);
329 public void select_identity (IdentityRequest request)
331 IdCard identity = null;
333 this.request_queue.push_tail (request);
335 if (request.select_default)
337 identity = this.default_id_card;
340 /* Automatic service matching rules can go here */
342 if (identity == null)
344 // Resort to manual selection
349 // Send back the identity (we can't directly run the
350 // callback because we may be being called from a 'yield')
351 Idle.add (() => { send_identity_cb (identity); return false; });
356 public void send_identity_cb (IdCard identity)
358 return_if_fail (request_queue.length > 0);
360 var request = this.request_queue.pop_head ();
361 bool reset_password = false;
363 if (identity.password == null)
365 var dialog = new AddPasswordDialog ();
366 var result = dialog.run ();
369 case ResponseType.OK:
370 identity.password = dialog.password;
371 reset_password = ! dialog.remember;
381 if (this.request_queue.is_empty())
384 if (identity != null)
385 this.default_id_card = identity;
387 request.return_identity (identity);
390 identity.password = null;
393 private void label_make_bold (Label label)
395 var font_desc = new Pango.FontDescription ();
397 font_desc.set_weight (Pango.Weight.BOLD);
399 /* This will only affect the weight of the font, the rest is
400 * from the current state of the widget, which comes from the
401 * theme or user prefs, since the font desc only has the
402 * weight flag turned on.
404 label.modify_font (font_desc);
407 private void fill_services_vbox (IdCard id_card)
410 var n_columns = id_card.services.length;
412 var services_table = new Table (n_columns, 2, false);
413 services_table.set_col_spacings (10);
414 services_table.set_row_spacings (10);
415 this.services_internal_vbox.add (services_table);
417 foreach (string service in id_card.services)
419 var label = new Label (service);
420 label.set_alignment (0, (float) 0.5);
422 var remove_button = new Button.from_stock (Stock.REMOVE);
424 var remove_button = new Button.from_stock (STOCK_REMOVE);
426 services_table.attach_defaults (label, 0, 1, i, i+1);
427 services_table.attach_defaults (remove_button, 1, 2, i, i+1);
430 this.services_internal_vbox.show_all ();
433 private void on_about_action ()
436 "Javier Jardón <jjardon@codethink.co.uk>",
437 "Sam Thursfield <samthursfield@codethink.co.uk>",
438 "Alberto Ruiz <alberto.ruiz@codethink.co.uk>",
442 string copyright = "Copyright 2011 JANET";
446 Copyright (c) 2011, JANET(UK)
449 Redistribution and use in source and binary forms, with or without
450 modification, are permitted provided that the following conditions
453 1. Redistributions of source code must retain the above copyright
454 notice, this list of conditions and the following disclaimer.
456 2. Redistributions in binary form must reproduce the above copyright
457 notice, this list of conditions and the following disclaimer in the
458 documentation and/or other materials provided with the distribution.
460 3. Neither the name of JANET(UK) nor the names of its contributors
461 may be used to endorse or promote products derived from this software
462 without specific prior written permission.
464 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
465 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
466 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
467 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
468 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
469 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
470 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
471 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
472 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
473 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
477 Gtk.show_about_dialog (this,
478 "comments", _("Moonshot project UI"),
479 "copyright", copyright,
480 "website", "http://www.project-moonshot.org/",
482 "website-label", _("Visit the Moonshot project web site"),
484 "translator-credits", _("translator-credits"),
489 private Gtk.ActionEntry[] create_actions() {
490 Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
492 Gtk.ActionEntry filemenu = { "FileMenuAction",
497 Gtk.ActionEntry add = { "AddIdCardAction",
505 N_("Add a new ID Card"),
508 Gtk.ActionEntry quit = { "QuitAction",
516 N_("Quit the application"),
520 Gtk.ActionEntry helpmenu = { "HelpMenuAction",
525 Gtk.ActionEntry about = { "AboutAction",
533 N_("About this application"),
541 private void create_ui_manager ()
543 Gtk.ActionGroup action_group = new Gtk.ActionGroup ("GeneralActionGroup");
544 action_group.add_actions (create_actions (), this);
545 ui_manager.insert_action_group (action_group, 0);
548 ui_manager.add_ui_from_string (layout, -1);
552 stderr.printf ("%s\n", e.message);
554 ui_manager.ensure_update ();
557 private void build_ui()
559 create_ui_manager ();
561 this.search_entry = new Entry();
563 set_atk_name_description (search_entry, _("Search entry"), _("Search for a specific ID Card"));
564 this.search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY,
565 find_icon_sized ("edit-find-symbolic", Gtk.IconSize.MENU));
566 this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY,
567 _("Search identity or service"));
568 this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false);
570 this.search_entry.set_icon_from_pixbuf (EntryIconPosition.SECONDARY,
571 find_icon_sized ("edit-clear-symbolic", Gtk.IconSize.MENU));
572 this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY,
573 _("Clear the current search"));
574 this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false);
577 this.search_entry.icon_press.connect (search_entry_icon_press_cb);
578 this.search_entry.notify["text"].connect (search_entry_text_changed_cb);
579 this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
581 this.custom_vbox = new CustomVBox (false, 6);
583 var viewport = new Viewport (null, null);
584 viewport.set_border_width (6);
585 viewport.set_shadow_type (ShadowType.NONE);
586 viewport.add (custom_vbox);
587 var scroll = new ScrolledWindow (null, null);
588 scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
589 scroll.set_shadow_type (ShadowType.IN);
590 scroll.add_with_viewport (viewport);
592 var vbox_left = new VBox (false, 0);
593 vbox_left.pack_start (search_entry, false, false, 6);
594 vbox_left.pack_start (scroll, true, true, 0);
595 vbox_left.set_size_request (WINDOW_WIDTH, 0);
597 var login_vbox_title = new Label (_("Login: "));
598 label_make_bold (login_vbox_title);
599 login_vbox_title.set_alignment (0, (float) 0.5);
600 var username_label = new Label (_("Username:"));
601 username_label.set_alignment (1, (float) 0.5);
602 this.username_entry = new Entry ();
603 var password_label = new Label (_("Password:"));
604 password_label.set_alignment (1, (float) 0.5);
605 this.password_entry = new Entry ();
606 password_entry.set_invisible_char ('*');
607 password_entry.set_visibility (false);
608 var remember_checkbutton = new CheckButton.with_label (_("Remember password"));
609 var login_table = new Table (3, 3, false);
610 login_table.set_col_spacings (10);
611 login_table.set_row_spacings (10);
612 login_table.attach_defaults (username_label, 0, 1, 0, 1);
613 login_table.attach_defaults (username_entry, 1, 2, 0, 1);
614 login_table.attach_defaults (password_label, 0, 1, 1, 2);
615 login_table.attach_defaults (password_entry, 1, 2, 1, 2);
616 login_table.attach_defaults (remember_checkbutton, 1, 2, 2, 3);
617 var login_vbox_alignment = new Alignment (0, 0, 0, 0);
618 login_vbox_alignment.set_padding (0, 0, 12, 0);
619 login_vbox_alignment.add (login_table);
620 var login_vbox = new VBox (false, 6);
621 login_vbox.pack_start (login_vbox_title, false, true, 0);
622 login_vbox.pack_start (login_vbox_alignment, false, true, 0);
624 var services_vbox_title = new Label (_("Services:"));
625 label_make_bold (services_vbox_title);
626 services_vbox_title.set_alignment (0, (float) 0.5);
627 var services_vbox_alignment = new Alignment (0, 0, 0, 0);
628 services_vbox_alignment.set_padding (0, 0, 12, 0);
629 this.services_internal_vbox = new VBox (true, 6);
630 services_vbox_alignment.add (services_internal_vbox);
631 var services_vbox = new VBox (false, 6);
632 services_vbox.pack_start (services_vbox_title, false, true, 0);
633 services_vbox.pack_start (services_vbox_alignment, false, true, 0);
635 this.vbox_right = new VBox (false, 18);
636 vbox_right.pack_start (login_vbox, false, true, 0);
637 vbox_right.pack_start (services_vbox, false, true, 0);
639 var hbox = new HBox (false, 12);
640 hbox.pack_start (vbox_left, false, false, 0);
641 hbox.pack_start (vbox_right, false, false, 0);
643 var main_vbox = new VBox (false, 0);
644 main_vbox.set_border_width (12);
645 var menubar = this.ui_manager.get_widget ("/MenuBar");
646 main_vbox.pack_start (menubar, false, false, 0);
647 main_vbox.pack_start (hbox, true, true, 0);
650 main_vbox.show_all();
651 this.vbox_right.hide ();
654 private void set_atk_name_description (Widget widget, string name, string description)
656 var atk_widget = widget.get_accessible ();
658 atk_widget.set_name (name);
659 atk_widget.set_description (description);
662 private void connect_signals()
664 this.destroy.connect (Gtk.main_quit);
667 private void init_ipc_server ()
670 /* Errors will currently be sent via g_log - ie. to an
671 * obtrusive message box, on Windows
673 this.ipc_server = MoonshotServer.get_instance ();
674 MoonshotServer.start (this);
677 var conn = DBus.Bus.get (DBus.BusType.SESSION);
678 dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
679 "/org/freedesktop/DBus",
680 "org.freedesktop.DBus");
682 // try to register service in session bus
683 uint reply = bus.request_name ("org.janet.Moonshot", (uint) 0);
684 assert (reply == DBus.RequestNameReply.PRIMARY_OWNER);
686 this.ipc_server = new MoonshotServer (this);
687 conn.register_object ("/org/janet/moonshot", ipc_server);
692 stderr.printf ("%s\n", e.message);
697 public static int main(string[] args)
702 // Force specific theme settings on Windows without requiring a gtkrc file
703 Gtk.Settings settings = Gtk.Settings.get_default ();
704 settings.set_string_property ("gtk-theme-name", "ms-windows", "moonshot");
705 settings.set_long_property ("gtk-menu-images", 0, "moonshot");
708 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
709 Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
710 Intl.textdomain (Config.GETTEXT_PACKAGE);
712 var window = new MainWindow();