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