3c00c2b3b9ff80955d0c22774cb4c307fa8b3456
[claws.git] / src / gtk / inputdialog.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 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 #endif
23
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gtk/gtkmain.h>
28 #include <gtk/gtkwidget.h>
29 #include <gtk/gtkdialog.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtksignal.h>
32 #include <gtk/gtkvbox.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkentry.h>
36 #include <gtk/gtkcomboboxentry.h>
37 #include <gtk/gtkbutton.h>
38 #include <gtk/gtkhbbox.h>
39 #include <gtk/gtkstock.h>
40 #include <gtk/gtkimage.h>
41 #include <gtk/gtktogglebutton.h>
42 #include <gtk/gtkcheckbutton.h>
43
44 #include "inputdialog.h"
45 #include "manage_window.h"
46 #include "gtkutils.h"
47 #include "utils.h"
48 #include "combobox.h"
49 #include "prefs_common.h"
50
51
52 #define INPUT_DIALOG_WIDTH      420
53
54 typedef enum
55 {
56         INPUT_DIALOG_NORMAL,
57         INPUT_DIALOG_INVISIBLE,
58         INPUT_DIALOG_COMBO
59 } InputDialogType;
60
61 static gboolean ack;
62 static gboolean fin;
63
64 static InputDialogType type;
65
66 static GtkWidget *dialog;
67 static GtkWidget *msg_title;
68 static GtkWidget *msg_label;
69 static GtkWidget *entry;
70 static GtkWidget *combo;
71 static GtkWidget *remember_checkbtn;
72 static GtkWidget *ok_button;
73 static GtkWidget *icon_q, *icon_p;
74 static gboolean is_pass = FALSE;
75 static void input_dialog_create (gboolean is_password);
76 static gchar *input_dialog_open (const gchar    *title,
77                                  const gchar    *message,
78                                  const gchar  *checkbtn_label,
79                                  const gchar    *default_string,
80                                  gboolean default_checkbtn_state,
81                                  gboolean       *remember);
82 static void input_dialog_set    (const gchar    *title,
83                                  const gchar    *message,
84                                  const gchar    *default_string);
85
86 static void ok_clicked          (GtkWidget      *widget,
87                                  gpointer        data);
88 static void cancel_clicked      (GtkWidget      *widget,
89                                  gpointer        data);
90 static gint delete_event        (GtkWidget      *widget,
91                                  GdkEventAny    *event,
92                                  gpointer        data);
93 static gboolean key_pressed     (GtkWidget      *widget,
94                                  GdkEventKey    *event,
95                                  gpointer        data);
96 static void entry_activated     (GtkEditable    *editable);
97 static void combo_activated     (GtkEditable    *editable);
98
99
100 gchar *input_dialog(const gchar *title, const gchar *message,
101                     const gchar *default_string)
102 {
103         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
104
105         if (!dialog)
106                 input_dialog_create(FALSE);
107
108         type = INPUT_DIALOG_NORMAL;
109         gtk_widget_hide(combo);
110         gtk_widget_show(entry);
111
112         gtk_widget_hide(remember_checkbtn);
113
114         gtk_widget_show(icon_q);
115         gtk_widget_hide(icon_p);
116         is_pass = FALSE;
117         gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
118 #ifdef MAEMO
119         hildon_gtk_entry_set_input_mode(GTK_ENTRY(entry), 
120                 HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
121 #endif
122
123         return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
124 }
125
126 gchar *input_dialog_with_invisible(const gchar *title, const gchar *message,
127                                    const gchar *default_string)
128 {
129         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
130
131         if (!dialog)
132                 input_dialog_create(TRUE);
133
134         type = INPUT_DIALOG_INVISIBLE;
135         gtk_widget_hide(combo);
136         gtk_widget_show(entry);
137         gtk_widget_hide(remember_checkbtn);
138
139         gtk_widget_hide(icon_q);
140         gtk_widget_show(icon_p);
141         is_pass = TRUE;
142         gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
143 #ifdef MAEMO
144         hildon_gtk_entry_set_input_mode(GTK_ENTRY(entry), 
145                 HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_INVISIBLE);
146 #endif
147
148         return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
149 }
150
151 gchar *input_dialog_combo(const gchar *title, const gchar *message,
152                           const gchar *default_string, GList *list)
153 {
154         return input_dialog_combo_remember(title, message, 
155                 default_string, list, FALSE);
156 }
157
158 gchar *input_dialog_combo_remember(const gchar *title, const gchar *message,
159                           const gchar *default_string, GList *list,
160                           gboolean *remember)
161 {
162         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
163
164         if (!dialog)
165                 input_dialog_create(FALSE);
166
167         type = INPUT_DIALOG_COMBO;
168         gtk_widget_hide(entry);
169         gtk_widget_show(combo);
170
171         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), FALSE);
172         if (remember)
173                 gtk_widget_show(remember_checkbtn);
174         else
175                 gtk_widget_hide(remember_checkbtn);
176
177         gtk_widget_show(icon_q);
178         gtk_widget_hide(icon_p);
179         is_pass = FALSE;
180
181         combobox_unset_popdown_strings(GTK_COMBO_BOX(combo));
182         combobox_set_popdown_strings(GTK_COMBO_BOX(combo), list);
183
184         return input_dialog_open(title, message, NULL, default_string, FALSE, remember);
185 }
186
187 gchar *input_dialog_with_checkbtn(const gchar   *title,
188                                    const gchar  *message,
189                                    const gchar  *default_string,
190                                    const gchar  *checkbtn_label,
191                                    gboolean *checkbtn_state)
192 {
193         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
194
195         if (!dialog)
196                 input_dialog_create(FALSE);
197
198         type = INPUT_DIALOG_NORMAL;
199         gtk_widget_hide(combo);
200         gtk_widget_show(entry);
201
202         if(checkbtn_label && checkbtn_state)
203                 gtk_widget_show(remember_checkbtn);
204         else
205                 gtk_widget_hide(remember_checkbtn);
206
207         gtk_widget_show(icon_q);
208         gtk_widget_hide(icon_p);
209         is_pass = FALSE;
210         gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
211 #ifdef MAEMO
212         hildon_gtk_entry_set_input_mode(GTK_ENTRY(entry), 
213                 HILDON_GTK_INPUT_MODE_FULL | HILDON_GTK_INPUT_MODE_AUTOCAP);
214 #endif
215
216         return input_dialog_open(title, message, checkbtn_label, default_string, 
217                                  prefs_common.inherit_folder_props, checkbtn_state);
218 }
219
220 gchar *input_dialog_query_password(const gchar *server, const gchar *user)
221 {
222         gchar *message;
223         gchar *pass;
224
225         message = g_strdup_printf(_("Input password for %s on %s:"),
226                                   user, server);
227         pass = input_dialog_with_invisible(_("Input password"), message, NULL);
228         g_free(message);
229
230         return pass;
231 }
232
233 static void input_dialog_create(gboolean is_password)
234 {
235         static PangoFontDescription *font_desc;
236         GtkWidget *w_hbox;
237         GtkWidget *hbox;
238         GtkWidget *vbox;
239         GtkWidget *cancel_button;
240         GtkWidget *confirm_area;
241
242         dialog = gtk_dialog_new();
243
244         gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
245         gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
246         gtk_window_set_title(GTK_WINDOW(dialog), "");
247         gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
248
249         g_signal_connect(G_OBJECT(dialog), "delete_event",
250                          G_CALLBACK(delete_event), NULL);
251         g_signal_connect(G_OBJECT(dialog), "key_press_event",
252                          G_CALLBACK(key_pressed), NULL);
253         MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
254
255         gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
256         hbox = gtk_hbox_new (FALSE, 12);
257         gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
258         gtk_widget_show (hbox);
259         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
260                             FALSE, FALSE, 0);
261
262         /* for title label */
263         w_hbox = gtk_hbox_new(FALSE, 0);
264         
265         icon_q = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
266                                 GTK_ICON_SIZE_DIALOG); 
267         gtk_misc_set_alignment (GTK_MISC (icon_q), 0.5, 0.0);
268         gtk_box_pack_start (GTK_BOX (hbox), icon_q, FALSE, FALSE, 0);
269         icon_p = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION,
270                                 GTK_ICON_SIZE_DIALOG); 
271         gtk_misc_set_alignment (GTK_MISC (icon_p), 0.5, 0.0);
272         gtk_box_pack_start (GTK_BOX (hbox), icon_p, FALSE, FALSE, 0);
273         
274         vbox = gtk_vbox_new (FALSE, 12);
275         gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
276         gtk_widget_show (vbox);
277         
278         msg_title = gtk_label_new("");
279         gtk_misc_set_alignment(GTK_MISC(msg_title), 0, 0.5);
280         gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT);
281         gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE);
282         gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0);
283         gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE);
284         if (!font_desc) {
285                 gint size;
286
287                 size = pango_font_description_get_size
288                         (msg_title->style->font_desc);
289                 font_desc = pango_font_description_new();
290                 pango_font_description_set_weight
291                         (font_desc, PANGO_WEIGHT_BOLD);
292                 pango_font_description_set_size
293                         (font_desc, size * PANGO_SCALE_LARGE);
294         }
295         if (font_desc)
296                 gtk_widget_modify_font(msg_title, font_desc);
297         
298         msg_label = gtk_label_new("");
299         gtk_misc_set_alignment(GTK_MISC(msg_label), 0, 0.5);
300         gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT);
301         gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0);
302         gtk_widget_show(msg_label);
303                 
304         entry = gtk_entry_new();
305         gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
306         g_signal_connect(G_OBJECT(entry), "activate",
307                          G_CALLBACK(entry_activated), NULL);
308
309         combo = gtk_combo_box_entry_new_text();
310         gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
311         g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "activate",
312                          G_CALLBACK(combo_activated), NULL);
313
314         remember_checkbtn = gtk_check_button_new_with_label(_("Remember this"));
315         gtk_box_pack_start(GTK_BOX(vbox), remember_checkbtn, FALSE, FALSE, 0);
316
317         hbox = gtk_hbox_new(TRUE, 0);
318
319         gtkut_stock_button_set_create(&confirm_area,
320                                       &cancel_button, GTK_STOCK_CANCEL,
321                                       &ok_button, GTK_STOCK_OK,
322                                       NULL, NULL);
323
324         gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
325                          confirm_area, FALSE, FALSE, 0);
326         gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
327
328         gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
329         
330         gtk_widget_hide(remember_checkbtn);
331
332         if (is_password)
333                 gtk_widget_hide(icon_q);
334         else
335                 gtk_widget_hide(icon_p);
336
337         is_pass = is_password;
338
339         gtk_widget_grab_default(ok_button);
340
341         g_signal_connect(G_OBJECT(ok_button), "clicked",
342                          G_CALLBACK(ok_clicked), NULL);
343         g_signal_connect(G_OBJECT(cancel_button), "clicked",
344                          G_CALLBACK(cancel_clicked), NULL);
345 }
346
347 static gchar *input_dialog_open(const gchar *title, const gchar *message,
348                                 const gchar *checkbtn_label,
349                                 const gchar *default_string,
350                                 gboolean default_checkbtn_state,
351                                 gboolean *remember)
352 {
353         gchar *str;
354
355         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
356
357         if (!dialog)
358                 input_dialog_create(FALSE);
359
360         if(checkbtn_label)
361                 gtk_button_set_label(GTK_BUTTON(remember_checkbtn), checkbtn_label);
362         else
363                 gtk_button_set_label(GTK_BUTTON(remember_checkbtn), _("Remember this"));
364
365         input_dialog_set(title, message, default_string);
366         gtk_widget_show(dialog);
367
368         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn),
369                                      default_checkbtn_state);
370         if (remember)
371                 gtk_widget_show(remember_checkbtn);
372         else
373                 gtk_widget_hide(remember_checkbtn);
374
375         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
376         manage_window_set_transient(GTK_WINDOW(dialog));
377
378         ack = fin = FALSE;
379
380         while (fin == FALSE)
381                 gtk_main_iteration();
382
383         manage_window_focus_out(dialog, NULL, NULL);
384         gtk_widget_hide(dialog);
385
386         if (ack) {
387                 GtkEditable *editable;
388
389                 if (type == INPUT_DIALOG_COMBO)
390                         editable = GTK_EDITABLE(GTK_BIN(combo)->child);
391                 else
392                         editable = GTK_EDITABLE(entry);
393
394                 str = gtk_editable_get_chars(editable, 0, -1);
395                 if (str && *str == '\0' && !is_pass) {
396                         g_free(str);
397                         str = NULL;
398                 }
399         } else
400                 str = NULL;
401
402         GTK_EVENTS_FLUSH();
403
404         if (remember) {
405                 *remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remember_checkbtn));
406         }
407         if (is_pass)
408                 debug_print("return string = %s\n", str ? "********": ("none"));
409         else
410                 debug_print("return string = %s\n", str ? str : "(none)");
411         return str;
412 }
413
414 static void input_dialog_set(const gchar *title, const gchar *message,
415                              const gchar *default_string)
416 {
417         GtkWidget *entry_;
418
419         if (type == INPUT_DIALOG_COMBO)
420                 entry_ = GTK_BIN(combo)->child;
421         else
422                 entry_ = entry;
423
424         gtk_window_set_title(GTK_WINDOW(dialog), title);
425         gtk_label_set_text(GTK_LABEL(msg_title), title);
426         gtk_label_set_text(GTK_LABEL(msg_label), message);
427         if (default_string && *default_string) {
428                 gtk_entry_set_text(GTK_ENTRY(entry_), default_string);
429                 gtk_editable_set_position(GTK_EDITABLE(entry_), 0);
430                 gtk_editable_select_region(GTK_EDITABLE(entry_), 0, -1);
431         } else
432                 gtk_entry_set_text(GTK_ENTRY(entry_), "");
433
434         gtk_widget_grab_focus(ok_button);
435         gtk_widget_grab_focus(entry_);
436 }
437
438 static void ok_clicked(GtkWidget *widget, gpointer data)
439 {
440         ack = TRUE;
441         fin = TRUE;
442 }
443
444 static void cancel_clicked(GtkWidget *widget, gpointer data)
445 {
446         ack = FALSE;
447         fin = TRUE;
448 }
449
450 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
451 {
452         ack = FALSE;
453         fin = TRUE;
454
455         return TRUE;
456 }
457
458 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
459 {
460         if (event && event->keyval == GDK_Escape) {
461                 ack = FALSE;
462                 fin = TRUE;
463         } else if (event && event->keyval == GDK_Return) {
464                 ack = TRUE;
465                 fin = TRUE;
466                 return TRUE; /* do not let Return pass - it
467                               * pops up the combo on validating */
468         }
469
470         return FALSE;
471 }
472
473 static void entry_activated(GtkEditable *editable)
474 {
475         ack = TRUE;
476         fin = TRUE;
477 }
478
479 static void combo_activated(GtkEditable *editable)
480 {
481         ack = TRUE;
482         fin = TRUE;
483 }