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