1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include <glib.h>
26 #include <glib/gi18n.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <gtk/gtk.h>
29 
30 #include "inputdialog.h"
31 #include "manage_window.h"
32 #include "gtkutils.h"
33 #include "utils.h"
34 #include "combobox.h"
35 #include "prefs_common.h"
36 
37 
38 #define INPUT_DIALOG_WIDTH	420
39 
40 typedef enum
41 {
42 	INPUT_DIALOG_NORMAL,
43 	INPUT_DIALOG_INVISIBLE,
44 	INPUT_DIALOG_COMBO
45 } InputDialogType;
46 
47 static gboolean ack;
48 static gboolean fin;
49 
50 static InputDialogType type;
51 
52 static GtkWidget *dialog;
53 static GtkWidget *msg_title;
54 static GtkWidget *msg_label;
55 static GtkWidget *entry;
56 static GtkWidget *combo;
57 static GtkWidget *remember_checkbtn;
58 static GtkWidget *ok_button;
59 static GtkWidget *icon_q, *icon_p;
60 static gboolean is_pass = FALSE;
61 static void input_dialog_create	(gboolean is_password);
62 static gchar *input_dialog_open	(const gchar	*title,
63 				 const gchar	*message,
64 				 const gchar  *checkbtn_label,
65 				 const gchar	*default_string,
66 				 gboolean default_checkbtn_state,
67 				 gboolean	*remember);
68 static void input_dialog_set	(const gchar	*title,
69 				 const gchar	*message,
70 				 const gchar	*default_string);
71 
72 static void ok_clicked		(GtkWidget	*widget,
73 				 gpointer	 data);
74 static void cancel_clicked	(GtkWidget	*widget,
75 				 gpointer	 data);
76 static gint delete_event	(GtkWidget	*widget,
77 				 GdkEventAny	*event,
78 				 gpointer	 data);
79 static gboolean key_pressed	(GtkWidget	*widget,
80 				 GdkEventKey	*event,
81 				 gpointer	 data);
82 static void entry_activated	(GtkEditable	*editable);
83 static void combo_activated	(GtkEditable	*editable);
84 
85 
input_dialog(const gchar * title,const gchar * message,const gchar * default_string)86 gchar *input_dialog(const gchar *title, const gchar *message,
87 		    const gchar *default_string)
88 {
89 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
90 
91 	if (!dialog)
92 		input_dialog_create(FALSE);
93 
94 	type = INPUT_DIALOG_NORMAL;
95 	gtk_widget_hide(combo);
96 	gtk_widget_show(entry);
97 
98 	gtk_widget_hide(remember_checkbtn);
99 
100 	gtk_widget_show(icon_q);
101 	gtk_widget_hide(icon_p);
102 	is_pass = FALSE;
103 	gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
104 
105 	return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
106 }
107 
input_dialog_with_invisible(const gchar * title,const gchar * message,const gchar * default_string)108 gchar *input_dialog_with_invisible(const gchar *title, const gchar *message,
109 				   const gchar *default_string)
110 {
111 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
112 
113 	if (!dialog)
114 		input_dialog_create(TRUE);
115 
116 	type = INPUT_DIALOG_INVISIBLE;
117 	gtk_widget_hide(combo);
118 	gtk_widget_show(entry);
119 	gtk_widget_hide(remember_checkbtn);
120 
121 	gtk_widget_hide(icon_q);
122 	gtk_widget_show(icon_p);
123 	is_pass = TRUE;
124 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
125 
126 	return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
127 }
128 
input_dialog_with_invisible_checkbtn(const gchar * title,const gchar * message,const gchar * default_string,const gchar * checkbtn_label,gboolean * checkbtn_state)129 gchar *input_dialog_with_invisible_checkbtn(const gchar *title, const gchar *message,
130 				   const gchar *default_string, const gchar *checkbtn_label,
131 				   gboolean *checkbtn_state)
132 {
133 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
134 
135 	if (!dialog)
136 		input_dialog_create(TRUE);
137 
138 	type = INPUT_DIALOG_INVISIBLE;
139 	gtk_widget_hide(combo);
140 	gtk_widget_show(entry);
141 
142 	if (checkbtn_label && checkbtn_state) {
143 		gtk_widget_show(remember_checkbtn);
144 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), *checkbtn_state);
145 	}
146 	else {
147 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), FALSE);
148 		gtk_widget_hide(remember_checkbtn);
149 	}
150 
151 	gtk_widget_hide(icon_q);
152 	gtk_widget_show(icon_p);
153 	is_pass = TRUE;
154 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
155 
156 	return input_dialog_open(title, message, checkbtn_label, default_string, (checkbtn_state? *checkbtn_state:FALSE), checkbtn_state);
157 }
158 
input_dialog_combo(const gchar * title,const gchar * message,const gchar * default_string,GList * list)159 gchar *input_dialog_combo(const gchar *title, const gchar *message,
160 			  const gchar *default_string, GList *list)
161 {
162 	return input_dialog_combo_remember(title, message,
163 		default_string, list, FALSE);
164 }
165 
input_dialog_combo_remember(const gchar * title,const gchar * message,const gchar * default_string,GList * list,gboolean * remember)166 gchar *input_dialog_combo_remember(const gchar *title, const gchar *message,
167 			  const gchar *default_string, GList *list,
168 			  gboolean *remember)
169 {
170 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
171 
172 	if (!dialog)
173 		input_dialog_create(FALSE);
174 
175 	type = INPUT_DIALOG_COMBO;
176 	gtk_widget_hide(entry);
177 	gtk_widget_show(combo);
178 
179 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), FALSE);
180 	if (remember)
181 		gtk_widget_show(remember_checkbtn);
182 	else
183 		gtk_widget_hide(remember_checkbtn);
184 
185 	gtk_widget_show(icon_q);
186 	gtk_widget_hide(icon_p);
187 	is_pass = FALSE;
188 	combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(combo));
189 	combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(combo), list);
190 	return input_dialog_open(title, message, NULL, default_string, FALSE, remember);
191 }
192 
input_dialog_with_checkbtn(const gchar * title,const gchar * message,const gchar * default_string,const gchar * checkbtn_label,gboolean * checkbtn_state)193 gchar *input_dialog_with_checkbtn(const gchar	*title,
194 				   const gchar	*message,
195 				   const gchar	*default_string,
196 				   const gchar  *checkbtn_label,
197 				   gboolean *checkbtn_state)
198 {
199 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
200 
201 	if (!dialog)
202 		input_dialog_create(FALSE);
203 
204 	type = INPUT_DIALOG_NORMAL;
205 	gtk_widget_hide(combo);
206 	gtk_widget_show(entry);
207 
208 	if (checkbtn_label && checkbtn_state)
209 		gtk_widget_show(remember_checkbtn);
210 	else
211 		gtk_widget_hide(remember_checkbtn);
212 
213 	gtk_widget_show(icon_q);
214 	gtk_widget_hide(icon_p);
215 	is_pass = FALSE;
216 	gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
217 
218 	return input_dialog_open(title, message, checkbtn_label, default_string,
219 	       			 prefs_common.inherit_folder_props, checkbtn_state);
220 }
221 
input_dialog_query_password(const gchar * server,const gchar * user)222 gchar *input_dialog_query_password(const gchar *server, const gchar *user)
223 {
224 	gchar *message;
225 	gchar *pass;
226 
227 	if (server && user)
228 		message = g_strdup_printf(_("Input password for %s on %s:"),
229 				  user, server);
230 	else if (server)
231 		message = g_strdup_printf(_("Input password for %s:"),
232 				  server);
233 	else if (user)
234 		message = g_strdup_printf(_("Input password for %s:"),
235 				  user);
236 	else
237 		message = g_strdup_printf(_("Input password:"));
238 	pass = input_dialog_with_invisible(_("Input password"), message, NULL);
239 	g_free(message);
240 
241 	return pass;
242 }
243 
input_dialog_query_password_keep(const gchar * server,const gchar * user,gchar ** keep)244 gchar *input_dialog_query_password_keep(const gchar *server, const gchar *user, gchar **keep)
245 {
246 	gchar *message;
247 	gchar *pass;
248 
249 	if (server && user)
250 		message = g_strdup_printf(_("Input password for %s on %s:"),
251 				  user, server);
252 	else if (server)
253 		message = g_strdup_printf(_("Input password for %s:"),
254 				  server);
255 	else if (user)
256 		message = g_strdup_printf(_("Input password for %s:"),
257 				  user);
258 	else
259 		message = g_strdup_printf(_("Input password:"));
260         if (keep) {
261 		if (*keep != NULL) {
262 			pass = g_strdup (*keep);
263 		}
264 		else {
265 			gboolean state = prefs_common.session_passwords;
266 			pass = input_dialog_with_invisible_checkbtn(_("Input password"),
267 					message, NULL,
268 					_("Remember password for this session"),
269 					&state);
270 			if (state) {
271 				*keep = g_strdup (pass);
272 				debug_print("keeping session password for account\n");
273 			}
274 			prefs_common.session_passwords = state;
275 		}
276 	}
277 	else {
278 		pass = input_dialog_with_invisible(_("Input password"), message, NULL);
279 	}
280 	g_free(message);
281 
282 	return pass;
283 }
284 
input_dialog_create(gboolean is_password)285 static void input_dialog_create(gboolean is_password)
286 {
287 	static PangoFontDescription *font_desc;
288 	GtkWidget *hbox;
289 	GtkWidget *vbox;
290 	GtkWidget *cancel_button;
291 	GtkWidget *confirm_area;
292 
293 	dialog = gtk_dialog_new();
294 
295 	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
296 	gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
297 	gtk_window_set_title(GTK_WINDOW(dialog), "");
298 
299 	g_signal_connect(G_OBJECT(dialog), "delete_event",
300 			 G_CALLBACK(delete_event), NULL);
301 	g_signal_connect(G_OBJECT(dialog), "key_press_event",
302 			 G_CALLBACK(key_pressed), NULL);
303 	MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
304 
305 	vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
306 	gtk_box_set_spacing (GTK_BOX (vbox), 14);
307 	hbox = gtk_hbox_new (FALSE, 12);
308 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
309 	gtk_widget_show (hbox);
310 	gtk_box_pack_start (GTK_BOX (vbox), hbox,
311 			    FALSE, FALSE, 0);
312 
313 	/* for title label */
314 	icon_q = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
315         			GTK_ICON_SIZE_DIALOG);
316 	gtk_misc_set_alignment (GTK_MISC (icon_q), 0.5, 0.0);
317 	gtk_box_pack_start (GTK_BOX (hbox), icon_q, FALSE, FALSE, 0);
318 	icon_p = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION,
319         			GTK_ICON_SIZE_DIALOG);
320 	gtk_misc_set_alignment (GTK_MISC (icon_p), 0.5, 0.0);
321 	gtk_box_pack_start (GTK_BOX (hbox), icon_p, FALSE, FALSE, 0);
322 
323 	vbox = gtk_vbox_new (FALSE, 12);
324 	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
325 	gtk_widget_show (vbox);
326 
327 	msg_title = gtk_label_new("");
328 	gtk_misc_set_alignment(GTK_MISC(msg_title), 0, 0.5);
329 	gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT);
330 	gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE);
331 	gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0);
332 	gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE);
333 	if (!font_desc) {
334 		gint size;
335 
336 		size = pango_font_description_get_size
337 			(gtk_widget_get_style(msg_title)->font_desc);
338 		font_desc = pango_font_description_new();
339 		pango_font_description_set_weight
340 			(font_desc, PANGO_WEIGHT_BOLD);
341 		pango_font_description_set_size
342 			(font_desc, size * PANGO_SCALE_LARGE);
343 	}
344 	if (font_desc)
345 		gtk_widget_modify_font(msg_title, font_desc);
346 
347 	msg_label = gtk_label_new("");
348 	gtk_misc_set_alignment(GTK_MISC(msg_label), 0, 0.5);
349 	gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT);
350 	gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0);
351 	gtk_widget_show(msg_label);
352 
353 	entry = gtk_entry_new();
354 	gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
355 	g_signal_connect(G_OBJECT(entry), "activate",
356 			 G_CALLBACK(entry_activated), NULL);
357 
358 	combo = gtk_combo_box_text_new_with_entry();
359 	gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
360 	g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "activate",
361 			 G_CALLBACK(combo_activated), NULL);
362 
363 	remember_checkbtn = gtk_check_button_new_with_label(_("Remember this"));
364 	gtk_box_pack_start(GTK_BOX(vbox), remember_checkbtn, FALSE, FALSE, 0);
365 
366 	gtkut_stock_button_set_create(&confirm_area,
367 				      &cancel_button, GTK_STOCK_CANCEL,
368 				      &ok_button, GTK_STOCK_OK,
369 				      NULL, NULL);
370 
371 	gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog))),
372 			 confirm_area, FALSE, FALSE, 0);
373 	gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
374 
375 	gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
376 
377 	gtk_widget_hide(remember_checkbtn);
378 
379 	if (is_password)
380 		gtk_widget_hide(icon_q);
381 	else
382 		gtk_widget_hide(icon_p);
383 
384 	is_pass = is_password;
385 
386 	gtk_widget_grab_default(ok_button);
387 
388 	g_signal_connect(G_OBJECT(ok_button), "clicked",
389 			 G_CALLBACK(ok_clicked), NULL);
390 	g_signal_connect(G_OBJECT(cancel_button), "clicked",
391 			 G_CALLBACK(cancel_clicked), NULL);
392 }
393 
input_dialog_open(const gchar * title,const gchar * message,const gchar * checkbtn_label,const gchar * default_string,gboolean default_checkbtn_state,gboolean * remember)394 static gchar *input_dialog_open(const gchar *title, const gchar *message,
395 				const gchar *checkbtn_label,
396 				const gchar *default_string,
397 				gboolean default_checkbtn_state,
398 				gboolean *remember)
399 {
400 	gchar *str;
401 
402 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
403 
404 	if (!dialog)
405 		input_dialog_create(FALSE);
406 
407 	if (checkbtn_label)
408 		gtk_button_set_label(GTK_BUTTON(remember_checkbtn), checkbtn_label);
409 	else
410 		gtk_button_set_label(GTK_BUTTON(remember_checkbtn), _("Remember this"));
411 
412 	input_dialog_set(title, message, default_string);
413 	gtk_window_present(GTK_WINDOW(dialog));
414 
415 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn),
416 				     default_checkbtn_state);
417 	if (remember)
418 		gtk_widget_show(remember_checkbtn);
419 	else
420 		gtk_widget_hide(remember_checkbtn);
421 
422 	manage_window_set_transient(GTK_WINDOW(dialog));
423 	gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
424 
425 	ack = fin = FALSE;
426 
427 	while (fin == FALSE)
428 		gtk_main_iteration();
429 
430 	manage_window_focus_out(dialog, NULL, NULL);
431 
432 	if (ack) {
433 		GtkEditable *editable;
434 
435 		if (type == INPUT_DIALOG_COMBO)
436 			editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo))));
437 		else
438 			editable = GTK_EDITABLE(entry);
439 
440 		str = gtk_editable_get_chars(editable, 0, -1);
441 		if (str && *str == '\0' && !is_pass) {
442 			g_free(str);
443 			str = NULL;
444 		}
445 	} else
446 		str = NULL;
447 
448 	GTK_EVENTS_FLUSH();
449 
450 	if (remember) {
451 		*remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remember_checkbtn));
452 	}
453 
454 	gtk_widget_destroy(dialog);
455 	dialog = NULL;
456 
457 	if (is_pass)
458 		debug_print("return string = %s\n", str ? "********": ("none"));
459 	else
460 		debug_print("return string = %s\n", str ? str : "(none)");
461 	return str;
462 }
463 
input_dialog_set(const gchar * title,const gchar * message,const gchar * default_string)464 static void input_dialog_set(const gchar *title, const gchar *message,
465 			     const gchar *default_string)
466 {
467 	GtkWidget *entry_;
468 
469 	if (type == INPUT_DIALOG_COMBO)
470 		entry_ = gtk_bin_get_child(GTK_BIN((combo)));
471 	else
472 		entry_ = entry;
473 
474 	gtk_window_set_title(GTK_WINDOW(dialog), title);
475 	gtk_label_set_text(GTK_LABEL(msg_title), title);
476 	gtk_label_set_text(GTK_LABEL(msg_label), message);
477 	if (default_string && *default_string) {
478 		gtk_entry_set_text(GTK_ENTRY(entry_), default_string);
479 		gtk_editable_set_position(GTK_EDITABLE(entry_), 0);
480 		gtk_editable_select_region(GTK_EDITABLE(entry_), 0, -1);
481 	} else
482 		gtk_entry_set_text(GTK_ENTRY(entry_), "");
483 
484 	gtk_widget_grab_focus(ok_button);
485 	gtk_widget_grab_focus(entry_);
486 }
487 
ok_clicked(GtkWidget * widget,gpointer data)488 static void ok_clicked(GtkWidget *widget, gpointer data)
489 {
490 	ack = TRUE;
491 	fin = TRUE;
492 }
493 
cancel_clicked(GtkWidget * widget,gpointer data)494 static void cancel_clicked(GtkWidget *widget, gpointer data)
495 {
496 	ack = FALSE;
497 	fin = TRUE;
498 }
499 
delete_event(GtkWidget * widget,GdkEventAny * event,gpointer data)500 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
501 {
502 	ack = FALSE;
503 	fin = TRUE;
504 
505 	return TRUE;
506 }
507 
key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)508 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
509 {
510 	if (event && event->keyval == GDK_KEY_Escape) {
511 		ack = FALSE;
512 		fin = TRUE;
513 	} else if (event && event->keyval == GDK_KEY_Return) {
514 		ack = TRUE;
515 		fin = TRUE;
516 		return TRUE; /* do not let Return pass - it
517 			      * pops up the combo on validating */
518 	}
519 
520 	return FALSE;
521 }
522 
entry_activated(GtkEditable * editable)523 static void entry_activated(GtkEditable *editable)
524 {
525 	ack = TRUE;
526 	fin = TRUE;
527 }
528 
combo_activated(GtkEditable * editable)529 static void combo_activated(GtkEditable *editable)
530 {
531 	ack = TRUE;
532 	fin = TRUE;
533 }
534