2008-07-03 [colin] 3.5.0cvs2
[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         if (server && user)
226                 message = g_strdup_printf(_("Input password for %s on %s:"),
227                                   user, server);
228         else if (server)
229                 message = g_strdup_printf(_("Input password for %s:"),
230                                   server);
231         else if (user)
232                 message = g_strdup_printf(_("Input password for %s:"),
233                                   user);
234         else
235                 message = g_strdup_printf(_("Input password:"));
236         pass = input_dialog_with_invisible(_("Input password"), message, NULL);
237         g_free(message);
238
239         return pass;
240 }
241
242 static void input_dialog_create(gboolean is_password)
243 {
244         static PangoFontDescription *font_desc;
245         GtkWidget *w_hbox;
246         GtkWidget *hbox;
247         GtkWidget *vbox;
248         GtkWidget *cancel_button;
249         GtkWidget *confirm_area;
250
251         dialog = gtk_dialog_new();
252
253         gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
254         gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
255         gtk_window_set_title(GTK_WINDOW(dialog), "");
256         gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
257
258         g_signal_connect(G_OBJECT(dialog), "delete_event",
259                          G_CALLBACK(delete_event), NULL);
260         g_signal_connect(G_OBJECT(dialog), "key_press_event",
261                          G_CALLBACK(key_pressed), NULL);
262         MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
263
264         gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
265         hbox = gtk_hbox_new (FALSE, 12);
266         gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
267         gtk_widget_show (hbox);
268         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
269                             FALSE, FALSE, 0);
270
271         /* for title label */
272         w_hbox = gtk_hbox_new(FALSE, 0);
273         
274         icon_q = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION,
275                                 GTK_ICON_SIZE_DIALOG); 
276         gtk_misc_set_alignment (GTK_MISC (icon_q), 0.5, 0.0);
277         gtk_box_pack_start (GTK_BOX (hbox), icon_q, FALSE, FALSE, 0);
278         icon_p = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION,
279                                 GTK_ICON_SIZE_DIALOG); 
280         gtk_misc_set_alignment (GTK_MISC (icon_p), 0.5, 0.0);
281         gtk_box_pack_start (GTK_BOX (hbox), icon_p, FALSE, FALSE, 0);
282         
283         vbox = gtk_vbox_new (FALSE, 12);
284         gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
285         gtk_widget_show (vbox);
286         
287         msg_title = gtk_label_new("");
288         gtk_misc_set_alignment(GTK_MISC(msg_title), 0, 0.5);
289         gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT);
290         gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE);
291         gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0);
292         gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE);
293         if (!font_desc) {
294                 gint size;
295
296                 size = pango_font_description_get_size
297                         (msg_title->style->font_desc);
298                 font_desc = pango_font_description_new();
299                 pango_font_description_set_weight
300                         (font_desc, PANGO_WEIGHT_BOLD);
301                 pango_font_description_set_size
302                         (font_desc, size * PANGO_SCALE_LARGE);
303         }
304         if (font_desc)
305                 gtk_widget_modify_font(msg_title, font_desc);
306         
307         msg_label = gtk_label_new("");
308         gtk_misc_set_alignment(GTK_MISC(msg_label), 0, 0.5);
309         gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT);
310         gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0);
311         gtk_widget_show(msg_label);
312                 
313         entry = gtk_entry_new();
314         gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
315         g_signal_connect(G_OBJECT(entry), "activate",
316                          G_CALLBACK(entry_activated), NULL);
317
318         combo = gtk_combo_box_entry_new_text();
319         gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
320         g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "activate",
321                          G_CALLBACK(combo_activated), NULL);
322
323         remember_checkbtn = gtk_check_button_new_with_label(_("Remember this"));
324         gtk_box_pack_start(GTK_BOX(vbox), remember_checkbtn, FALSE, FALSE, 0);
325
326         hbox = gtk_hbox_new(TRUE, 0);
327
328         gtkut_stock_button_set_create(&confirm_area,
329                                       &cancel_button, GTK_STOCK_CANCEL,
330                                       &ok_button, GTK_STOCK_OK,
331                                       NULL, NULL);
332
333         gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
334                          confirm_area, FALSE, FALSE, 0);
335         gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
336
337         gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
338         
339         gtk_widget_hide(remember_checkbtn);
340
341         if (is_password)
342                 gtk_widget_hide(icon_q);
343         else
344                 gtk_widget_hide(icon_p);
345
346         is_pass = is_password;
347
348         gtk_widget_grab_default(ok_button);
349
350         g_signal_connect(G_OBJECT(ok_button), "clicked",
351                          G_CALLBACK(ok_clicked), NULL);
352         g_signal_connect(G_OBJECT(cancel_button), "clicked",
353                          G_CALLBACK(cancel_clicked), NULL);
354 }
355
356 static gchar *input_dialog_open(const gchar *title, const gchar *message,
357                                 const gchar *checkbtn_label,
358                                 const gchar *default_string,
359                                 gboolean default_checkbtn_state,
360                                 gboolean *remember)
361 {
362         gchar *str;
363
364         if (dialog && GTK_WIDGET_VISIBLE(dialog)) return NULL;
365
366         if (!dialog)
367                 input_dialog_create(FALSE);
368
369         if (checkbtn_label)
370                 gtk_button_set_label(GTK_BUTTON(remember_checkbtn), checkbtn_label);
371         else
372                 gtk_button_set_label(GTK_BUTTON(remember_checkbtn), _("Remember this"));
373
374         input_dialog_set(title, message, default_string);
375         gtk_widget_show(dialog);
376
377         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn),
378                                      default_checkbtn_state);
379         if (remember)
380                 gtk_widget_show(remember_checkbtn);
381         else
382                 gtk_widget_hide(remember_checkbtn);
383
384         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
385         manage_window_set_transient(GTK_WINDOW(dialog));
386
387         ack = fin = FALSE;
388
389         while (fin == FALSE)
390                 gtk_main_iteration();
391
392         manage_window_focus_out(dialog, NULL, NULL);
393         gtk_widget_hide(dialog);
394
395         if (ack) {
396                 GtkEditable *editable;
397
398                 if (type == INPUT_DIALOG_COMBO)
399                         editable = GTK_EDITABLE(GTK_BIN(combo)->child);
400                 else
401                         editable = GTK_EDITABLE(entry);
402
403                 str = gtk_editable_get_chars(editable, 0, -1);
404                 if (str && *str == '\0' && !is_pass) {
405                         g_free(str);
406                         str = NULL;
407                 }
408         } else
409                 str = NULL;
410
411         GTK_EVENTS_FLUSH();
412
413         if (remember) {
414                 *remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remember_checkbtn));
415         }
416         if (is_pass)
417                 debug_print("return string = %s\n", str ? "********": ("none"));
418         else
419                 debug_print("return string = %s\n", str ? str : "(none)");
420         return str;
421 }
422
423 static void input_dialog_set(const gchar *title, const gchar *message,
424                              const gchar *default_string)
425 {
426         GtkWidget *entry_;
427
428         if (type == INPUT_DIALOG_COMBO)
429                 entry_ = GTK_BIN(combo)->child;
430         else
431                 entry_ = entry;
432
433         gtk_window_set_title(GTK_WINDOW(dialog), title);
434         gtk_label_set_text(GTK_LABEL(msg_title), title);
435         gtk_label_set_text(GTK_LABEL(msg_label), message);
436         if (default_string && *default_string) {
437                 gtk_entry_set_text(GTK_ENTRY(entry_), default_string);
438                 gtk_editable_set_position(GTK_EDITABLE(entry_), 0);
439                 gtk_editable_select_region(GTK_EDITABLE(entry_), 0, -1);
440         } else
441                 gtk_entry_set_text(GTK_ENTRY(entry_), "");
442
443         gtk_widget_grab_focus(ok_button);
444         gtk_widget_grab_focus(entry_);
445 }
446
447 static void ok_clicked(GtkWidget *widget, gpointer data)
448 {
449         ack = TRUE;
450         fin = TRUE;
451 }
452
453 static void cancel_clicked(GtkWidget *widget, gpointer data)
454 {
455         ack = FALSE;
456         fin = TRUE;
457 }
458
459 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
460 {
461         ack = FALSE;
462         fin = TRUE;
463
464         return TRUE;
465 }
466
467 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
468 {
469         if (event && event->keyval == GDK_Escape) {
470                 ack = FALSE;
471                 fin = TRUE;
472         } else if (event && event->keyval == GDK_Return) {
473                 ack = TRUE;
474                 fin = TRUE;
475                 return TRUE; /* do not let Return pass - it
476                               * pops up the combo on validating */
477         }
478
479         return FALSE;
480 }
481
482 static void entry_activated(GtkEditable *editable)
483 {
484         ack = TRUE;
485         fin = TRUE;
486 }
487
488 static void combo_activated(GtkEditable *editable)
489 {
490         ack = TRUE;
491         fin = TRUE;
492 }