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