moonshot-window: search filter works
[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 Entry search_entry;
9     private VBox vbox_rigth;
10     private CustomVBox custom_vbox;
11
12     private Entry username_entry;
13     private Entry password_entry;
14
15     private ListStore listmodel;
16     private TreeModelFilter filter;
17
18     private IdentitiesManager identities_manager;
19
20     private enum Columns
21     {
22         IDCARD_COL,
23         LOGO_COL,
24         ISSUER_COL,
25         USERNAME_COL,
26         PASSWORD_COL,
27         N_COLUMNS
28     }
29
30     public MainWindow()
31     {
32         this.title = "Moonshoot";
33         this.position = WindowPosition.CENTER;
34         set_default_size (WINDOW_WIDTH, WINDOW_HEIGHT);
35
36         build_ui();
37         setup_identities_list();
38         load_id_cards();
39         connect_signals();
40     }
41
42     private bool visible_func (TreeModel model, TreeIter iter)
43     {
44         string issuer;
45         string search_text;
46         string issuer_casefold;
47         string search_text_casefold;
48
49         model.get (iter,
50                    Columns.ISSUER_COL, out issuer);
51         search_text = this.search_entry.get_text ();
52
53         if (issuer == null || search_text == null)
54             return false;
55
56         issuer_casefold = issuer.casefold ();
57         search_text_casefold = search_text.casefold ();
58
59         if (issuer_casefold.contains (search_text_casefold))
60             return true;
61
62         return false;
63     }
64
65     private void setup_identities_list ()
66     {
67        this.listmodel = new ListStore (Columns.N_COLUMNS, typeof (IdCard),
68                                                           typeof (Gdk.Pixbuf),
69                                                           typeof (string),
70                                                           typeof (string),
71                                                           typeof (string));
72       this.filter = new TreeModelFilter (listmodel, null);
73
74       filter.set_visible_func (visible_func);
75     }
76
77     private void search_entry_icon_press_cb (EntryIconPosition pos, Gdk.Event event)
78     {
79         if (pos == EntryIconPosition.PRIMARY)
80         {
81             print ("Search entry icon pressed\n");
82         }
83         else
84         {
85             this.search_entry.set_text ("");
86         }
87     }
88
89     private void search_entry_text_changed_cb ()
90     {
91         this.filter.refilter ();
92         redraw_id_card_widgets ();
93
94         var has_text = this.search_entry.get_text_length () > 0;
95         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, has_text);
96         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, has_text);
97
98         this.vbox_rigth.set_visible (false);
99         this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
100     }
101
102     private bool search_entry_key_press_event_cb (Gdk.EventKey e)
103     {
104         if(Gdk.keyval_name(e.keyval) == "Escape")
105            this.search_entry.set_text("");
106
107         // Continue processing this event, since the
108         // text entry functionality needs to see it too.
109         return false;
110     }
111
112     private void load_id_cards ()
113     {
114         this.identities_manager = new IdentitiesManager ();
115
116         foreach (IdCard id_card in identities_manager.id_card_list)
117         {
118             add_id_card_data (id_card);
119             add_id_card_widget (id_card);
120         }
121     }
122
123     private void fill_details (IdCardWidget id_card_widget)
124     {
125        var id_card = id_card_widget.id_card;
126        this.username_entry.set_text (id_card.username);
127        this.password_entry.set_text (id_card.password);
128     }
129
130     private void show_details (IdCard id_card)
131     {
132        this.vbox_rigth.set_visible (!vbox_rigth.get_visible ());
133
134        if (this.vbox_rigth.get_visible () == false)
135        {
136            this.resize (WINDOW_WIDTH, WINDOW_HEIGHT);
137        }
138     }
139
140     private void details_identity_cb (IdCardWidget id_card_widget)
141     {
142        fill_details (id_card_widget);
143        show_details (id_card_widget.id_card);
144     }
145
146     private IdCard get_id_card_data (AddIdentityDialog dialog)
147     {
148         var id_card = new IdCard ();
149
150         id_card.issuer = dialog.issuer;
151         id_card.username = dialog.username;
152         id_card.password = dialog.password;
153
154         var icon_theme = IconTheme.get_default ();
155         try
156         {
157             id_card.pixbuf = icon_theme.load_icon ("avatar-default",
158                                                    48,
159                                                    IconLookupFlags.FORCE_SIZE);
160         }
161         catch (Error e)
162         {
163             id_card.pixbuf = null;
164             stdout.printf("Error: %s\n", e.message);
165         }
166
167         id_card.services = {"email","jabber","irc"};
168
169         return id_card;
170     }
171
172     private void add_id_card_data (IdCard id_card)
173     {
174         TreeIter iter;
175
176         this.listmodel.append (out iter);
177         listmodel.set (iter,
178                        Columns.IDCARD_COL, id_card,
179                        Columns.LOGO_COL, id_card.pixbuf,
180                        Columns.ISSUER_COL, id_card.issuer,
181                        Columns.USERNAME_COL, id_card.username,
182                        Columns.PASSWORD_COL, id_card.password);
183     }
184
185     private void remove_id_card_data (IdCard id_card)
186     {
187         TreeIter iter;
188         string issuer;
189
190         if (listmodel.get_iter_first (out iter))
191         {
192             do
193             {
194                 listmodel.get (iter,
195                                Columns.ISSUER_COL, out issuer);
196
197                 if (id_card.issuer == issuer)
198                 {
199                     listmodel.remove (iter);
200                     break;
201                 }
202             }
203             while (listmodel.iter_next (ref iter));
204         }
205     }
206
207     private void add_id_card_widget (IdCard id_card)
208     {
209         var id_card_widget = new IdCardWidget (id_card);
210
211         this.custom_vbox.add_id_card_widget (id_card_widget);
212
213         id_card_widget.details_id.connect (details_identity_cb);
214         id_card_widget.remove_id.connect (remove_identity_cb);
215         id_card_widget.expanded.connect (this.custom_vbox.receive_expanded_event);
216         id_card_widget.expanded.connect (fill_details);
217     }
218
219     private void add_identity (AddIdentityDialog dialog)
220     {
221         var id_card = get_id_card_data (dialog);
222
223         this.identities_manager.id_card_list.prepend (id_card);
224         this.identities_manager.store_id_cards ();
225
226         add_id_card_data (id_card);
227         add_id_card_widget (id_card);
228     }
229
230     private void add_identity_cb ()
231     {
232         var dialog = new AddIdentityDialog ();
233         var result = dialog.run ();
234
235         switch (result) {
236         case ResponseType.OK:
237             add_identity (dialog);
238             break;
239         default:
240             break;
241         }
242         dialog.destroy ();
243     }
244
245     private void remove_id_card_widget (IdCardWidget id_card_widget)
246     {
247         remove_id_card_data (id_card_widget.id_card);
248
249         this.custom_vbox.remove_id_card_widget (id_card_widget);
250     }
251
252     private void remove_identity (IdCardWidget id_card_widget)
253     {
254         var id_card = id_card_widget.id_card;
255
256         this.identities_manager.id_card_list.remove (id_card);
257         this.identities_manager.store_id_cards ();
258
259         remove_id_card_widget (id_card_widget);
260     }
261
262     private void redraw_id_card_widgets ()
263     {
264         TreeIter iter;
265         IdCard id_card;
266
267         var children = this.custom_vbox.get_children ();
268         foreach (var id_card_widget in children)
269             id_card_widget.destroy();
270
271         if (filter.get_iter_first (out iter))
272         {
273             do
274             {
275                 filter.get (iter,
276                             Columns.IDCARD_COL, out id_card);
277
278                 add_id_card_widget (id_card);
279             }
280             while (filter.iter_next (ref iter));
281         }
282     }
283
284     private void remove_identity_cb (IdCardWidget id_card_widget)
285     {
286         var id_card = id_card_widget.id_card;
287
288         var dialog = new MessageDialog (null,
289                                         DialogFlags.DESTROY_WITH_PARENT,
290                                         MessageType.INFO,
291                                         Gtk.ButtonsType.YES_NO,
292                                         _("Are you sure you want to delete %s ID Card?"), id_card.issuer);
293         var result = dialog.run ();
294         switch (result) {
295         case ResponseType.YES:
296             remove_identity (id_card_widget);
297             break;
298         default:
299             break;
300         }
301         dialog.destroy ();
302     }
303
304     private void label_make_bold (Label label)
305     {
306         var font_desc = new Pango.FontDescription ();
307
308         font_desc.set_weight (Pango.Weight.BOLD);
309
310         /* This will only affect the weight of the font, the rest is
311          * from the current state of the widget, which comes from the
312          * theme or user prefs, since the font desc only has the
313          * weight flag turned on.
314          */
315         label.modify_font (font_desc);
316     }
317
318     private void build_ui()
319     {
320         this.search_entry = new Entry();
321
322         this.search_entry.set_icon_from_icon_name (EntryIconPosition.PRIMARY,
323                                                    "edit-find-symbolic");
324         this.search_entry.set_icon_sensitive (EntryIconPosition.PRIMARY, false);
325         this.search_entry.set_icon_tooltip_text (EntryIconPosition.PRIMARY,
326                                                  _("Search identity or service"));
327
328         this.search_entry.set_icon_from_icon_name (EntryIconPosition.SECONDARY,
329                                                    "edit-clear-symbolic");
330         this.search_entry.set_icon_sensitive (EntryIconPosition.SECONDARY, false);
331         this.search_entry.set_icon_tooltip_text (EntryIconPosition.SECONDARY,
332                                                  _("Clear the current search"));
333
334         this.search_entry.icon_press.connect (search_entry_icon_press_cb);
335         this.search_entry.notify["text"].connect (search_entry_text_changed_cb);
336         this.search_entry.key_press_event.connect(search_entry_key_press_event_cb);
337
338         this.custom_vbox = new CustomVBox (false, 6);
339
340         var viewport = new Viewport (null, null);
341         viewport.set_border_width (6);
342         viewport.set_shadow_type (ShadowType.NONE);
343         viewport.add (custom_vbox);
344         var scroll = new ScrolledWindow (null, null);
345         scroll.set_policy (PolicyType.NEVER, PolicyType.AUTOMATIC);
346         scroll.set_shadow_type (ShadowType.IN);
347         scroll.add_with_viewport (viewport);
348
349         var button_add = new ToolButton (null, null);
350         button_add.set_icon_name ("list-add-symbolic");
351         button_add.clicked.connect (add_identity_cb);
352         var button_toolbar = new Toolbar ();
353         button_toolbar.insert (button_add, 0);
354
355         var vbox_left = new VBox (false, 0);
356         vbox_left.pack_start (search_entry, false, false, 6);
357         vbox_left.pack_start (scroll, true, true, 0);
358         vbox_left.pack_start (button_toolbar, false, false, 0);
359         vbox_left.set_size_request (WINDOW_WIDTH, 0);
360
361         var login_vbox_title = new Label (_("Login: "));
362         label_make_bold (login_vbox_title);
363         login_vbox_title.set_alignment (0, (float) 0.5);
364         var username_label = new Label (_("Username:"));
365         username_label.set_alignment (1, (float) 0.5);
366         this.username_entry = new Entry ();
367         var password_label = new Label (_("Password:"));
368         password_label.set_alignment (1, (float) 0.5);
369         this.password_entry = new Entry ();
370         password_entry.set_invisible_char ('*');
371         password_entry.set_visibility (false);
372         var remember_checkbutton = new CheckButton.with_label (_("Remember password"));
373         var login_table = new Table (3, 3, false);
374         login_table.set_col_spacings (10);
375         login_table.set_row_spacings (10);
376         login_table.attach_defaults (username_label, 0, 1, 0, 1);
377         login_table.attach_defaults (username_entry, 1, 2, 0, 1);
378         login_table.attach_defaults (password_label, 0, 1, 1, 2);
379         login_table.attach_defaults (password_entry, 1, 2, 1, 2);
380         login_table.attach_defaults (remember_checkbutton,  1, 2, 2, 3);
381         var login_vbox_alignment = new Alignment (0, 0, 0, 0);
382         login_vbox_alignment.set_padding (0, 0, 12, 0);
383         login_vbox_alignment.add (login_table);
384         var login_vbox = new VBox (false, 6);
385         login_vbox.pack_start (login_vbox_title, false, true, 0);
386         login_vbox.pack_start (login_vbox_alignment, false, true, 0);
387
388         var services_vbox_title = new Label (_("Services:"));
389         label_make_bold (services_vbox_title);
390         services_vbox_title.set_alignment (0, (float) 0.5);
391         var email_label = new Label (_("Email"));
392         var email_remove_button = new Button.from_stock (Stock.REMOVE);
393         var im_label = new Label (_("IM"));
394         var im_remove_button = new Button.from_stock (Stock.REMOVE);
395         var services_table = new Table (2, 2, false);
396         services_table.set_col_spacings (10);
397         services_table.set_row_spacings (10);
398         services_table.attach_defaults (email_label, 0, 1, 0, 1);
399         services_table.attach_defaults (email_remove_button, 1, 2, 0, 1);
400         services_table.attach_defaults (im_label, 0, 1, 1, 2);
401         services_table.attach_defaults (im_remove_button, 1, 2, 1, 2);
402         var services_vbox_alignment = new Alignment (0, 0, 0, 0);
403         services_vbox_alignment.set_padding (0, 0, 12, 0);
404         services_vbox_alignment.add (services_table);
405         var services_vbox = new VBox (false, 6);
406         services_vbox.pack_start (services_vbox_title, false, true, 0);
407         services_vbox.pack_start (services_vbox_alignment, false, true, 0);
408
409         this.vbox_rigth = new VBox (false, 18);
410         vbox_rigth.pack_start (login_vbox, false, true, 0);
411         vbox_rigth.pack_start (services_vbox, false, true, 0);
412
413         var hbox = new HBox (false, 12);
414         hbox.pack_start (vbox_left, false, false, 0);
415         hbox.pack_start (vbox_rigth, false, false, 0);
416
417         var main_vbox = new VBox (false, 12);
418         main_vbox.pack_start (hbox, true, true, 0);
419         main_vbox.set_border_width (12);
420         add (main_vbox);
421
422         main_vbox.show_all();
423         this.vbox_rigth.hide ();
424     }
425
426     private void connect_signals()
427     {
428         this.destroy.connect (Gtk.main_quit);
429     }
430
431     public static int main(string[] args)
432     {
433         Gtk.init(ref args);
434
435         Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
436         Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
437         Intl.textdomain (Config.GETTEXT_PACKAGE);
438
439         var window = new MainWindow();
440         window.show ();
441
442         Gtk.main();
443
444         return 0;
445     }
446 }