talons

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

inputdialog.c (14521B)


      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 <glib.h>
     20 #include <glib/gi18n.h>
     21 #include <gdk/gdkkeysyms.h>
     22 #include <gtk/gtk.h>
     23 
     24 #include "inputdialog.h"
     25 #include "manage_window.h"
     26 #include "gtkutils.h"
     27 #include "utils.h"
     28 #include "combobox.h"
     29 #include "prefs_common.h"
     30 
     31 
     32 #define INPUT_DIALOG_WIDTH	420
     33 
     34 typedef enum
     35 {
     36 	INPUT_DIALOG_NORMAL,
     37 	INPUT_DIALOG_INVISIBLE,
     38 	INPUT_DIALOG_COMBO
     39 } InputDialogType;
     40 
     41 static gboolean ack;
     42 static gboolean fin;
     43 
     44 static InputDialogType type;
     45 
     46 static GtkWidget *dialog;
     47 static GtkWidget *msg_title;
     48 static GtkWidget *msg_label;
     49 static GtkWidget *entry;
     50 static GtkWidget *combo;
     51 static GtkWidget *remember_checkbtn;
     52 static GtkWidget *ok_button;
     53 static GtkWidget *icon_q, *icon_p;
     54 static gboolean is_pass = FALSE;
     55 static void input_dialog_create	(gboolean is_password);
     56 static gchar *input_dialog_open	(const gchar	*title,
     57 				 const gchar	*message,
     58 				 const gchar  *checkbtn_label,
     59 				 const gchar	*default_string,
     60 				 gboolean default_checkbtn_state,
     61 				 gboolean	*remember);
     62 static void input_dialog_set	(const gchar	*title,
     63 				 const gchar	*message,
     64 				 const gchar	*default_string);
     65 
     66 static void ok_clicked		(GtkWidget	*widget,
     67 				 gpointer	 data);
     68 static void cancel_clicked	(GtkWidget	*widget,
     69 				 gpointer	 data);
     70 static gint delete_event	(GtkWidget	*widget,
     71 				 GdkEventAny	*event,
     72 				 gpointer	 data);
     73 static gboolean key_pressed	(GtkWidget	*widget,
     74 				 GdkEventKey	*event,
     75 				 gpointer	 data);
     76 static void entry_activated	(GtkEditable	*editable);
     77 static void combo_activated	(GtkEditable	*editable);
     78 
     79 
     80 gchar *input_dialog(const gchar *title, const gchar *message,
     81 		    const gchar *default_string)
     82 {
     83 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
     84 
     85 	if (!dialog)
     86 		input_dialog_create(FALSE);
     87 
     88 	type = INPUT_DIALOG_NORMAL;
     89 	gtk_widget_hide(combo);
     90 	gtk_widget_show(entry);
     91 
     92 	gtk_widget_hide(remember_checkbtn);
     93 
     94 	gtk_widget_show(icon_q);
     95 	gtk_widget_hide(icon_p);
     96 	is_pass = FALSE;
     97 	gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
     98 
     99 	return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
    100 }
    101 
    102 gchar *input_dialog_with_invisible(const gchar *title, const gchar *message,
    103 				   const gchar *default_string)
    104 {
    105 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
    106 
    107 	if (!dialog)
    108 		input_dialog_create(TRUE);
    109 
    110 	type = INPUT_DIALOG_INVISIBLE;
    111 	gtk_widget_hide(combo);
    112 	gtk_widget_show(entry);
    113 	gtk_widget_hide(remember_checkbtn);
    114 
    115 	gtk_widget_hide(icon_q);
    116 	gtk_widget_show(icon_p);
    117 	is_pass = TRUE;
    118 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
    119 
    120 	return input_dialog_open(title, message, NULL, default_string, FALSE, NULL);
    121 }
    122 
    123 gchar *input_dialog_with_invisible_checkbtn(const gchar *title, const gchar *message,
    124 				   const gchar *default_string, const gchar *checkbtn_label,
    125 				   gboolean *checkbtn_state)
    126 {
    127 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
    128 
    129 	if (!dialog)
    130 		input_dialog_create(TRUE);
    131 
    132 	type = INPUT_DIALOG_INVISIBLE;
    133 	gtk_widget_hide(combo);
    134 	gtk_widget_show(entry);
    135 
    136 	if (checkbtn_label && checkbtn_state) {
    137 		gtk_widget_show(remember_checkbtn);
    138 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), *checkbtn_state);
    139 	}
    140 	else {
    141 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), FALSE);
    142 		gtk_widget_hide(remember_checkbtn);
    143 	}
    144 
    145 	gtk_widget_hide(icon_q);
    146 	gtk_widget_show(icon_p);
    147 	is_pass = TRUE;
    148 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
    149 
    150 	return input_dialog_open(title, message, checkbtn_label,
    151 				 default_string, (checkbtn_state? *checkbtn_state:FALSE),
    152 				 checkbtn_state);
    153 }
    154 
    155 gchar *input_dialog_combo(const gchar *title, const gchar *message,
    156 			  const gchar *default_string, GList *list)
    157 {
    158 	return input_dialog_combo_remember(title, message,
    159 		default_string, list, FALSE);
    160 }
    161 
    162 gchar *input_dialog_combo_remember(const gchar *title, const gchar *message,
    163 			  const gchar *default_string, GList *list,
    164 			  gboolean *remember)
    165 {
    166 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
    167 
    168 	if (!dialog)
    169 		input_dialog_create(FALSE);
    170 
    171 	type = INPUT_DIALOG_COMBO;
    172 	gtk_widget_hide(entry);
    173 	gtk_widget_show(combo);
    174 
    175 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn), FALSE);
    176 	if (remember)
    177 		gtk_widget_show(remember_checkbtn);
    178 	else
    179 		gtk_widget_hide(remember_checkbtn);
    180 
    181 	gtk_widget_show(icon_q);
    182 	gtk_widget_hide(icon_p);
    183 	is_pass = FALSE;
    184 	combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(combo));
    185 	combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(combo), list);
    186 	return input_dialog_open(title, message, NULL, default_string, FALSE, remember);
    187 }
    188 
    189 gchar *input_dialog_with_checkbtn(const gchar	*title,
    190 				   const gchar	*message,
    191 				   const gchar	*default_string,
    192 				   const gchar  *checkbtn_label,
    193 				   gboolean *checkbtn_state)
    194 {
    195 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
    196 
    197 	if (!dialog)
    198 		input_dialog_create(FALSE);
    199 
    200 	type = INPUT_DIALOG_NORMAL;
    201 	gtk_widget_hide(combo);
    202 	gtk_widget_show(entry);
    203 
    204 	if (checkbtn_label && checkbtn_state)
    205 		gtk_widget_show(remember_checkbtn);
    206 	else
    207 		gtk_widget_hide(remember_checkbtn);
    208 
    209 	gtk_widget_show(icon_q);
    210 	gtk_widget_hide(icon_p);
    211 	is_pass = FALSE;
    212 	gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE);
    213 
    214 	return input_dialog_open(title, message, checkbtn_label, default_string,
    215 	       			 prefs_common.inherit_folder_props, checkbtn_state);
    216 }
    217 
    218 gchar *input_dialog_query_password(const gchar *server, const gchar *user)
    219 {
    220 	gchar *message;
    221 	gchar *pass;
    222 
    223 	if (server && user)
    224 		message = g_strdup_printf(_("Input password for %s on %s"),
    225 				  user, server);
    226 	else if (server)
    227 		message = g_strdup_printf(_("Input password for %s"),
    228 				  server);
    229 	else if (user)
    230 		message = g_strdup_printf(_("Input password for %s"),
    231 				  user);
    232 	else
    233 		message = g_strdup_printf(_("Input password"));
    234 	pass = input_dialog_with_invisible(_("Input password"), message, NULL);
    235 	g_free(message);
    236 
    237 	return pass;
    238 }
    239 
    240 gchar *input_dialog_query_password_keep(const gchar *server, const gchar *user, gchar **keep)
    241 {
    242 	gchar *message;
    243 	gchar *pass;
    244 
    245 	if (server && user)
    246 		message = g_strdup_printf(_("Input password for %s on %s"),
    247 				  user, server);
    248 	else if (server)
    249 		message = g_strdup_printf(_("Input password for %s"),
    250 				  server);
    251 	else if (user)
    252 		message = g_strdup_printf(_("Input password for %s"),
    253 				  user);
    254 	else
    255 		message = g_strdup_printf(_("Input password"));
    256         if (keep) {
    257 		if (*keep != NULL) {
    258 			pass = g_strdup (*keep);
    259 		}
    260 		else {
    261 			gboolean state = prefs_common.session_passwords;
    262 			pass = input_dialog_with_invisible_checkbtn(_("Input password"),
    263 					message, NULL,
    264 					_("Remember password for this session"),
    265 					&state);
    266 			if (state) {
    267 				*keep = g_strdup (pass);
    268 				debug_print("keeping session password for account\n");
    269 			}
    270 			prefs_common.session_passwords = state;
    271 		}
    272 	}
    273 	else {
    274 		pass = input_dialog_with_invisible(_("Input password"), message, NULL);
    275 	}
    276 	g_free(message);
    277 
    278 	return pass;
    279 }
    280 
    281 static void input_dialog_create(gboolean is_password)
    282 {
    283 	static PangoFontDescription *font_desc;
    284 	GtkWidget *hbox;
    285 	GtkWidget *vbox;
    286 	GtkWidget *cancel_button;
    287 
    288 	dialog = gtk_dialog_new();
    289 
    290 	gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
    291 	gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100);
    292 	gtk_window_set_title(GTK_WINDOW(dialog), "");
    293 
    294 	g_signal_connect(G_OBJECT(dialog), "delete_event",
    295 			 G_CALLBACK(delete_event), NULL);
    296 	g_signal_connect(G_OBJECT(dialog), "key_press_event",
    297 			 G_CALLBACK(key_pressed), NULL);
    298 	MANAGE_WINDOW_SIGNALS_CONNECT(dialog);
    299 
    300 	vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
    301 	gtk_box_set_spacing (GTK_BOX (vbox), 14);
    302 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
    303 	gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
    304 	gtk_widget_show (hbox);
    305 	gtk_box_pack_start (GTK_BOX (vbox), hbox,
    306 			    FALSE, FALSE, 0);
    307 
    308 	/* for title label */
    309 	icon_q = gtk_image_new_from_icon_name("dialog-question-symbolic",
    310         			GTK_ICON_SIZE_DIALOG);
    311 	gtk_widget_set_halign(icon_q, GTK_ALIGN_CENTER);
    312 	gtk_widget_set_valign(icon_q, GTK_ALIGN_START);
    313 	gtk_box_pack_start (GTK_BOX (hbox), icon_q, FALSE, FALSE, 0);
    314 	icon_p = gtk_image_new_from_icon_name("dialog-password-symbolic",
    315         			GTK_ICON_SIZE_DIALOG);
    316 	gtk_widget_set_halign(icon_p, GTK_ALIGN_CENTER);
    317 	gtk_widget_set_valign(icon_p, GTK_ALIGN_START);
    318 	gtk_box_pack_start (GTK_BOX (hbox), icon_p, FALSE, FALSE, 0);
    319 
    320 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
    321 	gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 16);
    322 	gtk_widget_show (vbox);
    323 
    324 	msg_title = gtk_label_new("");
    325 	gtk_label_set_xalign(GTK_LABEL(msg_title), 0.0);
    326 	gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT);
    327 	gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE);
    328 	gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0);
    329 	gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE);
    330 	if (!font_desc) {
    331 		gint size;
    332 
    333 		size = pango_font_description_get_size
    334 			(gtk_widget_get_style(msg_title)->font_desc);
    335 		font_desc = pango_font_description_new();
    336 		pango_font_description_set_weight
    337 			(font_desc, PANGO_WEIGHT_BOLD);
    338 		pango_font_description_set_size
    339 			(font_desc, size * PANGO_SCALE_LARGE);
    340 	}
    341 	if (font_desc)
    342 		gtk_widget_override_font(msg_title, font_desc);
    343 
    344 	msg_label = gtk_label_new("");
    345 	gtk_label_set_xalign(GTK_LABEL(msg_label), 0.0);
    346 	gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT);
    347 	gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0);
    348 	gtk_widget_show(msg_label);
    349 
    350 	entry = gtk_entry_new();
    351 	gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);
    352 	g_signal_connect(G_OBJECT(entry), "activate",
    353 			 G_CALLBACK(entry_activated), NULL);
    354 
    355 	combo = gtk_combo_box_text_new_with_entry();
    356 	gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
    357 	g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN((combo)))), "activate",
    358 			 G_CALLBACK(combo_activated), NULL);
    359 
    360 	remember_checkbtn = gtk_check_button_new_with_label(_("Remember this"));
    361 	gtk_box_pack_start(GTK_BOX(vbox), remember_checkbtn, FALSE, FALSE, 0);
    362 
    363 	cancel_button = gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Cancel"),
    364 					      GTK_RESPONSE_NONE);
    365 	ok_button = gtk_dialog_add_button(GTK_DIALOG(dialog),_("_OK"),
    366 					  GTK_RESPONSE_NONE);
    367 
    368 	gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
    369 
    370 	gtk_widget_hide(remember_checkbtn);
    371 
    372 	if (is_password)
    373 		gtk_widget_hide(icon_q);
    374 	else
    375 		gtk_widget_hide(icon_p);
    376 
    377 	is_pass = is_password;
    378 
    379 	gtk_widget_grab_default(ok_button);
    380 
    381 	g_signal_connect(G_OBJECT(ok_button), "clicked",
    382 			 G_CALLBACK(ok_clicked), NULL);
    383 	g_signal_connect(G_OBJECT(cancel_button), "clicked",
    384 			 G_CALLBACK(cancel_clicked), NULL);
    385 }
    386 
    387 static gchar *input_dialog_open(const gchar *title, const gchar *message,
    388 				const gchar *checkbtn_label,
    389 				const gchar *default_string,
    390 				gboolean default_checkbtn_state,
    391 				gboolean *remember)
    392 {
    393 	gchar *str;
    394 
    395 	if (dialog && gtk_widget_get_visible(dialog)) return NULL;
    396 
    397 	if (!dialog)
    398 		input_dialog_create(FALSE);
    399 
    400 	if (checkbtn_label)
    401 		gtk_button_set_label(GTK_BUTTON(remember_checkbtn), checkbtn_label);
    402 	else
    403 		gtk_button_set_label(GTK_BUTTON(remember_checkbtn), _("Remember this"));
    404 
    405 	input_dialog_set(title, message, default_string);
    406 	gtk_window_present(GTK_WINDOW(dialog));
    407 
    408 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(remember_checkbtn),
    409 				     default_checkbtn_state);
    410 	if (remember)
    411 		gtk_widget_show(remember_checkbtn);
    412 	else
    413 		gtk_widget_hide(remember_checkbtn);
    414 
    415 	manage_window_set_transient(GTK_WINDOW(dialog));
    416 	gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
    417 
    418 	ack = fin = FALSE;
    419 
    420 	while (fin == FALSE)
    421 		gtk_main_iteration();
    422 
    423 	manage_window_focus_out(dialog, NULL, NULL);
    424 
    425 	if (ack) {
    426 		GtkEditable *editable;
    427 
    428 		if (type == INPUT_DIALOG_COMBO)
    429 			editable = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN((combo))));
    430 		else
    431 			editable = GTK_EDITABLE(entry);
    432 
    433 		str = gtk_editable_get_chars(editable, 0, -1);
    434 		if (str && *str == '\0' && !is_pass) {
    435 			g_free(str);
    436 			str = NULL;
    437 		}
    438 	} else
    439 		str = NULL;
    440 
    441 	GTK_EVENTS_FLUSH();
    442 
    443 	if (remember) {
    444 		*remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(remember_checkbtn));
    445 	}
    446 
    447 	gtk_widget_destroy(dialog);
    448 	dialog = NULL;
    449 
    450 	if (is_pass)
    451 		debug_print("return string = %s\n", str ? "********": ("none"));
    452 	else
    453 		debug_print("return string = %s\n", str ? str : "(none)");
    454 	return str;
    455 }
    456 
    457 static void input_dialog_set(const gchar *title, const gchar *message,
    458 			     const gchar *default_string)
    459 {
    460 	GtkWidget *entry_;
    461 
    462 	if (type == INPUT_DIALOG_COMBO)
    463 		entry_ = gtk_bin_get_child(GTK_BIN((combo)));
    464 	else
    465 		entry_ = entry;
    466 
    467 	gtk_window_set_title(GTK_WINDOW(dialog), title);
    468 	gtk_label_set_text(GTK_LABEL(msg_title), title);
    469 	gtk_label_set_text(GTK_LABEL(msg_label), message);
    470 	if (default_string && *default_string) {
    471 		gtk_entry_set_text(GTK_ENTRY(entry_), default_string);
    472 		gtk_editable_set_position(GTK_EDITABLE(entry_), 0);
    473 		gtk_editable_select_region(GTK_EDITABLE(entry_), 0, -1);
    474 	} else
    475 		gtk_entry_set_text(GTK_ENTRY(entry_), "");
    476 
    477 	gtk_widget_grab_focus(ok_button);
    478 	gtk_widget_grab_focus(entry_);
    479 }
    480 
    481 static void ok_clicked(GtkWidget *widget, gpointer data)
    482 {
    483 	ack = TRUE;
    484 	fin = TRUE;
    485 }
    486 
    487 static void cancel_clicked(GtkWidget *widget, gpointer data)
    488 {
    489 	ack = FALSE;
    490 	fin = TRUE;
    491 }
    492 
    493 static gint delete_event(GtkWidget *widget, GdkEventAny *event, gpointer data)
    494 {
    495 	ack = FALSE;
    496 	fin = TRUE;
    497 
    498 	return TRUE;
    499 }
    500 
    501 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
    502 {
    503 	if (event && (event->keyval == GDK_KEY_KP_Enter ||
    504 		   event->keyval == GDK_KEY_Return)) {
    505 		ack = TRUE;
    506 		fin = TRUE;
    507 		return TRUE; /* do not let Return pass - it
    508 			      * pops up the combo on validating */
    509 	}
    510 
    511 	return FALSE;
    512 }
    513 
    514 static void entry_activated(GtkEditable *editable)
    515 {
    516 	ack = TRUE;
    517 	fin = TRUE;
    518 }
    519 
    520 static void combo_activated(GtkEditable *editable)
    521 {
    522 	ack = TRUE;
    523 	fin = TRUE;
    524 }