moonshot-window: Fix typos about the use of Stock icons
[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_rigth;
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     private IdentitiesManager identities_manager;
21
22     private MoonshotServer ipc_server;
23
24     public IdCardWidget selected_id_card_widget;
25
26     private SourceFunc callback;
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 "
40 <menubar name='MenuBar'>
41         <menu name='FileMenu' action='FileMenuAction'>
42             <menuitem name='AddIdCard' action='AddIdCardAction' />
43             <separator />
44             <menuitem name='Quit' action='QuitAction' />
45         </menu>
46
47         <menu name='HelpMenu' action='HelpMenuAction'>
48              <menuitem name='About' action='AboutAction' />
49         </menu>
50 </menubar>
51 ";
52
53     public MainWindow()
54     {
55         this.title = "Moonshoot";
56         this.set_position (WindowPosition.CENTER);
57         set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
58
59         build_ui();
60         setup_identities_list();
61         load_gss_eap_id_file();
62         //load_id_cards();
63         connect_signals();
64         init_ipc_server();
65     }
66
67     private bool visible_func (TreeModel model, TreeIter iter)
68     {
69         string issuer;
70         string search_text;
71         string issuer_casefold;
72         string search_text_casefold;
73
74         model.get (iter,
75                    Columns.ISSUER_COL, out issuer);
76         search_text = this.search_entry.get_text ();
77
78         if (issuer == null || search_text == null)
79             return false;
80
81         issuer_casefold = issuer.casefold ();
82         search_text_casefold = search_text.casefold ();
83
84         if (issuer_casefold.contains (search_text_casefold))
85             return true;
86
87         return false;
88     }
89
90     private void setup_identities_list ()
91     {
92        this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
93                                                           typeof (Gdk.Pixbuf),
94                                                           typeof (string),
95                                                           typeof (string),
96                                                           typeof (string));
97       this.filter = new TreeModelFilter (listmodel, null);
98
99       filter.set_visible_func (visible_func);
100     }
101
102     private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
103     {
104         if (pos == EntryIconPosition.PRIMARY)
105         {
106             print ("Search entry icon pressed\n");
107         }
108         else
109         {
110             this.search_entry.set_text ("");
111         }
112     }
113
114     private void search_entry_text_changed_cb ()
115     {
116         this.filter.refilter ();
117         redraw_id_card_widgets ();
118
119         var has_text = this.search_entry.get_text_length () > 0;
120         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
121         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
122
123         this.vbox_rigth.set_visible (false);
124         this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
125     }
126
127     private bool search_entry_key_press_event_cb (Gdk.EventKey e)
128     {
129         if(Gdk.keyval_name(e.keyval) == "Escape")
130            this.search_entry.set_text("");
131
132         // Continue processing this event, since the
133         // text entry functionality needs to see it too.
134         return false;
135     }
136
137     private void load_gss_eap_id_file ()
138     {
139         IdCard id_card;
140
141         this.identities_manager = new IdentitiesManager ();
142
143         id_card = this.identities_manager.load_gss_eap_id_file ();
144         if (id_card != null)
145         {
146             add_id_card_data (id_card);
147             add_id_card_widget (id_card);
148         }
149     }
150
151     private void load_id_cards ()
152     {
153         this.identities_manager = new IdentitiesManager ();
154
155         foreach (IdCard id_card in identities_manager.id_card_list)
156         {
157             add_id_card_data (id_card);
158             add_id_card_widget (id_card);
159         }
160     }
161
162     private void fill_details (IdCardWidget id_card_widget)
163     {
164        var id_card = id_card_widget.id_card;
165        this.username_entry.set_text (id_card.username);
166        this.password_entry.set_text (id_card.password);
167
168        var children = this.services_internal_vbox.get_children ();
169        foreach (var hbox in children)
170            hbox.destroy();
171        fill_services_vbox (id_card_widget.id_card);
172     }
173
174     private void show_details (IdCard id_card)
175     {
176        this.vbox_rigth.set_visible (!vbox_rigth.get_visible ());
177
178        if (this.vbox_rigth.get_visible () == false)
179        {
180            this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
181        }
182     }
183
184     private void details_identity_cb (IdCardWidget id_card_widget)
185     {
186        fill_details (id_card_widget);
187        show_details (id_card_widget.id_card);
188     }
189
190     private IdCard get_id_card_data (AddIdentityDialog dialog)
191     {
192         var id_card = new IdCard ();
193
194         id_card.issuer = dialog.issuer;
195         if (id_card.issuer == "")
196             id_card.issuer = "Issuer";
197         id_card.username = dialog.username;
198         id_card.password = dialog.password;
199         id_card.nai = id_card.username + "@" + id_card.issuer;
200         id_card.pixbuf = find_icon ("avatar-default", 48);
201         id_card.services = {"email","jabber","irc"};
202
203         return id_card;
204     }
205
206     private void add_id_card_data (IdCard id_card)
207     {
208         TreeIter iter;
209
210         this.listmodel.append (out iter);
211         listmodel.set (iter,
212                        Columns.IDCARD_COL, id_card,
213                        Columns.LOGO_COL, id_card.pixbuf,
214                        Columns.ISSUER_COL, id_card.issuer,
215                        Columns.USERNAME_COL, id_card.username,
216                        Columns.PASSWORD_COL, id_card.password);
217     }
218
219     private void remove_id_card_data (IdCard id_card)
220     {
221         TreeIter iter;
222         string issuer;
223
224         if (listmodel.get_iter_first (out iter))
225         {
226             do
227             {
228                 listmodel.get (iter,
229                                Columns.ISSUER_COL, out issuer);
230
231                 if (id_card.issuer == issuer)
232                 {
233                     listmodel.remove (iter);
234                     break;
235                 }
236             }
237             while (listmodel.iter_next (ref iter));
238         }
239     }
240
241     private void add_id_card_widget (IdCard id_card)
242     {
243         var id_card_widget = new IdCardWidget (id_card);
244
245         this.custom_vbox.add_id_card_widget (id_card_widget);
246
247         id_card_widget.details_id.connect (details_identity_cb);
248         id_card_widget.remove_id.connect (remove_identity_cb);
249         id_card_widget.send_id.connect (send_identity_cb);
250         id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
251         id_card_widget.expanded.connect (fill_details);
252     }
253
254     private void add_identity (AddIdentityDialog dialog)
255     {
256         var id_card = get_id_card_data (dialog);
257
258         this.identities_manager.id_card_list.prepend (id_card);
259         this.identities_manager.store_id_cards ();
260         this.identities_manager.store_gss_eap_id_file (id_card);
261
262         add_id_card_data (id_card);
263         add_id_card_widget (id_card);
264     }
265
266     private void add_identity_cb ()
267     {
268         var dialog = new AddIdentityDialog ();
269         var result = dialog.run ();
270
271         switch (result) {
272         case ResponseType.OK:
273             add_identity (dialog);
274             break;
275         default:
276             break;
277         }
278         dialog.destroy ();
279     }
280
281     private void remove_id_card_widget (IdCardWidget id_card_widget)
282     {
283         remove_id_card_data (id_card_widget.id_card);
284
285         this.custom_vbox.remove_id_card_widget (id_card_widget);
286     }
287
288     private void remove_identity (IdCardWidget id_card_widget)
289     {
290         var id_card = id_card_widget.id_card;
291
292         this.identities_manager.id_card_list.remove (id_card);
293         this.identities_manager.store_id_cards ();
294         this.identities_manager.store_gss_eap_id_file (null);
295
296         remove_id_card_widget (id_card_widget);
297     }
298
299     private void redraw_id_card_widgets ()
300     {
301         TreeIter iter;
302         IdCard id_card;
303
304         var children = this.custom_vbox.get_children ();
305         foreach (var id_card_widget in children)
306             id_card_widget.destroy();
307
308         if (filter.get_iter_first (out iter))
309         {
310             do
311             {
312                 filter.get (iter,
313                             Columns.IDCARD_COL, out id_card);
314
315                 add_id_card_widget (id_card);
316             }
317             while (filter.iter_next (ref iter));
318         }
319     }
320
321     private void remove_identity_cb (IdCardWidget id_card_widget)
322     {
323         var id_card = id_card_widget.id_card;
324
325         var dialog = new MessageDialog (null,
326                                         DialogFlags.DESTROY_WITH_PARENT,
327                                         MessageType.INFO,
328                                         Gtk.ButtonsType.YES_NO,
329                                         _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
330         var result = dialog.run ();
331         switch (result) {
332         case ResponseType.YES:
333             remove_identity (id_card_widget);
334             break;
335         default:
336             break;
337         }
338         dialog.destroy ();
339     }
340
341     public void set_callback (owned SourceFunc callback)
342     {
343         this.callback = (owned) callback;
344     }
345
346     public void send_identity_cb (IdCardWidget id_card_widget)
347     {
348         this.selected_id_card_widget = id_card_widget;
349
350         if (id_card_widget.id_card.password == null)
351         {
352             var dialog = new AddPasswordDialog ();
353             var result = dialog.run ();
354
355             switch (result) {
356             case ResponseType.OK:
357                 this.hide ();
358                 this.callback ();
359                 break;
360             default:
361                 this.hide ();
362                 break;
363             }
364             dialog.destroy ();
365         }
366         else
367         {
368           this.hide ();
369           this.callback ();
370         }
371     }
372
373     private void label_make_bold (Label label)
374     {
375         var font_desc = new Pango.FontDescription ();
376
377         font_desc.set_weight (Pango.Weight.BOLD);
378
379         /* This will only affect the weight of the font, the rest is
380          * from the current state of the widget, which comes from the
381          * theme or user prefs, since the font desc only has the
382          * weight flag turned on.
383          */
384         label.modify_font (font_desc);
385     }
386
387     private void fill_services_vbox (IdCard id_card)
388     {
389         int i = 0;
390         var n_columns = id_card.services.length;
391
392         var services_table = new Table (n_columns, 2, false);
393         services_table.set_col_spacings (10);
394         services_table.set_row_spacings (10);
395         this.services_internal_vbox.add (services_table);
396
397         foreach (string service in id_card.services)
398         {
399             var label = new Label (service);
400             label.set_alignment (0, (float) 0.5);
401 #if VALA_0_12
402             var remove_button = new Button.from_stock (Stock.REMOVE);
403 #else
404             var remove_button = new Button.from_stock (STOCK_REMOVE);
405 #endif
406             services_table.attach_defaults (label, 0, 1, i, i+1);
407             services_table.attach_defaults (remove_button, 1, 2, i, i+1);
408             i++;
409         }
410         this.services_internal_vbox.show_all ();
411     }
412
413     private void on_about_action ()
414     {
415         string[] authors = {
416             "Javier Jardón <jjardon@codethink.co.uk>",
417             "Sam Thursfield <samthursfield@codethink.co.uk>",
418             null
419         };
420
421         const string copyright = "Copyright 2011 JANET";
422
423         const string license =
424 """
425 Copyright (c) 2011, JANET(UK)
426 All rights reserved.
427
428 Redistribution and use in source and binary forms, with or without
429 modification, are permitted provided that the following conditions
430 are met:
431
432 1. Redistributions of source code must retain the above copyright
433    notice, this list of conditions and the following disclaimer.
434
435 2. Redistributions in binary form must reproduce the above copyright
436    notice, this list of conditions and the following disclaimer in the
437    documentation and/or other materials provided with the distribution.
438
439 3. Neither the name of JANET(UK) nor the names of its contributors
440    may be used to endorse or promote products derived from this software
441    without specific prior written permission.
442
443 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"
444 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
445 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
446 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
447 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
448 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
449 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
450 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
451 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
452 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
453 SUCH DAMAGE.
454 """;
455
456         Gtk.show_about_dialog (this,
457             "comments", _("Moonshot project UI"),
458             "copyright", copyright,
459             "website", "http://www.project-moonshot.org/",
460             "license", license,
461             "website-label", _("Visit the Moonshot project web site"),
462             "authors", authors,
463             "translator-credits", _("translator-credits"),
464             null
465         );
466     }
467
468     private Gtk.ActionEntry[] create_actions() {
469         Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
470
471         Gtk.ActionEntry filemenu = { "FileMenuAction",
472                                      null,
473                                      N_("_File"),
474                                      null, null, null };
475         actions += filemenu;
476         Gtk.ActionEntry add = { "AddIdCardAction",
477 #if VALA_0_12
478                                 Stock.ADD,
479 #else
480                                 STOCK_ADD,
481 #endif
482                                 N_("Add ID Card"),
483                                 null,
484                                 N_("Add a new ID Card"),
485                                 add_identity_cb };
486         actions += add;
487         Gtk.ActionEntry quit = { "QuitAction",
488 #if VALA_0_12
489                                  Stock.QUIT,
490 #else
491                                  STOCK_QUIT,
492 #endif
493                                  N_("Quit"),
494                                  "<control>Q",
495                                  N_("Quit the application"),
496                                  Gtk.main_quit };
497         actions += quit;
498
499         Gtk.ActionEntry helpmenu = { "HelpMenuAction",
500                                      null,
501                                      N_("_Help"),
502                                      null, null, null };
503         actions += helpmenu;
504         Gtk.ActionEntry about = { "AboutAction",
505 #if VALA_0_12
506                                   Stock.ABOUT,
507 #else
508                                   STOCK_ABOUT,
509 #endif
510                                   N_("About"),
511                                   null,
512                                   N_("About this application"),
513                                   on_about_action };
514         actions += about;
515
516         return actions;
517     }
518
519
520     private void create_ui_manager ()
521     {
522         Gtk.ActionGroup action_group = new Gtk.ActionGroup ("GeneralActionGroup");
523         action_group.add_actions (create_actions (), this);
524         ui_manager.insert_action_group (action_group, 0);
525         try
526         {
527             ui_manager.add_ui_from_string (layout, -1);
528         }
529         catch (Error e)
530         {
531             stderr.printf ("%s\n", e.message);
532         }
533         ui_manager.ensure_update ();
534     }
535
536     private void build_ui()
537     {
538         create_ui_manager ();
539
540         this.search_entry = new Entry();
541
542         set_atk_name_description (search_entry, _("Search entry"), _("Search for a specific ID Card"));
543         this.search_entry.set_icon_from_pixbuf (EntryIconPosition.PRIMARY,
544                                                 find_icon_sized ("edit-find-symbolic", Gtk.IconSize.MENU));
545         this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY,
546                                                  _("Search identity or service"));
547         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false);
548
549         this.search_entry.set_icon_from_pixbuf (EntryIconPosition.SECONDARY,
550                                                 find_icon_sized ("edit-clear-symbolic", Gtk.IconSize.MENU));
551         this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY,
552                                                  _("Clear the current search"));
553         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false);
554
555
556         this.search_entry.icon_press.connect (search_entry_icon_press_cb);
557         this.search_entry.notify["text"].connect (search_entry_text_changed_cb);
558         this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
559
560         this.custom_vbox = new CustomVBox (false, 6);
561
562         var viewport = new Viewport (null, null);
563         viewport.set_border_width (6);
564         viewport.set_shadow_type (ShadowType.NONE);
565         viewport.add (custom_vbox);
566         var scroll = new ScrolledWindow (null, null);
567         scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
568         scroll.set_shadow_type (ShadowType.IN);
569         scroll.add_with_viewport (viewport);
570
571         var vbox_left = new VBox (false, 0);
572         vbox_left.pack_start (search_entry, false, false, 6);
573         vbox_left.pack_start (scroll, true, true, 0);
574         vbox_left.set_size_request (WINDOW_WIDTH, 0);
575
576         var login_vbox_title = new Label (_("Login: "));
577         label_make_bold (login_vbox_title);
578         login_vbox_title.set_alignment (0, (float) 0.5);
579         var username_label = new Label (_("Username:"));
580         username_label.set_alignment (1, (float) 0.5);
581         this.username_entry = new Entry ();
582         var password_label = new Label (_("Password:"));
583         password_label.set_alignment (1, (float) 0.5);
584         this.password_entry = new Entry ();
585         password_entry.set_invisible_char ('*');
586         password_entry.set_visibility (false);
587         var remember_checkbutton = new CheckButton.with_label (_("Remember password"));
588         var login_table = new Table (3, 3, false);
589         login_table.set_col_spacings (10);
590         login_table.set_row_spacings (10);
591         login_table.attach_defaults (username_label, 0, 1, 0, 1);
592         login_table.attach_defaults (username_entry, 1, 2, 0, 1);
593         login_table.attach_defaults (password_label, 0, 1, 1, 2);
594         login_table.attach_defaults (password_entry, 1, 2, 1, 2);
595         login_table.attach_defaults (remember_checkbutton,  1, 2, 2, 3);
596         var login_vbox_alignment = new Alignment (0, 0, 0, 0);
597         login_vbox_alignment.set_padding (0, 0, 12, 0);
598         login_vbox_alignment.add (login_table);
599         var login_vbox = new VBox (false, 6);
600         login_vbox.pack_start (login_vbox_title, false, true, 0);
601         login_vbox.pack_start (login_vbox_alignment, false, true, 0);
602
603         var services_vbox_title = new Label (_("Services:"));
604         label_make_bold (services_vbox_title);
605         services_vbox_title.set_alignment (0, (float) 0.5);
606         var services_vbox_alignment = new Alignment (0, 0, 0, 0);
607         services_vbox_alignment.set_padding (0, 0, 12, 0);
608         this.services_internal_vbox = new VBox (true, 6);
609         services_vbox_alignment.add (services_internal_vbox);
610         var services_vbox = new VBox (false, 6);
611         services_vbox.pack_start (services_vbox_title, false, true, 0);
612         services_vbox.pack_start (services_vbox_alignment, false, true, 0);
613
614         this.vbox_rigth = new VBox (false, 18);
615         vbox_rigth.pack_start (login_vbox, false, true, 0);
616         vbox_rigth.pack_start (services_vbox, false, true, 0);
617
618         var hbox = new HBox (false, 12);
619         hbox.pack_start (vbox_left, false, false, 0);
620         hbox.pack_start (vbox_rigth, false, false, 0);
621
622         var main_vbox = new VBox (false, 0);
623         main_vbox.set_border_width (12);
624         var menubar = this.ui_manager.get_widget ("/MenuBar");
625         main_vbox.pack_start (menubar, false, false, 0);
626         main_vbox.pack_start (hbox, true, true, 0);
627         add (main_vbox);
628
629         main_vbox.show_all();
630         this.vbox_rigth.hide ();
631     }
632
633     private void set_atk_name_description (Widget widget, string name, string description)
634     {
635        var atk_widget = widget.get_accessible ();
636
637        atk_widget.set_name (name);
638        atk_widget.set_description (description);
639     }
640
641     private void connect_signals()
642     {
643         this.destroy.connect (Gtk.main_quit);
644     }
645
646     private void init_ipc_server ()
647     {
648 #if IPC_MSRPC
649         /* Errors will currently be sent via g_log - ie. to an
650          * obtrusive message box, on Windows
651          */
652         this.ipc_server = MoonshotServer.get_instance ();
653         MoonshotServer.start (this);
654 #else
655         try {
656             var conn = DBus.Bus.get (DBus.BusType.SESSION);
657             dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus",
658                                                        "/org/freedesktop/DBus",
659                                                        "org.freedesktop.DBus");
660
661             // try to register service in session bus
662             uint reply = bus.request_name ("org.janet.Moonshot", (uint) 0);
663             assert (reply == DBus.RequestNameReply.PRIMARY_OWNER);
664
665             this.ipc_server = new MoonshotServer (this);
666             conn.register_object ("/org/janet/moonshot", ipc_server);
667
668         }
669         catch (DBus.Error e)
670         {
671             stderr.printf ("%s\n", e.message);
672         }
673 #endif
674     }
675
676     public static int main(string[] args)
677     {
678         Gtk.init(ref args);
679
680 #if OS_WIN32
681         // Force specific theme settings on Windows without requiring a gtkrc file
682         Gtk.Settings settings = Gtk.Settings.get_default ();
683         settings.set_string_property ("gtk-theme-name", "ms-windows", "moonshot");
684         settings.set_long_property ("gtk-menu-images", 0, "moonshot");
685 #endif
686
687         Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
688         Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
689         Intl.textdomain (Config.GETTEXT_PACKAGE);
690
691         var window = new MainWindow();
692         window.show ();
693
694         Gtk.main();
695
696         return 0;
697     }
698 }