Dead code removal; also fixed Send button (at least a little; not tested yet.)
[moonshot-ui.git] / src / moonshot-identity-dialog.vala
1 /*
2  * Copyright (c) 2016, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31 */
32 using Gtk;
33
34
35 // Defined here as workaround for emacs vala-mode indentation failure.
36 #if VALA_0_12
37 static const string CANCEL = Stock.CANCEL;
38 #else
39 static const string CANCEL = STOCK_CANCEL;
40 #endif
41
42
43 class IdentityDialog : Dialog
44 {
45     private static MoonshotLogger logger = get_logger("IdentityDialog");
46
47     static const string displayname_labeltext = _("Display Name");
48     static const string issuer_labeltext = _("Issuer");
49     static const string username_labeltext = _("Username");
50     static const string password_labeltext = _("Password");
51
52     private IdentityManagerView parent;
53     private Entry displayname_entry;
54     private Label displayname_label;
55     private Entry issuer_entry;
56     private Label issuer_label;
57     private Entry username_entry;
58     private Label username_label;
59     private Entry password_entry;
60     private Label password_label;
61     private CheckButton remember_checkbutton;
62     private Label message_label;
63     public bool complete;
64     private IdCard card;
65     
66     private Label selected_item = null;
67
68     public string display_name {
69         get { return displayname_entry.get_text(); }
70     }
71
72     public string issuer {
73         get { return issuer_entry.get_text(); }
74     }
75
76     public string username {
77         get { return username_entry.get_text(); }
78     }
79
80     public string password {
81         get { return password_entry.get_text(); }
82     }
83
84     public bool store_password {
85         get { return remember_checkbutton.active; }
86     }
87
88     internal string[] get_services()
89     {
90         return card.services;
91     }
92
93     public IdentityDialog(IdentityManagerView parent)
94     {
95         this.with_idcard(null, _("Add ID Card"), parent);
96     }
97
98     public IdentityDialog.with_idcard(IdCard? a_card, string title, IdentityManagerView parent)
99     {
100         bool is_new_card = false;
101         if (a_card == null)
102         {
103             is_new_card = true;
104         }
105
106         card = a_card ?? new IdCard();
107         this.set_title(title);
108         this.set_modal(true);
109         this.set_transient_for(parent);
110         this.parent = parent;
111
112         this.add_buttons(_("OK"), ResponseType.OK, CANCEL, ResponseType.CANCEL);
113         var content_area = this.get_content_area();
114         ((Box) content_area).set_spacing(12);
115         
116         displayname_label = new Label(@"$displayname_labeltext:");
117         displayname_label.set_alignment(1,(float) 0.5);
118         displayname_entry = new Entry();
119         displayname_entry.set_text(card.display_name);
120
121         issuer_label = new Label(@"$issuer_labeltext:");
122         issuer_label.set_alignment(1,(float) 0.5);
123         this.issuer_entry = new Entry();
124         this.issuer_entry.set_text(card.issuer);
125
126         username_label = new Label(@"$username_labeltext:");
127         username_label.set_alignment(1,(float) 0.5);
128         this.username_entry = new Entry();
129         this.username_entry.set_text(card.username);
130
131         password_label = new Label(@"$password_labeltext:");
132         password_label.set_alignment(1,(float) 0.5);
133         this.password_entry = new Entry();
134         password_entry.set_invisible_char('*');
135         password_entry.set_visibility(false);
136         password_entry.set_text(card.password);
137
138         this.remember_checkbutton = new CheckButton.with_label(_("Remember password"));
139         this.message_label = new Label("");
140         message_label.set_visible(false);
141
142         set_atk_relation(displayname_label, displayname_entry, Atk.RelationType.LABEL_FOR);
143         set_atk_relation(issuer_label, issuer_entry, Atk.RelationType.LABEL_FOR);
144         set_atk_relation(username_label, username_entry, Atk.RelationType.LABEL_FOR);
145         set_atk_relation(password_entry, password_entry, Atk.RelationType.LABEL_FOR);
146
147         var table = new Table(6, 2, false);
148         table.set_col_spacings(10);
149         table.set_row_spacings(10);
150         
151         table.attach_defaults(message_label, 0, 2, 0, 1);
152         table.attach_defaults(displayname_label, 0, 1, 1, 2);
153         table.attach_defaults(displayname_entry, 1, 2, 1, 2);
154         table.attach_defaults(issuer_label, 0, 1, 2, 3);
155         table.attach_defaults(issuer_entry, 1, 2, 2, 3);
156         table.attach_defaults(username_label, 0, 1, 3, 4);
157         table.attach_defaults(username_entry, 1, 2, 3, 4);
158         table.attach_defaults(password_label, 0, 1, 4, 5);
159         table.attach_defaults(password_entry, 1, 2, 4, 5);
160         table.attach_defaults(remember_checkbutton,  1, 2, 5, 6);
161
162         this.response.connect(on_response);
163         var vbox = new VBox(false, 0);
164         vbox.set_border_width(6);
165         vbox.pack_start(table, false, false, 0);
166
167         if (!is_new_card)
168         {
169             var services_vbox = make_services_vbox();
170             vbox.pack_start(services_vbox);
171         }
172
173         ((Container) content_area).add(vbox);
174
175         this.set_border_width(6);
176         this.set_resizable(false);
177         this.show_all();
178     }
179
180     private static string update_preamble(string preamble)
181     {
182         if (preamble == "")
183             return _("Missing required field: ");
184         return _("Missing required fields: ");
185     }
186
187     private static string update_message(string old_message, string new_item)
188     {
189         string message;
190         if (old_message == "")
191             message = new_item;
192         else
193             message = old_message + ", " + new_item;
194         return message;
195     }
196
197     private static void check_field(string field, Label label, string fieldname, ref string preamble, ref string message)
198     {
199         if (field != "") {
200             label.set_markup(@"$fieldname:");
201             return;
202         }
203         label.set_markup(@"<span foreground=\"red\">$fieldname:</span>");
204         preamble = update_preamble(preamble);
205         message = update_message(message, fieldname);
206     }
207
208     private bool check_fields()
209     {
210         string preamble = "";
211         string message = "";
212         string password_test = store_password ? password : "not required";
213         check_field(display_name, displayname_label, displayname_labeltext, ref preamble, ref message);
214         check_field(issuer, issuer_label, issuer_labeltext, ref preamble, ref message);
215         check_field(username, username_label, username_labeltext, ref preamble, ref message);
216         check_field(password_test, password_label, password_labeltext, ref preamble, ref message);
217         if (message != "") {
218             message_label.set_visible(true);
219             message_label.set_markup(@"<span foreground=\"red\">$preamble$message</span>");
220             return false;
221         }
222         return true;
223     }
224
225     private void on_response(Dialog source, int response_id)
226     {
227         switch (response_id) {
228         case ResponseType.OK:
229             complete = check_fields();
230             break;
231         case ResponseType.CANCEL:
232             complete = true;
233             break;
234         }
235     }
236
237     private void set_atk_relation(Widget widget, Widget target_widget, Atk.RelationType relationship)
238     {
239         var atk_widget = widget.get_accessible();
240         var atk_target_widget = target_widget.get_accessible();
241
242         atk_widget.add_relationship(relationship, atk_target_widget);
243     }
244
245     private static void label_make_bold(Label label)
246     {
247         var font_desc = new Pango.FontDescription();
248
249         font_desc.set_weight(Pango.Weight.BOLD);
250
251         /* This will only affect the weight of the font. The rest is
252          * from the current state of the widget, which comes from the
253          * theme or user prefs, since the font desc only has the
254          * weight flag turned on.
255          */
256         label.modify_font(font_desc);
257     }
258
259     private VBox make_services_vbox()
260     {
261         logger.trace("make_services_vbox");
262
263         var services_vbox_alignment = new Alignment(0, 0, 0, 1);
264         services_vbox_alignment.set_padding(6, 6, 6, 6);
265         var services_vscroll = new ScrolledWindow(null, null);
266         services_vscroll.set_policy(PolicyType.NEVER, PolicyType.AUTOMATIC);
267         services_vscroll.set_shadow_type(ShadowType.IN);
268         services_vscroll.set_size_request(0, 60);
269         services_vscroll.add_with_viewport(services_vbox_alignment);
270
271 #if VALA_0_12
272         var remove_button = new Button.from_stock(Stock.REMOVE);
273 #else
274         var remove_button = new Button.from_stock(STOCK_REMOVE);
275 #endif
276         remove_button.set_sensitive(false);
277
278
279         var services_table = new Table(card.services.length, 1, false);
280         services_table.set_row_spacings(5);
281
282         var table_button_hbox = new HBox(false, 6);
283         table_button_hbox.pack_start(services_vscroll, true, true, 6);
284         table_button_hbox.pack_start(remove_button, false, false, 6);
285         services_vbox_alignment.add(services_table);        
286
287         var services_vbox_title = new Label(_("Services:"));
288         label_make_bold(services_vbox_title);
289         services_vbox_title.set_alignment(0, (float) 0.5);
290         
291         var services_vbox = new VBox(false, 6);
292         services_vbox.pack_start(services_vbox_title, false, false, 6);
293         services_vbox.pack_start(table_button_hbox, true, true, 6);
294
295
296         var selected_color = Gdk.Color();
297         selected_color.red = 0xd9 << 8;
298         selected_color.green = 0xf7 << 8;
299         selected_color.blue = 65535;
300
301         var unselected_color = Gdk.Color();
302         unselected_color.red = 65535;
303         unselected_color.green = 65535;
304         unselected_color.blue = 65535;
305
306         int i = 0;
307         foreach (string service in card.services)
308         {
309             var label = new Label(service);
310             label.set_alignment(0, (float) 0);
311
312             EventBox event_box = new EventBox();
313             event_box.add(label);
314             event_box.button_press_event.connect(() =>
315                 {
316                     var state = label.get_state();
317                     if (selected_item == label)
318                     {
319                         // Deselect
320                         selected_item.modify_bg(state, unselected_color);
321                         selected_item = null;
322                         remove_button.set_sensitive(false);
323                     }
324                     else
325                     {
326                         if (selected_item != null)
327                         {
328                             // Deselect
329                             selected_item.modify_bg(state, unselected_color);
330                             selected_item = null;
331                         }
332
333                         // Select
334                         selected_item = label;
335                         selected_item.modify_bg(state, selected_color);
336                         remove_button.set_sensitive(true);
337                     }
338                     return false;
339                 });
340
341             services_table.attach_defaults(event_box, 0, 1, i, i+1);
342             i++;
343         }
344
345         remove_button.clicked.connect((remove_button) =>
346             {
347                 var dialog = new Gtk.MessageDialog(this,
348                                                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
349                                                    Gtk.MessageType.QUESTION,
350                                                    Gtk.ButtonsType.YES_NO,
351                                                    _("You are about to remove the service '%s'. Are you sure you want to do this?"),
352                                                    selected_item.label);
353                 var ret = dialog.run();
354                 dialog.destroy();
355               
356                 if (ret == Gtk.ResponseType.YES)
357                 {
358                     if (card != null) {
359                         SList<string> services = new SList<string>();
360                 
361                         foreach (string srv in card.services)
362                         {
363                             if (srv != selected_item.label)
364                                 services.append(srv);
365                         }
366                 
367                         card.services = new string[services.length()];
368                         for (int j = 0; j < card.services.length; j++)
369                         {
370                             card.services[j] = services.nth_data(j);
371                         }
372
373                         services_table.remove(selected_item.parent);
374                         selected_item = null;
375                         remove_button.set_sensitive(false);
376                     }
377                 }
378               
379             });
380
381         return services_vbox;
382     }
383
384
385 }