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