Track whether launched by user or by dbus (LP: #1166456)
[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 CustomVBox custom_vbox;
15     private VBox services_internal_vbox;
16
17     private Entry username_entry;
18     private Entry password_entry;
19
20     private ListStore* listmodel;
21     private TreeModelFilter filter;
22
23     public IdentityManagerModel identities_manager;
24     private unowned SList<IdCard>    candidates;
25
26     public GLib.Queue<IdentityRequest> request_queue;
27
28     private HashTable<Gtk.Button, string> service_button_map;
29
30     private enum Columns
31     {
32         IDCARD_COL,
33         LOGO_COL,
34         ISSUER_COL,
35         USERNAME_COL,
36         PASSWORD_COL,
37         N_COLUMNS
38     }
39
40     private const string layout =
41 "<menubar name='MenuBar'>" +
42 "        <menu name='FileMenu' action='FileMenuAction'>" +
43 "            <menuitem name='AddIdCard' action='AddIdCardAction' />" +
44 "            <separator />" +
45 "            <menuitem name='Quit' action='QuitAction' />" +
46 "        </menu>" +
47 "" +
48 "        <menu name='HelpMenu' action='HelpMenuAction'>" +
49 "             <menuitem name='About' action='AboutAction' />" +
50 "        </menu>" +
51 "</menubar>";
52
53     public IdentityManagerView(IdentityManagerApp app) {
54        parent_app = app;
55 #if OS_MACOS
56                 osxApp = OSXApplication.get_instance();
57 #endif
58            identities_manager = parent_app.model;
59        request_queue = new GLib.Queue<IdentityRequest>();
60        service_button_map = new HashTable<Gtk.Button, string> (direct_hash, direct_equal);
61        this.title = "Moonshot Identity Selector";
62        this.set_position (WindowPosition.CENTER);
63        set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
64        build_ui();
65        setup_list_model(); 
66        load_id_cards(); 
67        connect_signals();
68     }
69     
70     public void on_card_list_changed () {
71         load_id_cards();
72     }
73     
74     private bool visible_func (TreeModel model, TreeIter iter)
75     {
76         IdCard id_card;
77
78         model.get (iter,
79                    Columns.IDCARD_COL, out id_card);
80
81         if (id_card == null)
82             return false;
83         
84         if (candidates != null)
85         {
86             bool is_candidate = false;
87             foreach (IdCard candidate in candidates)
88             {
89                 if (candidate == id_card)
90                     is_candidate = true;
91             }
92             if (!is_candidate)
93                 return false;
94         }
95         
96         string entry_text = search_entry.get_text ();
97         if (entry_text == null || entry_text == "")
98         {
99             return true;
100         }
101
102         foreach (string search_text in entry_text.split(" "))
103         {
104             if (search_text == "")
105                 continue;
106          
107
108             string search_text_casefold = search_text.casefold ();
109
110             if (id_card.issuer != null)
111             {
112               string issuer_casefold = id_card.issuer;
113
114               if (issuer_casefold.contains (search_text_casefold))
115                   return true;
116             }
117
118             if (id_card.display_name != null)
119             {
120                 string display_name_casefold = id_card.display_name.casefold ();
121               
122                 if (display_name_casefold.contains (search_text_casefold))
123                     return true;
124             }
125             
126             if (id_card.services.length > 0)
127             {
128                 foreach (string service in id_card.services)
129                 {
130                     string service_casefold = service.casefold ();
131
132                     if (service_casefold.contains (search_text_casefold))
133                         return true;
134                 }
135             }
136         }
137         return false;
138     }
139
140     private void setup_list_model ()
141     {
142       this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
143                                                           typeof (Gdk.Pixbuf),
144                                                           typeof (string),
145                                                           typeof (string),
146                                                           typeof (string));
147       this.filter = new TreeModelFilter (listmodel, null);
148
149       filter.set_visible_func (visible_func);
150     }
151
152     private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
153     {
154         if (pos == EntryIconPosition.PRIMARY)
155         {
156             print ("Search entry icon pressed\n");
157         }
158         else
159         {
160             this.search_entry.set_text ("");
161         }
162     }
163
164     private void search_entry_text_changed_cb ()
165     {
166         this.filter.refilter ();
167         redraw_id_card_widgets ();
168
169         var has_text = this.search_entry.get_text_length () > 0;
170         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
171         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
172
173         this.vbox_right.set_visible (false);
174     }
175
176     private bool search_entry_key_press_event_cb (Gdk.EventKey e)
177     {
178         if(Gdk.keyval_name(e.keyval) == "Escape")
179            this.search_entry.set_text("");
180
181         // Continue processing this event, since the
182         // text entry functionality needs to see it too.
183         return false;
184     }
185
186     private void load_id_cards () {
187         var children = this.custom_vbox.get_children ();
188         foreach (var id_card_widget in children) {
189         remove_id_card_widget((IdCardWidget)id_card_widget);
190         }   
191         this.listmodel->clear();
192         LinkedList<IdCard> card_list = identities_manager.get_card_list() ;
193         if (card_list == null) {
194             return;
195         }
196
197         foreach (IdCard id_card in card_list) {
198             add_id_card_data (id_card);
199             add_id_card_widget (id_card);
200         }
201     }
202     
203     private void fill_details (IdCardWidget id_card_widget)
204     {
205        var id_card = id_card_widget.id_card;
206        this.username_entry.set_text (id_card.username);
207        this.password_entry.set_text (id_card.password ?? "");
208
209        var children = this.services_internal_vbox.get_children ();
210        foreach (var hbox in children)
211            services_internal_vbox.remove(hbox);
212        fill_services_vbox (id_card_widget.id_card);
213 //       identities_manager.store_id_cards();
214     }
215
216     private void show_details (IdCard id_card)
217     {
218        this.vbox_right.set_visible (!vbox_right.get_visible ());
219
220        if (this.vbox_right.get_visible () == false)
221        {
222            this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
223        }
224     }
225
226     private void details_identity_cb (IdCardWidget id_card_widget)
227     {
228        fill_details (id_card_widget);
229        show_details (id_card_widget.id_card);
230     }
231
232     private IdCard get_id_card_data (AddIdentityDialog dialog)
233     {
234         var id_card = new IdCard ();
235
236         id_card.display_name = dialog.display_name;
237         id_card.issuer = dialog.issuer;
238         if (id_card.issuer == "")
239             id_card.issuer = "Issuer";
240         id_card.username = dialog.username;
241         id_card.password = dialog.password;
242         id_card.services = {};
243         id_card.set_data("pixbuf", find_icon ("avatar-default", 48));
244
245         return id_card;
246     }
247
248     private void add_id_card_data (IdCard id_card)
249     {
250         TreeIter   iter;
251         Gdk.Pixbuf pixbuf;
252         this.listmodel->append (out iter);
253         pixbuf = id_card.get_data("pixbuf");
254         listmodel->set (iter,
255                        Columns.IDCARD_COL, id_card,
256                        Columns.LOGO_COL, pixbuf,
257                        Columns.ISSUER_COL, id_card.issuer,
258                        Columns.USERNAME_COL, id_card.username,
259                        Columns.PASSWORD_COL, id_card.password);
260     }
261
262     private void remove_id_card_data (IdCard id_card)
263     {
264         TreeIter iter;
265         string issuer;
266
267         if (listmodel->get_iter_first (out iter))
268         {
269             do
270             {
271                 listmodel->get (iter,
272                                Columns.ISSUER_COL, out issuer);
273
274                 if (id_card.issuer == issuer)
275                 {
276                     listmodel->remove (iter);
277                     break;
278                 }
279             }
280             while (listmodel->iter_next (ref iter));
281         }
282     }
283
284     private void add_id_card_widget (IdCard id_card)
285     {
286         var id_card_widget = new IdCardWidget (id_card);
287         this.custom_vbox.add_id_card_widget (id_card_widget);
288         id_card_widget.details_id.connect (details_identity_cb);
289         id_card_widget.remove_id.connect (remove_identity_cb);
290         id_card_widget.send_id.connect ((w) => send_identity_cb (w.id_card));
291         id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
292         id_card_widget.expanded.connect (fill_details);
293     }
294
295     /* This method finds a valid display name */
296     public bool display_name_is_valid (string name,
297                                        out string? candidate)
298     {
299         foreach (IdCard id_card in identities_manager.get_card_list())
300         {
301           if (id_card.display_name == name)
302           {
303             if (&candidate != null)
304             {
305               for (int i=0; i<1000; i++)
306               {
307                 string tmp = "%s %d".printf (name, i);
308                 if (display_name_is_valid (tmp, null))
309                 {
310                   candidate = tmp;
311                   break;
312                 }
313               }
314             }
315             return false;
316           }
317         }
318         
319         return true;
320     }
321     
322     public void insert_id_card (IdCard id_card)
323     {
324         string candidate;
325         
326         if (!display_name_is_valid (id_card.display_name, out candidate))
327         {
328           id_card.display_name = candidate;
329         }
330     
331     this.identities_manager.add_card(id_card);
332     }
333
334     public bool add_identity (IdCard id_card)
335     {
336 #if OS_MACOS
337         /* 
338          * TODO: We should have a confirmation dialog, but currently it will crash on Mac OS
339          * so for now we will install silently
340          */
341         var ret = Gtk.ResponseType.YES;
342 #else
343
344         var dialog = new Gtk.MessageDialog (this,
345                                             Gtk.DialogFlags.DESTROY_WITH_PARENT,
346                                             Gtk.MessageType.QUESTION,
347                                             Gtk.ButtonsType.YES_NO,
348                                             _("Would you like to add '%s' ID Card to the ID Card Organizer?"),
349                                             id_card.display_name);
350
351         var ret = dialog.run ();
352         dialog.destroy ();
353 #endif
354
355         if (ret == Gtk.ResponseType.YES) {
356             id_card.set_data ("pixbuf", find_icon ("avatar-default", 48));
357             this.insert_id_card (id_card);
358             return true;
359         }
360
361         return false;
362     }
363
364     private void add_identity_manual_cb ()
365     {
366         var dialog = new AddIdentityDialog ();
367         var result = dialog.run ();
368
369         switch (result) {
370         case ResponseType.OK:
371             insert_id_card (get_id_card_data (dialog));
372             break;
373         default:
374             break;
375         }
376         dialog.destroy ();
377     }
378
379     private void remove_id_card_widget (IdCardWidget id_card_widget) {
380        this.custom_vbox.remove_id_card_widget (id_card_widget);
381     }
382
383     private void remove_identity (IdCardWidget id_card_widget)
384     {
385         var id_card = id_card_widget.id_card;
386         remove_id_card_widget (id_card_widget);
387
388         this.identities_manager.remove_card(id_card);
389     }
390
391     private void redraw_id_card_widgets ()
392     {
393         TreeIter iter;
394         IdCard id_card;
395
396         var children = this.custom_vbox.get_children ();
397         foreach (var id_card_widget in children)
398             remove_id_card_widget((IdCardWidget )id_card_widget); //id_card_widget.destroy();
399
400         if (filter.get_iter_first (out iter))
401         {
402             do
403             {
404                 filter.get (iter,
405                             Columns.IDCARD_COL, out id_card);
406
407                 add_id_card_widget (id_card);
408             }
409             while (filter.iter_next (ref iter));
410         }
411     }
412
413     private void remove_identity_cb (IdCardWidget id_card_widget)
414     {
415         var id_card = id_card_widget.id_card;
416
417         var dialog = new MessageDialog (this,
418                                         DialogFlags.DESTROY_WITH_PARENT,
419                                         MessageType.QUESTION,
420                                         Gtk.ButtonsType.YES_NO,
421                                         _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
422         var result = dialog.run ();
423         switch (result) {
424         case ResponseType.YES:
425             remove_identity (id_card_widget);
426             break;
427         default:
428             break;
429         }
430         dialog.destroy ();
431     }
432
433     public void queue_identity_request(IdentityRequest request)
434     {
435         if (this.request_queue.is_empty())
436         { /* setup widgets */
437             candidates = request.candidates;
438             filter.refilter();
439             redraw_id_card_widgets ();
440             show ();
441         }
442         this.request_queue.push_tail (request);
443     }
444
445     public void send_identity_cb (IdCard identity)
446     {
447         return_if_fail (request_queue.length > 0);
448
449         candidates = null;
450         var request = this.request_queue.pop_head ();
451         bool reset_password = false;
452
453         if (identity.password == null)
454         {
455             var dialog = new AddPasswordDialog ();
456             var result = dialog.run ();
457
458             switch (result) {
459             case ResponseType.OK:
460                 identity.password = dialog.password;
461                 reset_password = ! dialog.remember;
462                 break;
463             default:
464                 identity = null;
465                 break;
466             }
467
468             dialog.destroy ();
469         }
470
471         if (this.request_queue.is_empty())
472         {
473             candidates = null;
474             if (!parent_app.explicitly_launched)
475                 Gtk.main_quit ();
476         } else {
477             candidates = this.request_queue.peek_head().candidates;
478         }
479         filter.refilter();
480         redraw_id_card_widgets ();
481
482         if (identity != null)
483             parent_app.default_id_card = identity;
484
485         request.return_identity (identity);
486
487         if (reset_password)
488             identity.password = null;
489
490     }
491
492     private void label_make_bold (Label label)
493     {
494         var font_desc = new Pango.FontDescription ();
495
496         font_desc.set_weight (Pango.Weight.BOLD);
497
498         /* This will only affect the weight of the font, the rest is
499          * from the current state of the widget, which comes from the
500          * theme or user prefs, since the font desc only has the
501          * weight flag turned on.
502          */
503         label.modify_font (font_desc);
504     }
505
506     private void fill_services_vbox (IdCard id_card)
507     {
508         int i = 0;
509         var n_columns = id_card.services.length;
510
511         var services_table = new Table (n_columns, 2, false);
512         services_table.set_col_spacings (10);
513         services_table.set_row_spacings (10);
514         this.services_internal_vbox.add (services_table);
515         
516         service_button_map.remove_all ();
517
518         foreach (string service in id_card.services)
519         {
520             var label = new Label (service);
521             label.set_alignment (0, (float) 0.5);
522 #if VALA_0_12
523             var remove_button = new Button.from_stock (Stock.REMOVE);
524 #else
525             var remove_button = new Button.from_stock (STOCK_REMOVE);
526 #endif
527
528
529             service_button_map.insert (remove_button, service);
530             
531             remove_button.clicked.connect ((remove_button) =>
532             {
533               var dialog = new Gtk.MessageDialog (this,
534                                       Gtk.DialogFlags.DESTROY_WITH_PARENT,
535                                       Gtk.MessageType.QUESTION,
536                                       Gtk.ButtonsType.YES_NO,
537                                       _("Are you sure you want to stop '%s' ID Card from being used with %s?"),
538                                       custom_vbox.current_idcard.id_card.display_name,
539                                       _("this service"));
540               var ret = dialog.run();
541               dialog.hide();
542               
543               if (ret == Gtk.ResponseType.YES)
544               {
545                 IdCard idcard = custom_vbox.current_idcard.id_card;
546                 var candidate = service_button_map.lookup (remove_button);
547
548                 SList<string> services = new SList<string>();
549                 
550                 foreach (string srv in idcard.services)
551                 {
552                   if (srv == candidate)
553                     continue;
554                   services.append (srv);
555                 }
556                 
557                 idcard.services = new string[services.length()];
558                 for (int j=0; j<idcard.services.length; j++)
559                 {
560                   idcard.services[j] = services.nth_data(j);
561                 }
562                 
563                 var children = services_internal_vbox.get_children ();
564                 foreach (var hbox in children)
565                   services_internal_vbox.remove(hbox);
566                 
567                 fill_services_vbox (idcard);
568                 custom_vbox.current_idcard.update_id_card_label ();
569                 identities_manager.update_card(idcard);
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
742         var vbox_left = new VBox (false, 0);
743         vbox_left.pack_start (search_entry, false, false, 6);
744         vbox_left.pack_start (scroll, true, true, 0);
745         vbox_left.set_size_request (WINDOW_WIDTH, 0);
746
747         var login_vbox_title = new Label (_("Login: "));
748         label_make_bold (login_vbox_title);
749         login_vbox_title.set_alignment (0, (float) 0.5);
750         var username_label = new Label (_("Username:"));
751         username_label.set_alignment (1, (float) 0.5);
752         this.username_entry = new Entry ();
753         var password_label = new Label (_("Password:"));
754         password_label.set_alignment (1, (float) 0.5);
755         this.password_entry = new Entry ();
756         password_entry.set_invisible_char ('*');
757         password_entry.set_visibility (false);
758         var remember_checkbutton = new CheckButton.with_label (_("Remember password"));
759         var login_table = new Table (3, 3, false);
760         login_table.set_col_spacings (10);
761         login_table.set_row_spacings (10);
762         login_table.attach_defaults (username_label, 0, 1, 0, 1);
763         login_table.attach_defaults (username_entry, 1, 2, 0, 1);
764         login_table.attach_defaults (password_label, 0, 1, 1, 2);
765         login_table.attach_defaults (password_entry, 1, 2, 1, 2);
766         login_table.attach_defaults (remember_checkbutton,  1, 2, 2, 3);
767         var login_vbox_alignment = new Alignment (0, 0, 0, 0);
768         login_vbox_alignment.set_padding (0, 0, 12, 0);
769         login_vbox_alignment.add (login_table);
770         var login_vbox = new VBox (false, 6);
771         login_vbox.pack_start (login_vbox_title, false, true, 0);
772         login_vbox.pack_start (login_vbox_alignment, false, true, 0);
773
774         var services_vbox_title = new Label (_("Services:"));
775         label_make_bold (services_vbox_title);
776         services_vbox_title.set_alignment (0, (float) 0.5);
777         var services_vbox_alignment = new Alignment (0, 0, 0, 0);
778         services_vbox_alignment.set_padding (0, 0, 12, 0);
779         this.services_internal_vbox = new VBox (true, 6);
780         services_vbox_alignment.add (services_internal_vbox);
781         var services_vbox = new VBox (false, 6);
782         services_vbox.pack_start (services_vbox_title, false, true, 0);
783         services_vbox.pack_start (services_vbox_alignment, false, true, 0);
784
785         this.vbox_right = new VBox (false, 18);
786         vbox_right.pack_start (login_vbox, false, true, 0);
787         vbox_right.pack_start (services_vbox, false, true, 0);
788
789         var hbox = new HBox (false, 12);
790         hbox.pack_start (vbox_left, true, true, 0);
791         hbox.pack_start (vbox_right, false, false, 0);
792
793         var main_vbox = new VBox (false, 0);
794         main_vbox.set_border_width (12);
795  
796 #if OS_MACOS
797         // hide the  File | Quit menu item which is now on the Mac Menu
798         Gtk.Widget quit_item =  this.ui_manager.get_widget("/MenuBar/FileMenu/Quit");
799         quit_item.hide();
800         
801                 Gtk.MenuShell menushell = this.ui_manager.get_widget("/MenuBar") as Gtk.MenuShell;
802                 osxApp.set_menu_bar(menushell);
803                 osxApp.set_use_quartz_accelerators(true);
804                 osxApp.sync_menu_bar();
805                 osxApp.ready(); 
806 #else
807         var menubar = this.ui_manager.get_widget ("/MenuBar");
808         main_vbox.pack_start (menubar, false, false, 0);
809 #endif
810         main_vbox.pack_start (hbox, true, true, 0);
811         add (main_vbox);
812         main_vbox.show_all();
813         this.vbox_right.hide ();
814   } 
815
816     private void set_atk_name_description (Widget widget, string name, string description)
817     {
818        var atk_widget = widget.get_accessible ();
819
820        atk_widget.set_name (name);
821        atk_widget.set_description (description);
822     }
823
824     private void connect_signals()
825     {
826         this.destroy.connect (Gtk.main_quit);
827         this.identities_manager.card_list_changed.connect(this.on_card_list_changed);
828     }
829 }
830
831