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