Minor formatting fixes
[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 Gdk.Color white = make_color(65535, 65535, 65535);
46     private static Gdk.Color selected_color = make_color(0xd9 << 8, 0xf7 << 8, 65535);
47     private static Gdk.Color alt_color = make_color(0xf2 << 8, 0xf2 << 8, 0xf2 << 8);
48
49     private static Gdk.Color make_color(uint16 red, uint16 green, uint16 blue)
50     {
51         Gdk.Color color = Gdk.Color();
52         color.red = red;
53         color.green = green;
54         color.blue = blue;
55
56         return color;
57     }
58
59     private static MoonshotLogger logger = get_logger("IdentityDialog");
60
61     static const string displayname_labeltext = _("Display Name");
62     static const string issuer_labeltext = _("Realm");
63     static const string username_labeltext = _("Username");
64     static const string password_labeltext = _("Password");
65
66     private IdentityManagerView parent;
67     private Entry displayname_entry;
68     private Label displayname_label;
69     private Entry issuer_entry;
70     private Label issuer_label;
71     private Entry username_entry;
72     private Label username_label;
73     private Entry password_entry;
74     private Label password_label;
75     private CheckButton remember_checkbutton;
76     private Label message_label;
77     public bool complete;
78     private IdCard card;
79     
80     private Label selected_item = null;
81
82     public string display_name {
83         get { return displayname_entry.get_text(); }
84     }
85
86     public string issuer {
87         get { return issuer_entry.get_text(); }
88     }
89
90     public string username {
91         get { return username_entry.get_text(); }
92     }
93
94     public string password {
95         get { return password_entry.get_text(); }
96     }
97
98     public bool store_password {
99         get { return remember_checkbutton.active; }
100     }
101
102     internal string[] get_services()
103     {
104         return card.services;
105     }
106
107     public IdentityDialog(IdentityManagerView parent)
108     {
109         this.with_idcard(null, _("Add ID Card"), parent);
110     }
111
112     public IdentityDialog.with_idcard(IdCard? a_card, string title, IdentityManagerView parent)
113     {
114         bool is_new_card = false;
115         if (a_card == null)
116         {
117             is_new_card = true;
118         }
119
120         card = a_card ?? new IdCard();
121         this.set_title(title);
122         this.set_modal(true);
123         this.set_transient_for(parent);
124         this.parent = parent;
125
126         this.add_buttons(_("OK"), ResponseType.OK, CANCEL, ResponseType.CANCEL);
127         var content_area = this.get_content_area();
128         ((Box) content_area).set_spacing(12);
129         
130         displayname_label = new Label(@"$displayname_labeltext:");
131         displayname_label.set_alignment(1,(float) 0.5);
132         displayname_entry = new Entry();
133         displayname_entry.set_text(card.display_name);
134
135         issuer_label = new Label(@"$issuer_labeltext:");
136         issuer_label.set_alignment(1,(float) 0.5);
137         this.issuer_entry = new Entry();
138         this.issuer_entry.set_text(card.issuer);
139
140         username_label = new Label(@"$username_labeltext:");
141         username_label.set_alignment(1,(float) 0.5);
142         this.username_entry = new Entry();
143         this.username_entry.set_text(card.username);
144
145         password_label = new Label(@"$password_labeltext:");
146         password_label.set_alignment(1,(float) 0.5);
147         this.password_entry = new Entry();
148         password_entry.set_invisible_char('*');
149         password_entry.set_visibility(false);
150         password_entry.set_text(card.password);
151
152         this.remember_checkbutton = new CheckButton.with_label(_("Remember password"));
153         this.message_label = new Label("");
154         message_label.set_visible(false);
155
156         set_atk_relation(displayname_label, displayname_entry, Atk.RelationType.LABEL_FOR);
157         set_atk_relation(issuer_label, issuer_entry, Atk.RelationType.LABEL_FOR);
158         set_atk_relation(username_label, username_entry, Atk.RelationType.LABEL_FOR);
159         set_atk_relation(password_entry, password_entry, Atk.RelationType.LABEL_FOR);
160
161         var table = new Table(6, 2, false);
162         table.set_col_spacings(10);
163         table.set_row_spacings(10);
164         
165         table.attach_defaults(message_label, 0, 2, 0, 1);
166         table.attach_defaults(displayname_label, 0, 1, 1, 2);
167         table.attach_defaults(displayname_entry, 1, 2, 1, 2);
168         table.attach_defaults(issuer_label, 0, 1, 2, 3);
169         table.attach_defaults(issuer_entry, 1, 2, 2, 3);
170         table.attach_defaults(username_label, 0, 1, 3, 4);
171         table.attach_defaults(username_entry, 1, 2, 3, 4);
172         table.attach_defaults(password_label, 0, 1, 4, 5);
173         table.attach_defaults(password_entry, 1, 2, 4, 5);
174         table.attach_defaults(remember_checkbutton,  1, 2, 5, 6);
175
176         this.response.connect(on_response);
177         var vbox = new VBox(false, 0);
178         vbox.set_border_width(6);
179         vbox.pack_start(table, false, false, 0);
180
181         if (!is_new_card)
182         {
183             var services_vbox = make_services_vbox();
184             vbox.pack_start(services_vbox);
185         }
186
187         ((Container) content_area).add(vbox);
188
189         this.set_border_width(6);
190         this.set_resizable(false);
191         this.modify_bg(StateType.NORMAL, white);
192         this.show_all();
193     }
194
195     private static string update_preamble(string preamble)
196     {
197         if (preamble == "")
198             return _("Missing required field: ");
199         return _("Missing required fields: ");
200     }
201
202     private static string update_message(string old_message, string new_item)
203     {
204         string message;
205         if (old_message == "")
206             message = new_item;
207         else
208             message = old_message + ", " + new_item;
209         return message;
210     }
211
212     private static void check_field(string field, Label label, string fieldname, ref string preamble, ref string message)
213     {
214         if (field != "") {
215             label.set_markup(@"$fieldname:");
216             return;
217         }
218         label.set_markup(@"<span foreground=\"red\">$fieldname:</span>");
219         preamble = update_preamble(preamble);
220         message = update_message(message, fieldname);
221     }
222
223     private bool check_fields()
224     {
225         string preamble = "";
226         string message = "";
227         string password_test = store_password ? password : "not required";
228         check_field(display_name, displayname_label, displayname_labeltext, ref preamble, ref message);
229         check_field(issuer, issuer_label, issuer_labeltext, ref preamble, ref message);
230         check_field(username, username_label, username_labeltext, ref preamble, ref message);
231         check_field(password_test, password_label, password_labeltext, ref preamble, ref message);
232         if (message != "") {
233             message_label.set_visible(true);
234             message_label.set_markup(@"<span foreground=\"red\">$preamble$message</span>");
235             return false;
236         }
237         return true;
238     }
239
240     private void on_response(Dialog source, int response_id)
241     {
242         switch (response_id) {
243         case ResponseType.OK:
244             complete = check_fields();
245             break;
246         case ResponseType.CANCEL:
247             complete = true;
248             break;
249         }
250     }
251
252     private void set_atk_relation(Widget widget, Widget target_widget, Atk.RelationType relationship)
253     {
254         var atk_widget = widget.get_accessible();
255         var atk_target_widget = target_widget.get_accessible();
256
257         atk_widget.add_relationship(relationship, atk_target_widget);
258     }
259
260     private static void label_make_bold(Label label)
261     {
262         var font_desc = new Pango.FontDescription();
263
264         font_desc.set_weight(Pango.Weight.BOLD);
265
266         /* This will only affect the weight of the font. The rest is
267          * from the current state of the widget, which comes from the
268          * theme or user prefs, since the font desc only has the
269          * weight flag turned on.
270          */
271         label.modify_font(font_desc);
272     }
273
274     private VBox make_services_vbox()
275     {
276         logger.trace("make_services_vbox");
277
278         var services_vbox_alignment = new Alignment(0, 0, 0, 1);
279         services_vbox_alignment.set_padding(6, 6, 6, 6);
280         var services_vscroll = new ScrolledWindow(null, null);
281         services_vscroll.set_policy(PolicyType.NEVER, PolicyType.AUTOMATIC);
282         services_vscroll.set_shadow_type(ShadowType.IN);
283         services_vscroll.set_size_request(0, 60);
284         services_vscroll.add_with_viewport(services_vbox_alignment);
285
286 #if VALA_0_12
287         var remove_button = new Button.from_stock(Stock.REMOVE);
288 #else
289         var remove_button = new Button.from_stock(STOCK_REMOVE);
290 #endif
291         remove_button.set_sensitive(false);
292
293
294         var services_table = new Table(card.services.length, 1, false);
295         services_table.set_row_spacings(5);
296
297         var table_button_hbox = new HBox(false, 6);
298         table_button_hbox.pack_start(services_vscroll, true, true, 6);
299
300         // Hack to prevent the button from growing vertically
301         VBox fixed_height = new VBox(false, 0);
302         fixed_height.pack_start(remove_button, false, false, 0);
303         table_button_hbox.pack_start(fixed_height, false, false, 6);
304         services_vbox_alignment.add(services_table);        
305
306         var services_vbox_title = new Label(_("Services:"));
307         label_make_bold(services_vbox_title);
308         services_vbox_title.set_alignment(0, (float) 0.5);
309         
310         var services_vbox = new VBox(false, 6);
311         services_vbox.pack_start(services_vbox_title, false, false, 6);
312         services_vbox.pack_start(table_button_hbox, true, true, 6);
313
314         int i = 0;
315         foreach (string service in card.services)
316         {
317             var label = new Label(service);
318             label.set_alignment(0, (float) 0);
319
320             EventBox event_box = new EventBox();
321             event_box.add(label);
322             event_box.button_press_event.connect(() =>
323                 {
324                     var state = label.get_state();
325                     if (selected_item == label)
326                     {
327                         // Deselect
328                         selected_item.modify_bg(state, white);
329                         selected_item = null;
330                         remove_button.set_sensitive(false);
331                     }
332                     else
333                     {
334                         if (selected_item != null)
335                         {
336                             // Deselect
337                             selected_item.modify_bg(state, white);
338                             selected_item = null;
339                         }
340
341                         // Select
342                         selected_item = label;
343                         selected_item.modify_bg(state, selected_color);
344                         remove_button.set_sensitive(true);
345                     }
346                     return false;
347                 });
348
349             services_table.attach_defaults(event_box, 0, 1, i, i+1);
350             i++;
351         }
352
353         remove_button.clicked.connect((remove_button) =>
354             {
355                 var dialog = new Gtk.MessageDialog(this,
356                                                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
357                                                    Gtk.MessageType.QUESTION,
358                                                    Gtk.ButtonsType.YES_NO,
359                                                    _("You are about to remove the service '%s'. Are you sure you want to do this?"),
360                                                    selected_item.label);
361                 var ret = dialog.run();
362                 dialog.destroy();
363               
364                 if (ret == Gtk.ResponseType.YES)
365                 {
366                     if (card != null) {
367                         SList<string> services = new SList<string>();
368                 
369                         foreach (string srv in card.services)
370                         {
371                             if (srv != selected_item.label)
372                                 services.append(srv);
373                         }
374                 
375                         card.services = new string[services.length()];
376                         for (int j = 0; j < card.services.length; j++)
377                         {
378                             card.services[j] = services.nth_data(j);
379                         }
380
381                         services_table.remove(selected_item.parent);
382                         selected_item = null;
383                         remove_button.set_sensitive(false);
384                     }
385                 }
386               
387             });
388
389         return services_vbox;
390     }
391
392
393 }