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