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