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_rigth;
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 private IdentitiesManager identities_manager;
22 private MoonshotServer ipc_server;
24 public IdCardWidget selected_id_card_widget;
26 private Queue<IdentityRequest> request_queue;
38 private const string layout =
40 <menubar name='MenuBar'>
41 <menu name='FileMenu' action='FileMenuAction'>
42 <menuitem name='AddIdCard' action='AddIdCardAction' />
44 <menuitem name='Quit' action='QuitAction' />
47 <menu name='HelpMenu' action='HelpMenuAction'>
48 <menuitem name='About' action='AboutAction' />
55 request_queue = new Queue<IdentityRequest>();
57 this.title = "Moonshoot";
58 this.set_position (WindowPosition.CENTER);
59 set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
62 setup_identities_list();
63 load_gss_eap_id_file();
69 private bool visible_func (TreeModel model, TreeIter iter)
73 string issuer_casefold;
74 string search_text_casefold;
77 Columns.ISSUER_COL, out issuer);
78 search_text = this.search_entry.get_text ();
80 if (issuer == null || search_text == null)
83 issuer_casefold = issuer.casefold ();
84 search_text_casefold = search_text.casefold ();
86 if (issuer_casefold.contains (search_text_casefold))
92 private void setup_identities_list ()
94 this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
99 this.filter = new TreeModelFilter (listmodel, null);
101 filter.set_visible_func (visible_func);
104 private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
106 if (pos == EntryIconPosition.PRIMARY)
108 print ("Search entry icon pressed\n");
112 this.search_entry.set_text ("");
116 private void search_entry_text_changed_cb ()
118 this.filter.refilter ();
119 redraw_id_card_widgets ();
121 var has_text = this.search_entry.get_text_length () > 0;
122 this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
123 this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
125 this.vbox_rigth.set_visible (false);
126 this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
129 private bool search_entry_key_press_event_cb (Gdk.EventKey e)
131 if(Gdk.keyval_name(e.keyval) == "Escape")
132 this.search_entry.set_text("");
134 // Continue processing this event, since the
135 // text entry functionality needs to see it too.
139 private void load_gss_eap_id_file ()
143 this.identities_manager = new IdentitiesManager ();
145 id_card = this.identities_manager.load_gss_eap_id_file ();
148 add_id_card_data (id_card);
149 add_id_card_widget (id_card);
153 private void load_id_cards ()
155 this.identities_manager = new IdentitiesManager ();
157 foreach (IdCard id_card in identities_manager.id_card_list)
159 add_id_card_data (id_card);
160 add_id_card_widget (id_card);
164 private void fill_details (IdCardWidget id_card_widget)
166 var id_card = id_card_widget.id_card;
167 this.username_entry.set_text (id_card.username);
168 this.password_entry.set_text (id_card.password ?? "");
170 var children = this.services_internal_vbox.get_children ();
171 foreach (var hbox in children)
173 fill_services_vbox (id_card_widget.id_card);
176 private void show_details (IdCard id_card)
178 this.vbox_rigth.set_visible (!vbox_rigth.get_visible ());
180 if (this.vbox_rigth.get_visible () == false)
182 this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
186 private void details_identity_cb (IdCardWidget id_card_widget)
188 fill_details (id_card_widget);
189 show_details (id_card_widget.id_card);
192 private IdCard get_id_card_data (AddIdentityDialog dialog)
194 var id_card = new IdCard ();
196 id_card.issuer = dialog.issuer;
197 if (id_card.issuer == "")
198 id_card.issuer = "Issuer";
199 id_card.username = dialog.username;
200 id_card.password = dialog.password;
201 id_card.nai = id_card.username + "@" + id_card.issuer;
202 id_card.pixbuf = find_icon ("avatar-default", 48);
203 id_card.services = {"email","jabber","irc"};
208 private void add_id_card_data (IdCard id_card)
212 this.listmodel.append (out iter);
214 Columns.IDCARD_COL, id_card,
215 Columns.LOGO_COL, id_card.pixbuf,
216 Columns.ISSUER_COL, id_card.issuer,
217 Columns.USERNAME_COL, id_card.username,
218 Columns.PASSWORD_COL, id_card.password);
221 private void remove_id_card_data (IdCard id_card)
226 if (listmodel.get_iter_first (out iter))
231 Columns.ISSUER_COL, out issuer);
233 if (id_card.issuer == issuer)
235 listmodel.remove (iter);
239 while (listmodel.iter_next (ref iter));
243 private void add_id_card_widget (IdCard id_card)
245 var id_card_widget = new IdCardWidget (id_card);
247 this.custom_vbox.add_id_card_widget (id_card_widget);
249 id_card_widget.details_id.connect (details_identity_cb);
250 id_card_widget.remove_id.connect (remove_identity_cb);
251 id_card_widget.send_id.connect (send_identity_cb);
252 id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
253 id_card_widget.expanded.connect (fill_details);
256 private void add_identity (AddIdentityDialog dialog)
258 var id_card = get_id_card_data (dialog);
260 this.identities_manager.id_card_list.prepend (id_card);
261 this.identities_manager.store_id_cards ();
262 this.identities_manager.store_gss_eap_id_file (id_card);
264 add_id_card_data (id_card);
265 add_id_card_widget (id_card);
268 private void add_identity_cb ()
270 var dialog = new AddIdentityDialog ();
271 var result = dialog.run ();
274 case ResponseType.OK:
275 add_identity (dialog);
283 private void remove_id_card_widget (IdCardWidget id_card_widget)
285 remove_id_card_data (id_card_widget.id_card);
287 this.custom_vbox.remove_id_card_widget (id_card_widget);
290 private void remove_identity (IdCardWidget id_card_widget)
292 var id_card = id_card_widget.id_card;
294 this.identities_manager.id_card_list.remove (id_card);
295 this.identities_manager.store_id_cards ();
296 this.identities_manager.store_gss_eap_id_file (null);
298 remove_id_card_widget (id_card_widget);
301 private void redraw_id_card_widgets ()
306 var children = this.custom_vbox.get_children ();
307 foreach (var id_card_widget in children)
308 id_card_widget.destroy();
310 if (filter.get_iter_first (out iter))
315 Columns.IDCARD_COL, out id_card);
317 add_id_card_widget (id_card);
319 while (filter.iter_next (ref iter));
323 private void remove_identity_cb (IdCardWidget id_card_widget)
325 var id_card = id_card_widget.id_card;
327 var dialog = new MessageDialog (null,
328 DialogFlags.DESTROY_WITH_PARENT,
330 Gtk.ButtonsType.YES_NO,
331 _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
332 var result = dialog.run ();
334 case ResponseType.YES:
335 remove_identity (id_card_widget);
343 public void select_identity (IdentityRequest request)
345 /* Automatic service matching rules can go here */
347 /* Resort to manual selection */
348 this.request_queue.push_tail (request);
352 public void send_identity_cb (IdCardWidget id_card_widget)
354 return_if_fail (request_queue.length > 0);
356 this.selected_id_card_widget = id_card_widget;
358 var request = this.request_queue.pop_head ();
359 var identity = id_card_widget.id_card;
360 bool reset_password = false;
362 if (identity.password == null)
364 var dialog = new AddPasswordDialog ();
365 var result = dialog.run ();
368 case ResponseType.OK:
369 identity.password = dialog.password;
370 reset_password = ! dialog.remember;
380 if (this.request_queue.is_empty())
383 request.return_identity (identity);
386 identity.password = null;
389 private void label_make_bold (Label label)
391 var font_desc = new Pango.FontDescription ();
393 font_desc.set_weight (Pango.Weight.BOLD);
395 /* This will only affect the weight of the font, the rest is
396 * from the current state of the widget, which comes from the
397 * theme or user prefs, since the font desc only has the
398 * weight flag turned on.
400 label.modify_font (font_desc);
403 private void fill_services_vbox (IdCard id_card)
406 var n_columns = id_card.services.length;
408 var services_table = new Table (n_columns, 2, false);
409 services_table.set_col_spacings (10);
410 services_table.set_row_spacings (10);
411 this.services_internal_vbox.add (services_table);
413 foreach (string service in id_card.services)
415 var label = new Label (service);
416 label.set_alignment (0, (float) 0.5);
418 var remove_button = new Button.from_stock (Stock.REMOVE);
420 var remove_button = new Button.from_stock (STOCK_REMOVE);
422 services_table.attach_defaults (label, 0, 1, i, i+1);
423 services_table.attach_defaults (remove_button, 1, 2, i, i+1);
426 this.services_internal_vbox.show_all ();
429 private void on_about_action ()
432 "Javier Jardón <jjardon@codethink.co.uk>",
433 "Sam Thursfield <samthursfield@codethink.co.uk>",
437 string copyright = "Copyright 2011 JANET";
441 Copyright (c) 2011, JANET(UK)
444 Redistribution and use in source and binary forms, with or without
445 modification, are permitted provided that the following conditions
448 1. Redistributions of source code must retain the above copyright
449 notice, this list of conditions and the following disclaimer.
451 2. Redistributions in binary form must reproduce the above copyright
452 notice, this list of conditions and the following disclaimer in the
453 documentation and/or other materials provided with the distribution.
455 3. Neither the name of JANET(UK) nor the names of its contributors
456 may be used to endorse or promote products derived from this software
457 without specific prior written permission.
459 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
460 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
461 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
462 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
463 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
464 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
465 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
466 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
467 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
468 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
472 Gtk.show_about_dialog (this,
473 "comments", _("Moonshot project UI"),
474 "copyright", copyright,
475 "website", "http://www.project-moonshot.org/",
477 "website-label", _("Visit the Moonshot project web site"),
479 "translator-credits", _("translator-credits"),
484 private Gtk.ActionEntry[] create_actions() {
485 Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
487 Gtk.ActionEntry filemenu = { "FileMenuAction",
492 Gtk.ActionEntry add = { "AddIdCardAction",
500 N_("Add a new ID Card"),
503 Gtk.ActionEntry quit = { "QuitAction",
511 N_("Quit the application"),
515 Gtk.ActionEntry helpmenu = { "HelpMenuAction",
520 Gtk.ActionEntry about = { "AboutAction",
528 N_("About this application"),
536 private void create_ui_manager ()
538 Gtk.ActionGroup action_group = new Gtk.ActionGroup ("GeneralActionGroup");
539 action_group.add_actions (create_actions (), this);
540 ui_manager.insert_action_group (action_group, 0);
543 ui_manager.add_ui_from_string (layout, -1);
547 stderr.printf ("%s\n", e.message);
549 ui_manager.ensure_update ();
552 private void build_ui()
554 create_ui_manager ();
556 this.search_entry = new Entry();
558 set_atk_name_description (search_entry, _("Search entry"), _("Search for a specific ID Card"));
559 this.search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY,
560 find_icon_sized ("edit-find-symbolic", Gtk.IconSize.MENU));
561 this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY,
562 _("Search identity or service"));
563 this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false);
565 this.search_entry.set_icon_from_pixbuf (EntryIconPosition.SECONDARY,
566 find_icon_sized ("edit-clear-symbolic", Gtk.IconSize.MENU));
567 this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY,
568 _("Clear the current search"));
569 this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false);
572 this.search_entry.icon_press.connect (search_entry_icon_press_cb);
573 this.search_entry.notify["text"].connect (search_entry_text_changed_cb);
574 this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
576 this.custom_vbox = new CustomVBox (false, 6);
578 var viewport = new Viewport (null, null);
579 viewport.set_border_width (6);
580 viewport.set_shadow_type (ShadowType.NONE);
581 viewport.add (custom_vbox);
582 var scroll = new ScrolledWindow (null, null);
583 scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
584 scroll.set_shadow_type (ShadowType.IN);
585 scroll.add_with_viewport (viewport);
587 var vbox_left = new VBox (false, 0);
588 vbox_left.pack_start (search_entry, false, false, 6);
589 vbox_left.pack_start (scroll, true, true, 0);
590 vbox_left.set_size_request (WINDOW_WIDTH, 0);
592 var login_vbox_title = new Label (_("Login: "));
593 label_make_bold (login_vbox_title);
594 login_vbox_title.set_alignment (0, (float) 0.5);
595 var username_label = new Label (_("Username:"));
596 username_label.set_alignment (1, (float) 0.5);
597 this.username_entry = new Entry ();
598 var password_label = new Label (_("Password:"));
599 password_label.set_alignment (1, (float) 0.5);
600 this.password_entry = new Entry ();
601 password_entry.set_invisible_char ('*');
602 password_entry.set_visibility (false);
603 var remember_checkbutton = new CheckButton.with_label (_("Remember password"));
604 var login_table = new Table (3, 3, false);
605 login_table.set_col_spacings (10);
606 login_table.set_row_spacings (10);
607 login_table.attach_defaults (username_label, 0, 1, 0, 1);
608 login_table.attach_defaults (username_entry, 1, 2, 0, 1);
609 login_table.attach_defaults (password_label, 0, 1, 1, 2);
610 login_table.attach_defaults (password_entry, 1, 2, 1, 2);
611 login_table.attach_defaults (remember_checkbutton, 1, 2, 2, 3);
612 var login_vbox_alignment = new Alignment (0, 0, 0, 0);
613 login_vbox_alignment.set_padding (0, 0, 12, 0);
614 login_vbox_alignment.add (login_table);
615 var login_vbox = new VBox (false, 6);
616 login_vbox.pack_start (login_vbox_title, false, true, 0);
617 login_vbox.pack_start (login_vbox_alignment, false, true, 0);
619 var services_vbox_title = new Label (_("Services:"));
620 label_make_bold (services_vbox_title);
621 services_vbox_title.set_alignment (0, (float) 0.5);
622 var services_vbox_alignment = new Alignment (0, 0, 0, 0);
623 services_vbox_alignment.set_padding (0, 0, 12, 0);
624 this.services_internal_vbox = new VBox (true, 6);
625 services_vbox_alignment.add (services_internal_vbox);
626 var services_vbox = new VBox (false, 6);
627 services_vbox.pack_start (services_vbox_title, false, true, 0);
628 services_vbox.pack_start (services_vbox_alignment, false, true, 0);
630 this.vbox_rigth = new VBox (false, 18);
631 vbox_rigth.pack_start (login_vbox, false, true, 0);
632 vbox_rigth.pack_start (services_vbox, false, true, 0);
634 var hbox = new HBox (false, 12);
635 hbox.pack_start (vbox_left, false, false, 0);
636 hbox.pack_start (vbox_rigth, false, false, 0);
638 var main_vbox = new VBox (false, 0);
639 main_vbox.set_border_width (12);
640 var menubar = this.ui_manager.get_widget ("/MenuBar");
641 main_vbox.pack_start (menubar, false, false, 0);
642 main_vbox.pack_start (hbox, true, true, 0);
645 main_vbox.show_all();
646 this.vbox_rigth.hide ();
649 private void set_atk_name_description (Widget widget, string name, string description)
651 var atk_widget = widget.get_accessible ();
653 atk_widget.set_name (name);
654 atk_widget.set_description (description);
657 private void connect_signals()
659 this.destroy.connect (Gtk.main_quit);
662 private void init_ipc_server ()
665 /* Errors will currently be sent via g_log - ie. to an
666 * obtrusive message box, on Windows
668 this.ipc_server = MoonshotServer.get_instance ();
669 MoonshotServer.start (this);
672 var conn = DBus.Bus.get (DBus.BusType.SESSION);
673 dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
674 "/org/freedesktop/DBus",
675 "org.freedesktop.DBus");
677 // try to register service in session bus
678 uint reply = bus.request_name ("org.janet.Moonshot", (uint) 0);
679 assert (reply == DBus.RequestNameReply.PRIMARY_OWNER);
681 this.ipc_server = new MoonshotServer (this);
682 conn.register_object ("/org/janet/moonshot", ipc_server);
687 stderr.printf ("%s\n", e.message);
692 public static int main(string[] args)
697 // Force specific theme settings on Windows without requiring a gtkrc file
698 Gtk.Settings settings = Gtk.Settings.get_default ();
699 settings.set_string_property ("gtk-theme-name", "ms-windows", "moonshot");
700 settings.set_long_property ("gtk-menu-images", 0, "moonshot");
703 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
704 Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
705 Intl.textdomain (Config.GETTEXT_PACKAGE);
707 var window = new MainWindow();