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