Reselect current id card by nai when cards are reloaded.
[moonshot-ui.git] / src / moonshot-identity-management-view.vala
1 using Gee;
2 using Gtk;
3
4 public class IdentityManagerView : Window {
5     private const int WINDOW_WIDTH = 400;
6     private const int WINDOW_HEIGHT = 500;
7     private const int RIGHT_PANE_WIDTH = 275;
8     protected IdentityManagerApp parent_app;
9 #if OS_MACOS
10         public OSXApplication osxApp;
11 #endif
12     private UIManager ui_manager = new UIManager();
13     private Entry search_entry;
14     private VBox vbox_right;
15     private VBox login_vbox;
16     private VBox services_vbox;
17     private CustomVBox custom_vbox;
18     private VBox services_internal_vbox;
19
20     private Entry username_entry;
21     private Entry password_entry;
22     private Label prompting_service;
23     private Label no_identity_title;
24     private CheckButton remember_checkbutton;
25
26     private ListStore* listmodel;
27     private TreeModelFilter filter;
28
29     public IdentityManagerModel identities_manager;
30     private unowned SList<IdCard>    candidates;
31
32     public GLib.Queue<IdentityRequest> request_queue;
33
34     private HashTable<Gtk.Button, string> service_button_map;
35
36     private enum Columns
37     {
38         IDCARD_COL,
39         LOGO_COL,
40         ISSUER_COL,
41         USERNAME_COL,
42         PASSWORD_COL,
43         N_COLUMNS
44     }
45
46     private const string layout =
47 "<menubar name='MenuBar'>" +
48 "        <menu name='FileMenu' action='FileMenuAction'>" +
49 "            <menuitem name='AddIdCard' action='AddIdCardAction' />" +
50 "            <separator />" +
51 "            <menuitem name='Quit' action='QuitAction' />" +
52 "        </menu>" +
53 "" +
54 "        <menu name='HelpMenu' action='HelpMenuAction'>" +
55 "             <menuitem name='About' action='AboutAction' />" +
56 "        </menu>" +
57 "</menubar>";
58
59     public IdentityManagerView(IdentityManagerApp app) {
60        parent_app = app;
61 #if OS_MACOS
62                 osxApp = OSXApplication.get_instance();
63 #endif
64            identities_manager = parent_app.model;
65        request_queue = new GLib.Queue<IdentityRequest>();
66        service_button_map = new HashTable<Gtk.Button, string> (direct_hash, direct_equal);
67        this.title = "Moonshot Identity Selector";
68        this.set_position (WindowPosition.CENTER);
69        set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
70        build_ui();
71        setup_list_model(); 
72        load_id_cards(); 
73        connect_signals();
74     }
75     
76     public void on_card_list_changed () {
77         load_id_cards();
78     }
79     
80     private bool visible_func (TreeModel model, TreeIter iter)
81     {
82         IdCard id_card;
83
84         model.get (iter,
85                    Columns.IDCARD_COL, out id_card);
86
87         if (id_card == null)
88             return false;
89         
90         if (candidates != null)
91         {
92             bool is_candidate = false;
93             foreach (IdCard candidate in candidates)
94             {
95                 if (candidate == id_card)
96                     is_candidate = true;
97             }
98             if (!is_candidate)
99                 return false;
100         }
101         
102         string entry_text = search_entry.get_text ();
103         if (entry_text == null || entry_text == "")
104         {
105             return true;
106         }
107
108         foreach (string search_text in entry_text.split(" "))
109         {
110             if (search_text == "")
111                 continue;
112          
113
114             string search_text_casefold = search_text.casefold ();
115
116             if (id_card.issuer != null)
117             {
118               string issuer_casefold = id_card.issuer;
119
120               if (issuer_casefold.contains (search_text_casefold))
121                   return true;
122             }
123
124             if (id_card.display_name != null)
125             {
126                 string display_name_casefold = id_card.display_name.casefold ();
127               
128                 if (display_name_casefold.contains (search_text_casefold))
129                     return true;
130             }
131             
132             if (id_card.services.length > 0)
133             {
134                 foreach (string service in id_card.services)
135                 {
136                     string service_casefold = service.casefold ();
137
138                     if (service_casefold.contains (search_text_casefold))
139                         return true;
140                 }
141             }
142         }
143         return false;
144     }
145
146     private void setup_list_model ()
147     {
148       this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
149                                                           typeof (Gdk.Pixbuf),
150                                                           typeof (string),
151                                                           typeof (string),
152                                                           typeof (string));
153       this.filter = new TreeModelFilter (listmodel, null);
154
155       filter.set_visible_func (visible_func);
156     }
157
158     private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
159     {
160         if (pos == EntryIconPosition.PRIMARY)
161         {
162             print ("Search entry icon pressed\n");
163         }
164         else
165         {
166             this.search_entry.set_text ("");
167         }
168     }
169
170     private void search_entry_text_changed_cb ()
171     {
172         this.filter.refilter ();
173         redraw_id_card_widgets ();
174
175         var has_text = this.search_entry.get_text_length () > 0;
176         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
177         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
178
179         this.vbox_right.set_visible (false);
180     }
181
182     private bool search_entry_key_press_event_cb (Gdk.EventKey e)
183     {
184         if(Gdk.keyval_name(e.keyval) == "Escape")
185            this.search_entry.set_text("");
186
187         // Continue processing this event, since the
188         // text entry functionality needs to see it too.
189         return false;
190     }
191
192     private void load_id_cards () {
193         string current_idcard_nai = null;
194         if (this.custom_vbox.current_idcard != null) {
195             current_idcard_nai = custom_vbox.current_idcard.id_card.nai;
196             custom_vbox.current_idcard = null;
197         }
198         var children = this.custom_vbox.get_children ();
199         foreach (var id_card_widget in children) {
200         remove_id_card_widget((IdCardWidget)id_card_widget);
201         }   
202         this.listmodel->clear();
203         LinkedList<IdCard> card_list = identities_manager.get_card_list() ;
204         if (card_list == null) {
205             return;
206         }
207
208         foreach (IdCard id_card in card_list) {
209             add_id_card_data (id_card);
210             IdCardWidget id_card_widget = add_id_card_widget (id_card);
211             if (id_card_widget.id_card.nai == current_idcard_nai) {
212                 fill_details(id_card_widget);
213                 id_card_widget.expand();
214             }
215         }
216         if (custom_vbox.current_idcard == null)
217             fill_details(null);
218     }
219     
220     private void fill_details (IdCardWidget? id_card_widget)
221     {
222         var vr_children = this.vbox_right.get_children();
223         foreach (var vr_child in vr_children)
224             this.vbox_right.remove(vr_child);
225         if (id_card_widget != null) {
226             var id_card = id_card_widget.id_card;
227             if (id_card.display_name == IdCard.NO_IDENTITY) {
228                 this.vbox_right.pack_start(no_identity_title, false, true, 0);
229             } else {
230                 this.username_entry.set_text (id_card.username);
231                 this.password_entry.set_text (id_card.password ?? "");
232                 this.vbox_right.pack_start(login_vbox, false, true, 0);
233                 this.remember_checkbutton.active = id_card.store_password;
234             }
235             this.vbox_right.pack_start (services_vbox, false, true, 0);
236
237             var children = this.services_internal_vbox.get_children ();
238             foreach (var hbox in children)
239                 services_internal_vbox.remove(hbox);
240             fill_services_vbox (id_card_widget.id_card);
241         }
242     }
243
244     private void show_details (IdCard id_card)
245     {
246        this.vbox_right.set_visible (!vbox_right.get_visible ());
247
248        if (this.vbox_right.get_visible () == false)
249        {
250            this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
251        }
252     }
253
254     private void details_identity_cb (IdCardWidget id_card_widget)
255     {
256        fill_details (id_card_widget);
257        show_details (id_card_widget.id_card);
258     }
259
260     private IdCard get_id_card_data (AddIdentityDialog dialog)
261     {
262         var id_card = new IdCard ();
263
264         id_card.display_name = dialog.display_name;
265         id_card.issuer = dialog.issuer;
266         if (id_card.issuer == "")
267             id_card.issuer = "Issuer";
268         id_card.username = dialog.username;
269         id_card.password = dialog.password;
270         id_card.store_password = dialog.store_password;
271         id_card.services = {};
272
273         return id_card;
274     }
275
276     private void add_id_card_data (IdCard id_card)
277     {
278         TreeIter   iter;
279         Gdk.Pixbuf pixbuf;
280         this.listmodel->append (out iter);
281         pixbuf = get_pixbuf(id_card);
282         listmodel->set (iter,
283                        Columns.IDCARD_COL, id_card,
284                        Columns.LOGO_COL, pixbuf,
285                        Columns.ISSUER_COL, id_card.issuer,
286                        Columns.USERNAME_COL, id_card.username,
287                        Columns.PASSWORD_COL, id_card.password);
288     }
289
290     private void remove_id_card_data (IdCard id_card)
291     {
292         TreeIter iter;
293         string issuer;
294
295         if (listmodel->get_iter_first (out iter))
296         {
297             do
298             {
299                 listmodel->get (iter,
300                                Columns.ISSUER_COL, out issuer);
301
302                 if (id_card.issuer == issuer)
303                 {
304                     listmodel->remove (iter);
305                     break;
306                 }
307             }
308             while (listmodel->iter_next (ref iter));
309         }
310     }
311
312     private IdCardWidget add_id_card_widget (IdCard id_card)
313     {
314         var id_card_widget = new IdCardWidget (id_card);
315         this.custom_vbox.add_id_card_widget (id_card_widget);
316         id_card_widget.details_id.connect (details_identity_cb);
317         id_card_widget.remove_id.connect (remove_identity_cb);
318         id_card_widget.send_id.connect ((w) => send_identity_cb (w.id_card));
319         id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
320         id_card_widget.expanded.connect (fill_details);
321         return id_card_widget;
322     }
323
324     public bool add_identity (IdCard id_card, bool force_flat_file_store)
325     {
326 #if OS_MACOS
327         /* 
328          * TODO: We should have a confirmation dialog, but currently it will crash on Mac OS
329          * so for now we will install silently
330          */
331         var ret = Gtk.ResponseType.YES;
332 #else
333
334         var 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 add '%s' ID Card to the ID Card Organizer?"),
339                                             id_card.display_name);
340
341         var ret = dialog.run ();
342         dialog.destroy ();
343 #endif
344
345         if (ret == Gtk.ResponseType.YES) {
346             this.identities_manager.add_card (id_card, force_flat_file_store);
347             return true;
348         }
349
350         return false;
351     }
352
353     private void add_identity_manual_cb ()
354     {
355         var dialog = new AddIdentityDialog ();
356         var result = dialog.run ();
357
358         switch (result) {
359         case ResponseType.OK:
360             this.identities_manager.add_card (get_id_card_data (dialog), false);
361             break;
362         default:
363             break;
364         }
365         dialog.destroy ();
366     }
367
368     private void remove_id_card_widget (IdCardWidget id_card_widget) {
369        this.custom_vbox.remove_id_card_widget (id_card_widget);
370     }
371
372     private void remove_identity (IdCardWidget id_card_widget)
373     {
374         var id_card = id_card_widget.id_card;
375         remove_id_card_widget (id_card_widget);
376
377         this.identities_manager.remove_card(id_card);
378     }
379
380     private void redraw_id_card_widgets ()
381     {
382         TreeIter iter;
383         IdCard id_card;
384
385         var children = this.custom_vbox.get_children ();
386         foreach (var id_card_widget in children)
387             remove_id_card_widget((IdCardWidget )id_card_widget); //id_card_widget.destroy();
388
389         if (filter.get_iter_first (out iter))
390         {
391             do
392             {
393                 filter.get (iter,
394                             Columns.IDCARD_COL, out id_card);
395
396                 add_id_card_widget (id_card);
397             }
398             while (filter.iter_next (ref iter));
399         }
400     }
401
402     private void remove_identity_cb (IdCardWidget id_card_widget)
403     {
404         var id_card = id_card_widget.id_card;
405
406         var dialog = new MessageDialog (this,
407                                         DialogFlags.DESTROY_WITH_PARENT,
408                                         MessageType.QUESTION,
409                                         Gtk.ButtonsType.YES_NO,
410                                         _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
411         var result = dialog.run ();
412         switch (result) {
413         case ResponseType.YES:
414             remove_identity (id_card_widget);
415             break;
416         default:
417             break;
418         }
419         dialog.destroy ();
420     }
421
422     public void set_prompting_service(string service)
423     {
424         prompting_service.set_label( _("Identity requested for service: %s").printf(service) );
425     }
426
427     public void queue_identity_request(IdentityRequest request)
428     {
429         if (this.request_queue.is_empty())
430         { /* setup widgets */
431             candidates = request.candidates;
432             filter.refilter();
433             redraw_id_card_widgets ();
434             set_prompting_service(request.service);
435             show ();
436         }
437         this.request_queue.push_tail (request);
438     }
439
440     public void check_add_password(IdCard identity, IdentityRequest request, IdentityManagerModel model)
441     {
442         if ((identity.password == "") && !identity.IsNoIdentity())
443         {
444             var dialog = new AddPasswordDialog (identity, request);
445             var result = dialog.run ();
446
447             switch (result) {
448             case ResponseType.OK:
449                 identity.password = dialog.password;
450                 identity.store_password = dialog.remember;
451                 model.update_card(identity);
452                 break;
453             default:
454                 identity = null;
455                 break;
456             }
457
458             dialog.destroy ();
459         }
460     }
461
462     public void send_identity_cb (IdCard identity)
463     {
464         return_if_fail (request_queue.length > 0);
465
466         candidates = null;
467         var request = this.request_queue.pop_head ();
468         check_add_password(identity, request, identities_manager);
469         if (this.request_queue.is_empty())
470         {
471             candidates = null;
472             prompting_service.set_label(_(""));
473             if (!parent_app.explicitly_launched) {
474 // The following occasionally causes the app to exit without sending the dbus
475 // reply, so for now we just don't exit
476 //                Gtk.main_quit ();
477 // just hide instead
478                 this.hide();
479             }
480         } else {
481             IdentityRequest next = this.request_queue.peek_head();
482             candidates = next.candidates;
483             set_prompting_service(next.service);
484         }
485         filter.refilter();
486         redraw_id_card_widgets ();
487
488         if (identity != null)
489             parent_app.default_id_card = identity;
490
491         request.return_identity (identity);
492     }
493
494     private void label_make_bold (Label label)
495     {
496         var font_desc = new Pango.FontDescription ();
497
498         font_desc.set_weight (Pango.Weight.BOLD);
499
500         /* This will only affect the weight of the font, the rest is
501          * from the current state of the widget, which comes from the
502          * theme or user prefs, since the font desc only has the
503          * weight flag turned on.
504          */
505         label.modify_font (font_desc);
506     }
507
508     private void fill_services_vbox (IdCard id_card)
509     {
510         int i = 0;
511         var n_columns = id_card.services.length;
512
513         var services_table = new Table (n_columns, 2, false);
514         services_table.set_col_spacings (10);
515         services_table.set_row_spacings (10);
516         this.services_internal_vbox.add (services_table);
517         
518         service_button_map.remove_all ();
519
520         foreach (string service in id_card.services)
521         {
522             var label = new Label (service);
523             label.set_alignment (0, (float) 0.5);
524 #if VALA_0_12
525             var remove_button = new Button.from_stock (Stock.REMOVE);
526 #else
527             var remove_button = new Button.from_stock (STOCK_REMOVE);
528 #endif
529
530
531             service_button_map.insert (remove_button, service);
532             
533             remove_button.clicked.connect ((remove_button) =>
534             {
535               var candidate = service_button_map.lookup (remove_button);
536               if (candidate == null)
537                 return;
538               var dialog = new Gtk.MessageDialog (this,
539                                       Gtk.DialogFlags.DESTROY_WITH_PARENT,
540                                       Gtk.MessageType.QUESTION,
541                                       Gtk.ButtonsType.YES_NO,
542                                       _("Are you sure you want to stop '%s' ID Card from being used with %s?"),
543                                       custom_vbox.current_idcard.id_card.display_name,
544                                       candidate);
545               var ret = dialog.run();
546               dialog.hide();
547               
548               if (ret == Gtk.ResponseType.YES)
549               {
550                 IdCard idcard = custom_vbox.current_idcard.id_card;
551                 if (idcard != null) {
552                   SList<string> services = new SList<string>();
553                 
554                   foreach (string srv in idcard.services)
555                   {
556                     if (srv == candidate)
557                       continue;
558                     services.append (srv);
559                   }
560                 
561                   idcard.services = new string[services.length()];
562                   for (int j=0; j<idcard.services.length; j++)
563                   {
564                     idcard.services[j] = services.nth_data(j);
565                   }
566                 
567                   identities_manager.update_card(idcard);
568                 }
569               }
570               
571             });
572             services_table.attach_defaults (label, 0, 1, i, i+1);
573             services_table.attach_defaults (remove_button, 1, 2, i, i+1);
574             i++;
575         }
576         this.services_internal_vbox.show_all ();
577     }
578
579     private void on_about_action ()
580     {
581         string[] authors = {
582             "Javier Jardón <jjardon@codethink.co.uk>",
583             "Sam Thursfield <samthursfield@codethink.co.uk>",
584             "Alberto Ruiz <alberto.ruiz@codethink.co.uk>",
585             null
586         };
587
588         string copyright = "Copyright 2011 JANET";
589
590         string license =
591 """
592 Copyright (c) 2011, JANET(UK)
593 All rights reserved.
594
595 Redistribution and use in source and binary forms, with or without
596 modification, are permitted provided that the following conditions
597 are met:
598
599 1. Redistributions of source code must retain the above copyright
600    notice, this list of conditions and the following disclaimer.
601
602 2. Redistributions in binary form must reproduce the above copyright
603    notice, this list of conditions and the following disclaimer in the
604    documentation and/or other materials provided with the distribution.
605
606 3. Neither the name of JANET(UK) nor the names of its contributors
607    may be used to endorse or promote products derived from this software
608    without specific prior written permission.
609
610 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
611 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
612 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
613 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
614 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
615 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
616 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
617 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
618 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
619 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
620 SUCH DAMAGE.
621 """;
622
623         Gtk.show_about_dialog (this,
624             "comments", _("Moonshot project UI"),
625             "copyright", copyright,
626             "website", Config.PACKAGE_URL,
627             "version", Config.PACKAGE_VERSION,
628             "license", license,
629             "website-label", _("Visit the Moonshot project web site"),
630             "authors", authors,
631             "translator-credits", _("translator-credits"),
632             null
633         );
634     }
635
636     private Gtk.ActionEntry[] create_actions() {
637         Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
638
639         Gtk.ActionEntry filemenu = { "FileMenuAction",
640                                      null,
641                                      N_("_File"),
642                                      null, null, null };
643         actions += filemenu;
644         Gtk.ActionEntry add = { "AddIdCardAction",
645 #if VALA_0_12
646                                 Stock.ADD,
647 #else
648                                 STOCK_ADD,
649 #endif
650                                 N_("Add ID Card"),
651                                 null,
652                                 N_("Add a new ID Card"),
653                                 add_identity_manual_cb };
654         actions += add;
655         Gtk.ActionEntry quit = { "QuitAction",
656 #if VALA_0_12
657                                  Stock.QUIT,
658 #else
659                                  STOCK_QUIT,
660 #endif
661                                  N_("Quit"),
662                                  "<control>Q",
663                                  N_("Quit the application"),
664                                  Gtk.main_quit };
665         actions += quit;
666
667         Gtk.ActionEntry helpmenu = { "HelpMenuAction",
668                                      null,
669                                      N_("_Help"),
670                                      null, null, null };
671         actions += helpmenu;
672         Gtk.ActionEntry about = { "AboutAction",
673 #if VALA_0_12
674                                   Stock.ABOUT,
675 #else
676                                   STOCK_ABOUT,
677 #endif
678                                   N_("About"),
679                                   null,
680                                   N_("About this application"),
681                                   on_about_action };
682         actions += about;
683
684         return actions;
685     }
686
687
688     private void create_ui_manager ()
689     {
690         Gtk.ActionGroup action_group = new Gtk.ActionGroup ("GeneralActionGroup");
691         action_group.add_actions (create_actions (), this);
692         ui_manager.insert_action_group (action_group, 0);
693         try
694         {
695             ui_manager.add_ui_from_string (layout, -1);
696         }
697         catch (Error e)
698         {
699             stderr.printf ("%s\n", e.message);
700         }
701         ui_manager.ensure_update ();
702     }
703
704     private void build_ui()
705     {
706         create_ui_manager ();
707
708         this.search_entry = new Entry();
709
710         set_atk_name_description (search_entry, _("Search entry"), _("Search for a specific ID Card"));
711         this.search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY,
712                                                 find_icon_sized ("edit-find", Gtk.IconSize.MENU));
713 //                                                find_icon_sized ("edit-find-symbolic", Gtk.IconSize.MENU));
714         this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY,
715                                                  _("Search identity or service"));
716         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false);
717
718         this.search_entry.set_icon_from_pixbuf (EntryIconPosition.SECONDARY,
719                                                 find_icon_sized ("process-stop", Gtk.IconSize.MENU));
720 //                                                find_icon_sized ("edit-clear-symbolic", Gtk.IconSize.MENU));
721         this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY,
722                                                  _("Clear the current search"));
723         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false);
724
725
726         this.search_entry.icon_press.connect (search_entry_icon_press_cb);
727         this.search_entry.notify["text"].connect (search_entry_text_changed_cb);
728         this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
729
730         this.custom_vbox = new CustomVBox (this, false, 6);
731
732         var viewport = new Viewport (null, null);
733         viewport.set_border_width (6);
734         viewport.set_shadow_type (ShadowType.NONE);
735         viewport.add (custom_vbox);
736         var scroll = new ScrolledWindow (null, null);
737         scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
738         scroll.set_shadow_type (ShadowType.IN);
739         scroll.add_with_viewport (viewport);
740         this.prompting_service = new Label (_(""));
741         // left-align
742         prompting_service.set_alignment(0, (float )0.5);
743
744         var vbox_left = new VBox (false, 0);
745         vbox_left.pack_start (search_entry, false, false, 6);
746         vbox_left.pack_start (scroll, true, true, 0);
747         vbox_left.pack_start (prompting_service, false, false, 6);
748         vbox_left.set_size_request (WINDOW_WIDTH, 0);
749
750         this.no_identity_title = new Label (_("No Identity: Send this identity to services which should not use Moonshot"));
751         no_identity_title.set_alignment(0, (float ) 0.5);
752         no_identity_title.set_size_request(RIGHT_PANE_WIDTH, -1);
753         no_identity_title.set_line_wrap(true);
754         no_identity_title.show();
755
756         var login_vbox_title = new Label (_("Login: "));
757         label_make_bold (login_vbox_title);
758         login_vbox_title.set_alignment (0, (float) 0.5);
759         var username_label = new Label (_("Username:"));
760         username_label.set_alignment (1, (float) 0.5);
761         this.username_entry = new Entry ();
762         var password_label = new Label (_("Password:"));
763         password_label.set_alignment (1, (float) 0.5);
764         this.password_entry = new Entry ();
765         password_entry.set_invisible_char ('*');
766         password_entry.set_visibility (false);
767         this.remember_checkbutton = new CheckButton.with_label (_("Remember password"));
768         var login_table = new Table (3, 3, false);
769         login_table.set_col_spacings (10);
770         login_table.set_row_spacings (10);
771         login_table.attach_defaults (username_label, 0, 1, 0, 1);
772         login_table.attach_defaults (username_entry, 1, 2, 0, 1);
773         login_table.attach_defaults (password_label, 0, 1, 1, 2);
774         login_table.attach_defaults (password_entry, 1, 2, 1, 2);
775         login_table.attach_defaults (remember_checkbutton,  1, 2, 2, 3);
776         var login_vbox_alignment = new Alignment (0, 0, 0, 0);
777         login_vbox_alignment.set_padding (0, 0, 12, 0);
778         login_vbox_alignment.add (login_table);
779         this.login_vbox = new VBox (false, 6);
780         login_vbox.pack_start (login_vbox_title, false, true, 0);
781         login_vbox.pack_start (login_vbox_alignment, false, true, 0);
782
783         var services_vbox_title = new Label (_("Services:"));
784         label_make_bold (services_vbox_title);
785         services_vbox_title.set_alignment (0, (float) 0.5);
786         var services_vbox_alignment = new Alignment (0, 0, 0, 0);
787         services_vbox_alignment.set_padding (0, 0, 12, 0);
788         this.services_internal_vbox = new VBox (true, 6);
789         services_vbox_alignment.add (services_internal_vbox);
790         this.services_vbox = new VBox (false, 6);
791         services_vbox.pack_start (services_vbox_title, false, true, 0);
792         services_vbox.pack_start (services_vbox_alignment, false, true, 0);
793
794         this.vbox_right = new VBox (false, 18);
795         vbox_right.pack_start (login_vbox, false, true, 0);
796         vbox_right.pack_start (services_vbox, false, true, 0);
797         vbox_right.set_size_request( RIGHT_PANE_WIDTH, -1 );
798
799         var hbox = new HBox (false, 12);
800         hbox.pack_start (vbox_left, true, true, 0);
801         hbox.pack_start (vbox_right, false, false, 0);
802
803         var main_vbox = new VBox (false, 0);
804         main_vbox.set_border_width (12);
805  
806 #if OS_MACOS
807         // hide the  File | Quit menu item which is now on the Mac Menu
808         Gtk.Widget quit_item =  this.ui_manager.get_widget("/MenuBar/FileMenu/Quit");
809         quit_item.hide();
810         
811                 Gtk.MenuShell menushell = this.ui_manager.get_widget("/MenuBar") as Gtk.MenuShell;
812                 osxApp.set_menu_bar(menushell);
813                 osxApp.set_use_quartz_accelerators(true);
814                 osxApp.sync_menu_bar();
815                 osxApp.ready(); 
816 #else
817         var menubar = this.ui_manager.get_widget ("/MenuBar");
818         main_vbox.pack_start (menubar, false, false, 0);
819 #endif
820         main_vbox.pack_start (hbox, true, true, 0);
821         add (main_vbox);
822         main_vbox.show_all();
823         this.vbox_right.hide ();
824   } 
825
826     private void set_atk_name_description (Widget widget, string name, string description)
827     {
828        var atk_widget = widget.get_accessible ();
829
830        atk_widget.set_name (name);
831        atk_widget.set_description (description);
832     }
833
834     private void connect_signals()
835     {
836         this.destroy.connect (Gtk.main_quit);
837         this.identities_manager.card_list_changed.connect(this.on_card_list_changed);
838     }
839 }
840
841