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