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