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 }