talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

alertpanel.c (13184B)


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