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