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