2007-03-24 [wwp] 2.8.1cvs48
[claws.git] / src / alertpanel.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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include <stddef.h>
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30
31 #include "mainwindow.h"
32 #include "alertpanel.h"
33 #include "manage_window.h"
34 #include "utils.h"
35 #include "gtkutils.h"
36 #include "inc.h"
37 #include "logwindow.h"
38 #include "prefs_common.h"
39
40 #define ALERT_PANEL_WIDTH       380
41 #define TITLE_HEIGHT            72
42 #define MESSAGE_HEIGHT          62
43
44 gboolean alertpanel_is_open = FALSE;
45 static AlertValue value;
46
47 static GtkWidget *dialog;
48
49 static void alertpanel_show             (void);
50 static void alertpanel_create           (const gchar    *title,
51                                          const gchar    *message,
52                                          const gchar    *button1_label,
53                                          const gchar    *button2_label,
54                                          const gchar    *button3_label,
55                                          gboolean        can_disable,
56                                          GtkWidget      *custom_widget,
57                                          gint            alert_type,
58                                          AlertValue      default_value);
59
60 static void alertpanel_button_toggled   (GtkToggleButton        *button,
61                                          gpointer                data);
62 static void alertpanel_button_clicked   (GtkWidget              *widget,
63                                          gpointer                data);
64 static gint alertpanel_deleted          (GtkWidget              *widget,
65                                          GdkEventAny            *event,
66                                          gpointer                data);
67 static gboolean alertpanel_close        (GtkWidget              *widget,
68                                          GdkEventAny            *event,
69                                          gpointer                data);
70
71 AlertValue alertpanel_with_widget(const gchar *title,
72                                   const gchar *message,
73                                   const gchar *button1_label,
74                                   const gchar *button2_label,
75                                   const gchar *button3_label,
76                                   gboolean     can_disable,
77                                   AlertValue   default_value,
78                                   GtkWidget   *widget)
79 {
80         return alertpanel_full(title, message, button1_label,
81                                     button2_label, button3_label,
82                                     can_disable, widget, ALERT_QUESTION,
83                                     default_value);
84 }
85
86 AlertValue alertpanel_full(const gchar *title, const gchar *message,
87                            const gchar *button1_label,
88                            const gchar *button2_label,
89                            const gchar *button3_label,
90                            gboolean     can_disable,
91                            GtkWidget   *widget,
92                            AlertType    alert_type,
93                            AlertValue   default_value)
94 {
95         if (alertpanel_is_open)
96                 return -1;
97         else
98                 alertpanel_is_open = TRUE;
99         
100         alertpanel_create(title, message, button1_label, button2_label,
101                           button3_label, can_disable, widget, alert_type,
102                           default_value);
103         alertpanel_show();
104
105         debug_print("return value = %d\n", value);
106         return value;
107 }
108
109 AlertValue alertpanel(const gchar *title,
110                       const gchar *message,
111                       const gchar *button1_label,
112                       const gchar *button2_label,
113                       const gchar *button3_label)
114 {
115         return alertpanel_full(title, message, button1_label, button2_label,
116                                button3_label, FALSE, NULL, ALERT_QUESTION,
117                                G_ALERTDEFAULT);
118 }
119
120 static void alertpanel_message(const gchar *title, const gchar *message, gint type)
121 {
122         if (alertpanel_is_open)
123                 return;
124         else
125                 alertpanel_is_open = TRUE;
126
127         alertpanel_create(title, message, GTK_STOCK_CLOSE, NULL, NULL,
128                           FALSE, NULL, type, G_ALERTDEFAULT);
129         alertpanel_show();
130 }
131
132 void alertpanel_notice(const gchar *format, ...)
133 {
134         va_list args;
135         gchar buf[256];
136
137         va_start(args, format);
138         g_vsnprintf(buf, sizeof(buf), format, args);
139         va_end(args);
140         strretchomp(buf);
141
142         alertpanel_message(_("Notice"), buf, ALERT_NOTICE);
143 }
144
145 void alertpanel_warning(const gchar *format, ...)
146 {
147         va_list args;
148         gchar buf[256];
149
150         va_start(args, format);
151         g_vsnprintf(buf, sizeof(buf), format, args);
152         va_end(args);
153         strretchomp(buf);
154
155         alertpanel_message(_("Warning"), buf, ALERT_WARNING);
156 }
157
158 void alertpanel_error(const gchar *format, ...)
159 {
160         va_list args;
161         gchar buf[512];
162
163         va_start(args, format);
164         g_vsnprintf(buf, sizeof(buf), format, args);
165         va_end(args);
166         strretchomp(buf);
167
168         alertpanel_message(_("Error"), buf, ALERT_ERROR);
169 }
170
171 /*!
172  *\brief        display an error with a View Log button
173  *
174  */
175 void alertpanel_error_log(const gchar *format, ...)
176 {
177         va_list args;
178         int val;
179         MainWindow *mainwin;
180         gchar buf[256];
181
182         va_start(args, format);
183         g_vsnprintf(buf, sizeof(buf), format, args);
184         va_end(args);
185         strretchomp(buf);
186
187         mainwin = mainwindow_get_mainwindow();
188         
189         if (mainwin && mainwin->logwin) {
190                 mainwindow_clear_error(mainwin);
191                 val = alertpanel_full(_("Error"), buf, GTK_STOCK_CLOSE,
192                                       _("_View log"), NULL, FALSE, NULL,
193                                       ALERT_ERROR, G_ALERTDEFAULT);
194                 if (val == G_ALERTALTERNATE)
195                         log_window_show(mainwin->logwin);
196         } else
197                 alertpanel_error(buf);
198 }
199
200 static void alertpanel_show(void)
201 {
202         gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
203         manage_window_set_transient(GTK_WINDOW(dialog));
204         value = G_ALERTWAIT;
205
206         if (gdk_pointer_is_grabbed())
207                 gdk_pointer_ungrab(GDK_CURRENT_TIME);
208         inc_lock();
209         while ((value & G_ALERT_VALUE_MASK) == G_ALERTWAIT)
210                 gtk_main_iteration();
211
212         gtk_widget_destroy(dialog);
213         GTK_EVENTS_FLUSH();
214
215         alertpanel_is_open = FALSE;
216         inc_unlock();
217 }
218
219 static void alertpanel_create(const gchar *title,
220                               const gchar *message,
221                               const gchar *button1_label,
222                               const gchar *button2_label,
223                               const gchar *button3_label,
224                               gboolean     can_disable,
225                               GtkWidget   *custom_widget,
226                               gint         alert_type,
227                               AlertValue   default_value)
228 {
229         static PangoFontDescription *font_desc;
230         GtkWidget *image;
231         GtkWidget *label;
232         GtkWidget *hbox;
233         GtkWidget *vbox;
234         GtkWidget *disable_checkbtn;
235         GtkWidget *confirm_area;
236         GtkWidget *button1;
237         GtkWidget *button2;
238         GtkWidget *button3;
239         const gchar *label2;
240         const gchar *label3;
241         gchar *tmp = title?g_markup_printf_escaped("%s", title)
242                         :g_strdup("");
243         gchar *title_full = g_strdup_printf("<span weight=\"bold\" "
244                                 "size=\"larger\">%s</span>",
245                                 tmp);
246         g_free(tmp);
247         debug_print("Creating alert panel dialog...\n");
248
249         dialog = gtk_dialog_new();
250         gtk_window_set_title(GTK_WINDOW(dialog), title);
251         gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
252
253         gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
254         gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
255         
256         gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
257         g_signal_connect(G_OBJECT(dialog), "delete_event",
258                          G_CALLBACK(alertpanel_deleted),
259                          (gpointer)G_ALERTCANCEL);
260         g_signal_connect(G_OBJECT(dialog), "key_press_event",
261                          G_CALLBACK(alertpanel_close),
262                          (gpointer)G_ALERTCANCEL);
263
264         /* for title icon, label and message */
265         hbox = gtk_hbox_new(FALSE, 12);
266         gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
267         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
268                            hbox, FALSE, FALSE, 0);
269
270         /* title icon */
271         switch (alert_type) {
272         case ALERT_QUESTION:
273                 image = gtk_image_new_from_stock
274                         (GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
275                 break;
276         case ALERT_WARNING:
277                 image = gtk_image_new_from_stock
278                         (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
279                 break;
280         case ALERT_ERROR:
281                 image = gtk_image_new_from_stock
282                         (GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
283                 break;
284         case ALERT_NOTICE:
285         default:
286                 image = gtk_image_new_from_stock
287                         (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
288                 break;
289         }
290         gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0);
291         gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
292
293         vbox = gtk_vbox_new (FALSE, 12);
294         gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
295         gtk_widget_show (vbox);
296         
297         label = gtk_label_new(title_full);
298         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
299         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
300         gtk_label_set_use_markup(GTK_LABEL (label), TRUE);
301         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
302         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
303         if (!font_desc) {
304                 gint size;
305
306                 size = pango_font_description_get_size
307                         (label->style->font_desc);
308                 font_desc = pango_font_description_new();
309                 pango_font_description_set_weight
310                         (font_desc, PANGO_WEIGHT_BOLD);
311                 pango_font_description_set_size
312                         (font_desc, size * PANGO_SCALE_LARGE);
313         }
314         if (font_desc)
315                 gtk_widget_modify_font(label, font_desc);
316         g_free(title_full);
317         
318         label = gtk_label_new(message);
319         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
320         gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
321         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
322         gtk_label_set_selectable(GTK_LABEL(label), TRUE);
323         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
324         GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS);
325         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
326         gtk_widget_show(label);
327                 
328         /* Claws: custom widget */
329         if (custom_widget) {
330                 gtk_box_pack_start(GTK_BOX(vbox), custom_widget, FALSE,
331                                    FALSE, 0);
332         }
333
334         if (can_disable) {
335                 hbox = gtk_hbox_new(FALSE, 0);
336                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,
337                                    FALSE, FALSE, 0);
338
339                 disable_checkbtn = gtk_check_button_new_with_label
340                         (_("Show this message next time"));
341                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(disable_checkbtn),
342                                              TRUE);
343                 gtk_box_pack_start(GTK_BOX(hbox), disable_checkbtn,
344                                    FALSE, FALSE, 12);
345                 g_signal_connect(G_OBJECT(disable_checkbtn), "toggled",
346                                  G_CALLBACK(alertpanel_button_toggled),
347                                  GUINT_TO_POINTER(G_ALERTDISABLE));
348         }
349
350         /* for button(s) */
351         if (!button1_label)
352                 button1_label = GTK_STOCK_OK;
353         label2 = button2_label;
354         label3 = button3_label;
355         if (label2 && *label2 == '+') label2++;
356         if (label3 && *label3 == '+') label3++;
357
358         gtkut_stock_button_set_create(&confirm_area,
359                                       &button1, button1_label,
360                                       button2_label ? &button2 : NULL, label2,
361                                       button3_label ? &button3 : NULL, label3);
362
363         gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->action_area),
364                          confirm_area, FALSE, FALSE, 0);
365         gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5);
366         gtk_widget_grab_default(button1);
367         gtk_widget_grab_focus(button1);
368         if (button2_label &&
369             (default_value == G_ALERTALTERNATE || *button2_label == '+')) {
370                 gtk_widget_grab_default(button2);
371                 gtk_widget_grab_focus(button2);
372         }
373         if (button3_label &&
374             (default_value == G_ALERTOTHER || *button3_label == '+')) {
375                 gtk_widget_grab_default(button3);
376                 gtk_widget_grab_focus(button3);
377         }
378
379         g_signal_connect(G_OBJECT(button1), "clicked",
380                          G_CALLBACK(alertpanel_button_clicked),
381                          GUINT_TO_POINTER(G_ALERTDEFAULT));
382         if (button2_label)
383                 g_signal_connect(G_OBJECT(button2), "clicked",
384                                  G_CALLBACK(alertpanel_button_clicked),
385                                  GUINT_TO_POINTER(G_ALERTALTERNATE));
386         if (button3_label)
387                 g_signal_connect(G_OBJECT(button3), "clicked",
388                                  G_CALLBACK(alertpanel_button_clicked),
389                                  GUINT_TO_POINTER(G_ALERTOTHER));
390
391         gtk_widget_show_all(dialog);
392 }
393
394 static void alertpanel_button_toggled(GtkToggleButton *button,
395                                       gpointer data)
396 {
397         if (gtk_toggle_button_get_active(button))
398                 value &= ~GPOINTER_TO_UINT(data);
399         else
400                 value |= GPOINTER_TO_UINT(data);
401 }
402
403 static void alertpanel_button_clicked(GtkWidget *widget, gpointer data)
404 {
405         value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data;
406 }
407
408 static gint alertpanel_deleted(GtkWidget *widget, GdkEventAny *event,
409                                gpointer data)
410 {
411         value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data;
412         return TRUE;
413 }
414
415 static gboolean alertpanel_close(GtkWidget *widget, GdkEventAny *event,
416                                  gpointer data)
417 {
418         if (event->type == GDK_KEY_PRESS)
419                 if (((GdkEventKey *)event)->keyval != GDK_Escape)
420                         return FALSE;
421
422         value = (value & ~G_ALERT_VALUE_MASK) | (AlertValue)data;
423         return FALSE;
424 }