Add UI elements to control single-line regex settings
[geany-mirror.git] / src / dialogs.c
blob645f374bfc197f787702a1db5a9b7fbf47fee117
1 /*
2 * dialogs.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2012 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2012 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * File related dialogs, miscellaneous dialogs, font dialog.
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include "dialogs.h"
32 #include "app.h"
33 #include "build.h"
34 #include "document.h"
35 #include "encodings.h"
36 #include "filetypes.h"
37 #include "main.h"
38 #include "support.h"
39 #include "utils.h"
40 #include "ui_utils.h"
42 #include "gtkcompat.h"
44 #include <gdk/gdkkeysyms.h>
45 #include <string.h>
47 #ifdef HAVE_SYS_TIME_H
48 # include <sys/time.h>
49 #endif
50 #include <time.h>
52 #ifdef HAVE_SYS_TYPES_H
53 # include <sys/types.h>
54 #endif
56 /* gstdio.h also includes sys/stat.h */
57 #include <glib/gstdio.h>
60 enum
62 GEANY_RESPONSE_RENAME,
63 GEANY_RESPONSE_VIEW
67 static struct FileSelState
69 struct
71 guint filter_idx;
72 gint encoding_idx;
73 gint filetype_idx;
74 gboolean show_hidden;
75 gboolean more_options_visible;
76 } open;
78 filesel_state = {
81 GEANY_ENCODINGS_MAX, /* default encoding is detect from file */
82 GEANY_FILETYPES_NONE, /* default filetype is detect from extension */
83 FALSE,
84 FALSE
89 static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo);
92 /* gets the ID of the current file filter */
93 static guint file_chooser_get_filter_idx(GtkFileChooser *chooser)
95 guint idx = 0;
96 GtkFileFilter *current;
97 GSList *filters, *item;
99 current = gtk_file_chooser_get_filter(chooser);
100 filters = gtk_file_chooser_list_filters(chooser);
101 foreach_slist(item, filters)
103 if (item->data == current)
104 break;
105 idx ++;
107 g_slist_free(filters);
108 return idx;
112 /* sets the current file filter from its ID */
113 static void file_chooser_set_filter_idx(GtkFileChooser *chooser, guint idx)
115 GtkFileFilter *current;
116 GSList *filters;
118 filters = gtk_file_chooser_list_filters(chooser);
119 current = g_slist_nth_data(filters, idx);
120 g_slist_free(filters);
121 gtk_file_chooser_set_filter(chooser, current);
125 static void open_file_dialog_handle_response(GtkWidget *dialog, gint response)
127 if (response == GTK_RESPONSE_ACCEPT || response == GEANY_RESPONSE_VIEW)
129 GSList *filelist;
130 GeanyFiletype *ft = NULL;
131 const gchar *charset = NULL;
132 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
133 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
134 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
135 gboolean ro = (response == GEANY_RESPONSE_VIEW); /* View clicked */
137 filesel_state.open.more_options_visible = gtk_expander_get_expanded(GTK_EXPANDER(expander));
138 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
139 filesel_state.open.filetype_idx = filetype_combo_box_get_active_filetype(GTK_COMBO_BOX(filetype_combo));
141 /* ignore detect from file item */
142 if (filesel_state.open.filetype_idx > 0)
143 ft = filetypes_index(filesel_state.open.filetype_idx);
145 filesel_state.open.encoding_idx = ui_encodings_combo_box_get_active_encoding(GTK_COMBO_BOX(encoding_combo));
146 if (filesel_state.open.encoding_idx >= 0 && filesel_state.open.encoding_idx < GEANY_ENCODINGS_MAX)
147 charset = encodings[filesel_state.open.encoding_idx].charset;
149 filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
150 if (filelist != NULL)
152 document_open_files(filelist, ro, ft, charset);
153 g_slist_foreach(filelist, (GFunc) g_free, NULL); /* free filenames */
155 g_slist_free(filelist);
157 if (app->project && !EMPTY(app->project->base_path))
158 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
159 app->project->base_path, NULL);
163 static void on_file_open_show_hidden_notify(GObject *filechooser,
164 GParamSpec *pspec, gpointer data)
166 GtkWidget *toggle_button;
168 toggle_button = ui_lookup_widget(GTK_WIDGET(filechooser), "check_hidden");
170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button),
171 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser)));
175 static void
176 on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, GtkWidget *dialog)
178 filesel_state.open.show_hidden = gtk_toggle_button_get_active(togglebutton);
179 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), filesel_state.open.show_hidden);
183 static void filetype_combo_cell_data_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell,
184 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
186 gboolean sensitive = !gtk_tree_model_iter_has_child(tree_model, iter);
187 gchar *text;
189 gtk_tree_model_get(tree_model, iter, 1, &text, -1);
190 g_object_set(cell, "sensitive", sensitive, "text", text, NULL);
191 g_free(text);
195 static GtkWidget *create_filetype_combo_box(void)
197 GtkTreeStore *store;
198 GtkTreeIter iter_compiled, iter_script, iter_markup, iter_misc, iter_detect;
199 GtkTreeIter *iter_parent;
200 GtkWidget *combo;
201 GtkCellRenderer *renderer;
202 GSList *node;
204 store = gtk_tree_store_new(2, G_TYPE_INT, G_TYPE_STRING);
206 gtk_tree_store_insert_with_values(store, &iter_detect, NULL, -1,
207 0, GEANY_FILETYPES_NONE, 1, _("Detect from file"), -1);
209 gtk_tree_store_insert_with_values(store, &iter_compiled, NULL, -1,
210 0, -1, 1, _("Programming Languages"), -1);
211 gtk_tree_store_insert_with_values(store, &iter_script, NULL, -1,
212 0, -1, 1, _("Scripting Languages"), -1);
213 gtk_tree_store_insert_with_values(store, &iter_markup, NULL, -1,
214 0, -1, 1, _("Markup Languages"), -1);
215 gtk_tree_store_insert_with_values(store, &iter_misc, NULL, -1,
216 0, -1, 1, _("Miscellaneous"), -1);
218 foreach_slist (node, filetypes_by_title)
220 GeanyFiletype *ft = node->data;
222 if (ft->id == GEANY_FILETYPES_NONE)
223 continue;
225 switch (ft->group)
227 case GEANY_FILETYPE_GROUP_COMPILED: iter_parent = &iter_compiled; break;
228 case GEANY_FILETYPE_GROUP_SCRIPT: iter_parent = &iter_script; break;
229 case GEANY_FILETYPE_GROUP_MARKUP: iter_parent = &iter_markup; break;
230 case GEANY_FILETYPE_GROUP_MISC: iter_parent = &iter_misc; break;
231 case GEANY_FILETYPE_GROUP_NONE:
232 default: iter_parent = NULL;
234 gtk_tree_store_insert_with_values(store, NULL, iter_parent, -1,
235 0, (gint) ft->id, 1, ft->title, -1);
238 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
239 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter_detect);
240 renderer = gtk_cell_renderer_text_new();
241 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
242 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
243 filetype_combo_cell_data_func, NULL, NULL);
245 g_object_unref(store);
247 return combo;
251 static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo)
253 gint id = 0;
254 GtkTreeIter iter;
256 if (gtk_combo_box_get_active_iter(combo, &iter))
258 GtkTreeModel *model = gtk_combo_box_get_model(combo);
259 gtk_tree_model_get(model, &iter, 0, &id, -1);
261 return id;
265 static gboolean filetype_combo_box_set_active_filetype(GtkComboBox *combo, const gint id)
267 GtkTreeModel *model = gtk_combo_box_get_model(combo);
268 GtkTreeIter iter;
270 if (gtk_tree_model_get_iter_first(model, &iter))
274 gint row_id;
275 gtk_tree_model_get(model, &iter, 0, &row_id, -1);
276 if (id == row_id)
278 gtk_combo_box_set_active_iter(combo, &iter);
279 return TRUE;
282 while (ui_tree_model_iter_any_next(model, &iter, TRUE));
284 return FALSE;
288 static GtkWidget *add_file_open_extra_widget(GtkWidget *dialog)
290 GtkWidget *expander, *vbox, *table, *check_hidden;
291 GtkWidget *filetype_ebox, *filetype_label, *filetype_combo;
292 GtkWidget *encoding_ebox, *encoding_label, *encoding_combo;
294 expander = gtk_expander_new_with_mnemonic(_("_More Options"));
295 vbox = gtk_vbox_new(FALSE, 6);
296 gtk_container_add(GTK_CONTAINER(expander), vbox);
298 table = gtk_table_new(2, 4, FALSE);
300 /* line 1 with checkbox and encoding combo */
301 check_hidden = gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
302 gtk_widget_show(check_hidden);
303 gtk_table_attach(GTK_TABLE(table), check_hidden, 0, 1, 0, 1,
304 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
305 (GtkAttachOptions) (0), 0, 5);
307 /* spacing */
308 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""), 1, 2, 0, 1,
309 (GtkAttachOptions) (GTK_FILL),
310 (GtkAttachOptions) (0), 5, 5);
312 encoding_label = gtk_label_new(_("Set encoding:"));
313 gtk_misc_set_alignment(GTK_MISC(encoding_label), 1, 0);
314 gtk_table_attach(GTK_TABLE(table), encoding_label, 2, 3, 0, 1,
315 (GtkAttachOptions) (GTK_FILL),
316 (GtkAttachOptions) (0), 4, 5);
317 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
318 encoding_ebox = gtk_event_box_new();
319 encoding_combo = ui_create_encodings_combo_box(TRUE, GEANY_ENCODINGS_MAX);
320 gtk_widget_set_tooltip_text(encoding_ebox,
321 _("Explicitly defines an encoding for the file, if it would not be detected. This is useful when you know that the encoding of a file cannot be detected correctly by Geany.\nNote if you choose multiple files, they will all be opened with the chosen encoding."));
322 gtk_container_add(GTK_CONTAINER(encoding_ebox), encoding_combo);
323 gtk_table_attach(GTK_TABLE(table), encoding_ebox, 3, 4, 0, 1,
324 (GtkAttachOptions) (GTK_FILL),
325 (GtkAttachOptions) (0), 0, 5);
327 /* line 2 with filetype combo */
328 filetype_label = gtk_label_new(_("Set filetype:"));
329 gtk_misc_set_alignment(GTK_MISC(filetype_label), 1, 0);
330 gtk_table_attach(GTK_TABLE(table), filetype_label, 2, 3, 1, 2,
331 (GtkAttachOptions) (GTK_FILL),
332 (GtkAttachOptions) (0), 4, 5);
333 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
334 filetype_ebox = gtk_event_box_new();
335 filetype_combo = create_filetype_combo_box();
336 gtk_widget_set_tooltip_text(filetype_ebox,
337 _("Explicitly defines a filetype for the file, if it would not be detected by filename extension.\nNote if you choose multiple files, they will all be opened with the chosen filetype."));
338 gtk_container_add(GTK_CONTAINER(filetype_ebox), filetype_combo);
339 gtk_table_attach(GTK_TABLE(table), filetype_ebox, 3, 4, 1, 2,
340 (GtkAttachOptions) (GTK_FILL),
341 (GtkAttachOptions) (0), 0, 5);
343 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
344 gtk_widget_show_all(vbox);
346 g_signal_connect(check_hidden, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled), dialog);
348 ui_hookup_widget(dialog, expander, "more_options_expander");
349 ui_hookup_widget(dialog, check_hidden, "check_hidden");
350 ui_hookup_widget(dialog, filetype_combo, "filetype_combo");
351 ui_hookup_widget(dialog, encoding_combo, "encoding_combo");
353 return expander;
357 static GtkWidget *create_open_file_dialog(void)
359 GtkWidget *dialog;
360 GtkWidget *viewbtn;
361 GSList *node;
363 dialog = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets.window),
364 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
365 gtk_widget_set_name(dialog, "GeanyDialog");
367 viewbtn = gtk_dialog_add_button(GTK_DIALOG(dialog), C_("Open dialog action", "_View"), GEANY_RESPONSE_VIEW);
368 gtk_widget_set_tooltip_text(viewbtn,
369 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
371 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
372 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
373 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
374 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
376 gtk_widget_set_size_request(dialog, -1, 460);
377 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
378 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
379 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
380 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
381 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
382 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
383 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
385 /* add checkboxes and filename entry */
386 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), add_file_open_extra_widget(dialog));
388 /* add FileFilters(start with "All Files") */
389 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
390 filetypes_create_file_filter(filetypes[GEANY_FILETYPES_NONE]));
391 /* now create meta filter "All Source" */
392 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
393 filetypes_create_file_filter_all_source());
394 foreach_slist(node, filetypes_by_title)
396 GeanyFiletype *ft = node->data;
398 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
399 continue;
400 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filetypes_create_file_filter(ft));
403 g_signal_connect(dialog, "notify::show-hidden",
404 G_CALLBACK(on_file_open_show_hidden_notify), NULL);
406 return dialog;
410 static void open_file_dialog_apply_settings(GtkWidget *dialog)
412 static gboolean initialized = FALSE;
413 GtkWidget *check_hidden = ui_lookup_widget(dialog, "check_hidden");
414 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
415 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
416 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
418 /* we can't know the initial position of combo boxes, so retreive it the first time */
419 if (! initialized)
421 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
423 initialized = TRUE;
425 else
427 file_chooser_set_filter_idx(GTK_FILE_CHOOSER(dialog), filesel_state.open.filter_idx);
429 gtk_expander_set_expanded(GTK_EXPANDER(expander), filesel_state.open.more_options_visible);
430 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden), filesel_state.open.show_hidden);
431 ui_encodings_combo_box_set_active_encoding(GTK_COMBO_BOX(encoding_combo), filesel_state.open.encoding_idx);
432 filetype_combo_box_set_active_filetype(GTK_COMBO_BOX(filetype_combo), filesel_state.open.filetype_idx);
436 /* This shows the file selection dialog to open a file. */
437 void dialogs_show_open_file(void)
439 gchar *initdir;
441 /* set dialog directory to the current file's directory, if present */
442 initdir = utils_get_current_file_dir_utf8();
444 /* use project or default startup directory (if set) if no files are open */
445 /** TODO should it only be used when initally open the dialog and not on every show? */
446 if (! initdir)
447 initdir = g_strdup(utils_get_default_dir_utf8());
449 SETPTR(initdir, utils_get_locale_from_utf8(initdir));
451 #ifdef G_OS_WIN32
452 if (interface_prefs.use_native_windows_dialogs)
453 win32_show_document_open_dialog(GTK_WINDOW(main_widgets.window), _("Open File"), initdir);
454 else
455 #endif
457 GtkWidget *dialog = create_open_file_dialog();
458 gint response;
460 open_file_dialog_apply_settings(dialog);
462 if (initdir != NULL && g_path_is_absolute(initdir))
463 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), initdir);
465 if (app->project && !EMPTY(app->project->base_path))
466 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
467 app->project->base_path, NULL);
469 response = gtk_dialog_run(GTK_DIALOG(dialog));
470 open_file_dialog_handle_response(dialog, response);
471 gtk_widget_destroy(dialog);
473 g_free(initdir);
477 static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file)
479 GeanyDocument *doc = document_get_current();
480 gboolean success = FALSE;
482 g_return_val_if_fail(!EMPTY(utf8_filename), FALSE);
484 if (doc->file_name != NULL)
486 if (rename_file)
488 document_rename_file(doc, utf8_filename);
490 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
491 tm_workspace_remove_object(doc->tm_file, TRUE, TRUE);
492 doc->tm_file = NULL;
494 success = document_save_file_as(doc, utf8_filename);
496 build_menu_update(doc);
497 return success;
501 static gboolean save_as_dialog_handle_response(GtkWidget *dialog, gint response)
503 gboolean rename_file = FALSE;
504 gboolean success = FALSE;
505 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
507 switch (response)
509 case GEANY_RESPONSE_RENAME:
510 /* rename doesn't check for empty filename or overwriting */
511 if (G_UNLIKELY(EMPTY(new_filename)))
513 utils_beep();
514 break;
516 if (g_file_test(new_filename, G_FILE_TEST_EXISTS) &&
517 !dialogs_show_question_full(NULL, NULL, NULL,
518 _("Overwrite?"),
519 _("Filename already exists!")))
520 break;
521 rename_file = TRUE;
522 /* fall through */
523 case GTK_RESPONSE_ACCEPT:
525 gchar *utf8_filename;
527 utf8_filename = utils_get_utf8_from_locale(new_filename);
528 success = handle_save_as(utf8_filename, rename_file);
529 g_free(utf8_filename);
530 break;
532 case GTK_RESPONSE_DELETE_EVENT:
533 case GTK_RESPONSE_CANCEL:
534 success = TRUE;
535 break;
537 g_free(new_filename);
539 return success;
543 static GtkWidget *create_save_file_dialog(GeanyDocument *doc)
545 GtkWidget *dialog, *rename_btn;
546 const gchar *initdir;
548 dialog = gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets.window),
549 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
550 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
551 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
552 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
553 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
554 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
555 gtk_widget_set_name(dialog, "GeanyDialog");
557 rename_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), _("R_ename"), GEANY_RESPONSE_RENAME);
558 gtk_widget_set_tooltip_text(rename_btn, _("Save the file and rename it"));
559 /* disable rename unless file exists on disk */
560 gtk_widget_set_sensitive(rename_btn, doc->real_path != NULL);
562 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
563 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
564 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
565 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
567 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
568 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
570 /* set the folder by default to the project base dir or the global pref for opening files */
571 initdir = utils_get_default_dir_utf8();
572 if (initdir)
574 gchar *linitdir = utils_get_locale_from_utf8(initdir);
575 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), linitdir);
576 g_free(linitdir);
578 return dialog;
582 static gboolean show_save_as_gtk(GeanyDocument *doc)
584 GtkWidget *dialog;
585 gint resp;
587 g_return_val_if_fail(DOC_VALID(doc), FALSE);
589 dialog = create_save_file_dialog(doc);
591 if (doc->file_name != NULL)
593 if (g_path_is_absolute(doc->file_name))
595 gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name);
596 gchar *locale_basename = g_path_get_basename(locale_filename);
597 gchar *locale_dirname = g_path_get_dirname(locale_filename);
599 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
600 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), locale_basename);
602 g_free(locale_filename);
603 g_free(locale_basename);
604 g_free(locale_dirname);
606 else
607 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), doc->file_name);
609 else
611 gchar *fname = NULL;
613 if (doc->file_type != NULL && doc->file_type->extension != NULL)
614 fname = g_strconcat(GEANY_STRING_UNTITLED, ".",
615 doc->file_type->extension, NULL);
616 else
617 fname = g_strdup(GEANY_STRING_UNTITLED);
619 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
621 g_free(fname);
624 if (app->project && !EMPTY(app->project->base_path))
625 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
626 app->project->base_path, NULL);
628 /* Run the dialog synchronously, pausing this function call */
631 resp = gtk_dialog_run(GTK_DIALOG(dialog));
633 while (! save_as_dialog_handle_response(dialog, resp));
635 if (app->project && !EMPTY(app->project->base_path))
636 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
637 app->project->base_path, NULL);
639 gtk_widget_destroy(dialog);
641 return (resp == GTK_RESPONSE_ACCEPT);
646 * Shows the Save As dialog for the current notebook page.
648 * @return @c TRUE if the file was saved, otherwise @c FALSE.
650 gboolean dialogs_show_save_as(void)
652 GeanyDocument *doc = document_get_current();
653 gboolean result = FALSE;
655 g_return_val_if_fail(doc, FALSE);
657 #ifdef G_OS_WIN32
658 if (interface_prefs.use_native_windows_dialogs)
660 gchar *utf8_name = win32_show_document_save_as_dialog(GTK_WINDOW(main_widgets.window),
661 _("Save File"), doc);
662 if (utf8_name != NULL)
663 result = handle_save_as(utf8_name, FALSE);
665 else
666 #endif
667 result = show_save_as_gtk(doc);
668 return result;
672 #ifndef G_OS_WIN32
673 static void show_msgbox_dialog(GtkWidget *dialog, GtkMessageType type, GtkWindow *parent)
675 const gchar *title;
676 switch (type)
678 case GTK_MESSAGE_ERROR:
679 title = _("Error");
680 break;
681 case GTK_MESSAGE_QUESTION:
682 title = _("Question");
683 break;
684 case GTK_MESSAGE_WARNING:
685 title = _("Warning");
686 break;
687 default:
688 title = _("Information");
689 break;
691 gtk_window_set_title(GTK_WINDOW(dialog), title);
692 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
693 gtk_widget_set_name(dialog, "GeanyDialog");
695 gtk_dialog_run(GTK_DIALOG(dialog));
696 gtk_widget_destroy(dialog);
698 #endif
702 * Shows a message box of the type @a type with @a text.
703 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
704 * message dialog box is shown.
706 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
707 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
708 * @param text Printf()-style format string.
709 * @param ... Arguments for the @a text format string.
711 void dialogs_show_msgbox(GtkMessageType type, const gchar *text, ...)
713 #ifndef G_OS_WIN32
714 GtkWidget *dialog;
715 #endif
716 gchar *string;
717 va_list args;
718 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
720 va_start(args, text);
721 string = g_strdup_vprintf(text, args);
722 va_end(args);
724 #ifdef G_OS_WIN32
725 win32_message_dialog(GTK_WIDGET(parent), type, string);
726 #else
727 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
728 type, GTK_BUTTONS_OK, "%s", string);
729 show_msgbox_dialog(dialog, type, parent);
730 #endif
731 g_free(string);
735 void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
737 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
738 #ifdef G_OS_WIN32
739 /* put the two strings together because Windows message boxes don't support secondary texts */
740 gchar *string = g_strconcat(text, "\n", secondary, NULL);
741 win32_message_dialog(GTK_WIDGET(parent), type, string);
742 g_free(string);
743 #else
744 GtkWidget *dialog;
745 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
746 type, GTK_BUTTONS_OK, "%s", text);
747 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
748 show_msgbox_dialog(dialog, type, parent);
749 #endif
753 static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
755 GtkWidget *dialog, *button;
756 gint ret;
758 dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
759 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
760 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
761 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg2);
762 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
764 button = ui_button_new_with_image(GTK_STOCK_CLEAR, _("_Don't save"));
765 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
766 gtk_widget_show(button);
768 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
770 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
771 ret = gtk_dialog_run(GTK_DIALOG(dialog));
773 gtk_widget_destroy(dialog);
775 return ret;
779 gboolean dialogs_show_unsaved_file(GeanyDocument *doc)
781 gchar *msg, *short_fn = NULL;
782 const gchar *msg2;
783 gint response;
784 gboolean old_quitting_state = main_status.quitting;
786 /* display the file tab to remind the user of the document */
787 main_status.quitting = FALSE;
788 document_show_tab(doc);
789 main_status.quitting = old_quitting_state;
791 short_fn = document_get_basename_for_display(doc, -1);
793 msg = g_strdup_printf(_("The file '%s' is not saved."), short_fn);
794 msg2 = _("Do you want to save it before closing?");
795 g_free(short_fn);
797 response = run_unsaved_dialog(msg, msg2);
798 g_free(msg);
800 switch (response)
802 case GTK_RESPONSE_YES:
803 /* document_save_file() returns the status if the file could be saved */
804 return document_save_file(doc, FALSE);
806 case GTK_RESPONSE_NO:
807 return TRUE;
809 case GTK_RESPONSE_CANCEL: /* fall through to default and leave the function */
810 default:
811 return FALSE;
816 #ifndef G_OS_WIN32
817 /* Use GtkFontChooserDialog on GTK3.2 for consistency, and because
818 * GtkFontSelectionDialog is somewhat broken on 3.4 */
819 #if GTK_CHECK_VERSION(3, 2, 0)
820 # undef GTK_FONT_SELECTION_DIALOG
821 # define GTK_FONT_SELECTION_DIALOG GTK_FONT_CHOOSER_DIALOG
823 # define gtk_font_selection_dialog_new(title) \
824 gtk_font_chooser_dialog_new((title), NULL)
825 # define gtk_font_selection_dialog_get_font_name(dlg) \
826 gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dlg))
827 # define gtk_font_selection_dialog_set_font_name(dlg, font) \
828 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dlg), (font))
829 #endif
831 static void
832 on_font_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
834 gboolean close = TRUE;
836 switch (response)
838 case GTK_RESPONSE_APPLY:
839 case GTK_RESPONSE_OK:
841 gchar *fontname;
843 fontname = gtk_font_selection_dialog_get_font_name(
844 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel));
845 ui_set_editor_font(fontname);
846 g_free(fontname);
848 close = (response == GTK_RESPONSE_OK);
849 break;
853 if (close)
854 gtk_widget_hide(ui_widgets.open_fontsel);
856 #endif
859 /* This shows the font selection dialog to choose a font. */
860 void dialogs_show_open_font(void)
862 #ifdef G_OS_WIN32
863 win32_show_font_dialog();
864 #else
866 if (ui_widgets.open_fontsel == NULL)
868 GtkWidget *apply_button;
870 ui_widgets.open_fontsel = gtk_font_selection_dialog_new(_("Choose font"));;
871 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets.open_fontsel), 4);
872 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
873 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
874 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
875 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_fontsel), GDK_WINDOW_TYPE_HINT_DIALOG);
876 gtk_widget_set_name(ui_widgets.open_fontsel, "GeanyDialog");
878 #if GTK_CHECK_VERSION(2, 20, 0)
879 /* apply button doesn't have a getter and is hidden by default, but we'd like to show it */
880 apply_button = gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets.open_fontsel), GTK_RESPONSE_APPLY);
881 #else
882 apply_button = GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->apply_button;
883 #endif
884 if (apply_button)
885 gtk_widget_show(apply_button);
887 g_signal_connect(ui_widgets.open_fontsel,
888 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
889 g_signal_connect(ui_widgets.open_fontsel,
890 "response", G_CALLBACK(on_font_dialog_response), NULL);
892 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_fontsel), GTK_WINDOW(main_widgets.window));
894 gtk_font_selection_dialog_set_font_name(
895 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel), interface_prefs.editor_font);
896 /* We make sure the dialog is visible. */
897 gtk_window_present(GTK_WINDOW(ui_widgets.open_fontsel));
898 #endif
902 static void
903 on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
905 gtk_widget_grab_focus(entry);
909 static void
910 on_input_entry_activate(GtkEntry *entry, GtkDialog *dialog)
912 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
916 static void
917 on_input_numeric_activate(GtkEntry *entry, GtkDialog *dialog)
919 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
923 static void
924 on_input_dialog_response(GtkDialog *dialog, gint response, GtkWidget *entry)
926 gboolean persistent = (gboolean) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dialog), "has_combo"));
928 if (response == GTK_RESPONSE_ACCEPT)
930 const gchar *str = gtk_entry_get_text(GTK_ENTRY(entry));
931 GeanyInputCallback input_cb =
932 (GeanyInputCallback) g_object_get_data(G_OBJECT(dialog), "input_cb");
934 if (persistent)
936 GtkWidget *combo = (GtkWidget *) g_object_get_data(G_OBJECT(dialog), "combo");
937 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(combo), str, 0);
939 input_cb(str);
941 gtk_widget_hide(GTK_WIDGET(dialog));
945 static void add_input_widgets(GtkWidget *dialog, GtkWidget *vbox,
946 const gchar *label_text, const gchar *default_text, gboolean persistent,
947 GCallback insert_text_cb)
949 GtkWidget *entry;
951 if (label_text)
953 GtkWidget *label = gtk_label_new(label_text);
954 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
955 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
956 gtk_container_add(GTK_CONTAINER(vbox), label);
959 if (persistent) /* remember previous entry text in a combo box */
961 GtkWidget *combo = gtk_combo_box_text_new_with_entry();
963 entry = gtk_bin_get_child(GTK_BIN(combo));
964 ui_entry_add_clear_icon(GTK_ENTRY(entry));
965 g_object_set_data(G_OBJECT(dialog), "combo", combo);
966 gtk_container_add(GTK_CONTAINER(vbox), combo);
968 else
970 entry = gtk_entry_new();
971 ui_entry_add_clear_icon(GTK_ENTRY(entry));
972 gtk_container_add(GTK_CONTAINER(vbox), entry);
975 if (default_text != NULL)
977 gtk_entry_set_text(GTK_ENTRY(entry), default_text);
979 gtk_entry_set_max_length(GTK_ENTRY(entry), 255);
980 gtk_entry_set_width_chars(GTK_ENTRY(entry), 30);
982 if (insert_text_cb != NULL)
983 g_signal_connect(entry, "insert-text", insert_text_cb, NULL);
984 g_signal_connect(entry, "activate", G_CALLBACK(on_input_entry_activate), dialog);
985 g_signal_connect(dialog, "show", G_CALLBACK(on_input_dialog_show), entry);
986 g_signal_connect(dialog, "response", G_CALLBACK(on_input_dialog_response), entry);
990 /* Create and display an input dialog.
991 * persistent: whether to remember previous entry text in a combo box;
992 * in this case the dialog returned is not destroyed on a response,
993 * and can be reshown.
994 * Returns: the dialog widget. */
995 static GtkWidget *
996 dialogs_show_input_full(const gchar *title, GtkWindow *parent,
997 const gchar *label_text, const gchar *default_text,
998 gboolean persistent, GeanyInputCallback input_cb, GCallback insert_text_cb)
1000 GtkWidget *dialog, *vbox;
1002 dialog = gtk_dialog_new_with_buttons(title, parent,
1003 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1004 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1005 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1006 gtk_widget_set_name(dialog, "GeanyDialog");
1007 gtk_box_set_spacing(GTK_BOX(vbox), 6);
1009 g_object_set_data(G_OBJECT(dialog), "has_combo", GINT_TO_POINTER(persistent));
1010 g_object_set_data(G_OBJECT(dialog), "input_cb", (gpointer) input_cb);
1012 add_input_widgets(dialog, vbox, label_text, default_text, persistent, insert_text_cb);
1014 if (persistent)
1016 /* override default handler */
1017 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1018 gtk_widget_show_all(dialog);
1019 return dialog;
1021 gtk_widget_show_all(dialog);
1022 gtk_dialog_run(GTK_DIALOG(dialog));
1023 gtk_widget_destroy(dialog);
1024 return NULL;
1028 /* Remember previous entry text in a combo box.
1029 * Returns: the dialog widget. */
1030 GtkWidget *
1031 dialogs_show_input_persistent(const gchar *title, GtkWindow *parent,
1032 const gchar *label_text, const gchar *default_text,
1033 GeanyInputCallback input_cb)
1035 return dialogs_show_input_full(title, parent, label_text, default_text, TRUE, input_cb, NULL);
1039 /* ugly hack - user_data not supported for callback */
1040 static gchar *dialog_input = NULL;
1042 static void on_dialog_input(const gchar *str)
1044 dialog_input = g_strdup(str);
1048 /** Asks the user for text input.
1049 * @param title Dialog title.
1050 * @param parent The currently focused window, usually @c geany->main_widgets->window.
1051 * @c NULL can be used but is discouraged due to window manager effects.
1052 * @param label_text Label text, or @c NULL.
1053 * @param default_text Text to display in the input field, or @c NULL.
1054 * @return New copy of user input or @c NULL if cancelled.
1055 * @since 0.20. */
1056 gchar *dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text,
1057 const gchar *default_text)
1059 dialog_input = NULL;
1060 dialogs_show_input_full(title, parent, label_text, default_text, FALSE, on_dialog_input, NULL);
1061 return dialog_input;
1065 /* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1066 /* Returns: newly allocated copy of the entry text or NULL on cancel.
1067 * Specialised variant for Goto Line dialog. */
1068 gchar *dialogs_show_input_goto_line(const gchar *title, GtkWindow *parent, const gchar *label_text,
1069 const gchar *default_text)
1071 dialog_input = NULL;
1072 dialogs_show_input_full(
1073 title, parent, label_text, default_text, FALSE, on_dialog_input,
1074 G_CALLBACK(ui_editable_insert_text_callback));
1075 return dialog_input;
1080 * Shows an input box to enter a numerical value using a GtkSpinButton.
1081 * If the dialog is aborted, @a value remains untouched.
1083 * @param title The dialog title.
1084 * @param label_text The shown dialog label.
1085 * @param value The default value for the spin button and the return location of the entered value.
1086 * Must be non-NULL.
1087 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1088 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1089 * @param step Increment added or subtracted by spinning the widget
1090 * (see documentation for @c gtk_spin_button_new_with_range()).
1092 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1094 * @since 0.16
1096 gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text,
1097 gdouble *value, gdouble min, gdouble max, gdouble step)
1099 GtkWidget *dialog, *label, *spin, *vbox;
1100 gboolean res = FALSE;
1102 g_return_val_if_fail(title != NULL, FALSE);
1103 g_return_val_if_fail(label_text != NULL, FALSE);
1104 g_return_val_if_fail(value != NULL, FALSE);
1106 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
1107 GTK_DIALOG_DESTROY_WITH_PARENT,
1108 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1109 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1110 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1111 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1112 gtk_widget_set_name(dialog, "GeanyDialog");
1114 label = gtk_label_new(label_text);
1115 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1117 spin = gtk_spin_button_new_with_range(min, max, step);
1118 ui_entry_add_clear_icon(GTK_ENTRY(spin));
1119 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), *value);
1120 g_signal_connect(spin, "activate", G_CALLBACK(on_input_numeric_activate), dialog);
1122 gtk_container_add(GTK_CONTAINER(vbox), label);
1123 gtk_container_add(GTK_CONTAINER(vbox), spin);
1124 gtk_widget_show_all(vbox);
1126 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1128 *value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1129 res = TRUE;
1131 gtk_widget_destroy(dialog);
1133 return res;
1137 void dialogs_show_file_properties(GeanyDocument *doc)
1139 GtkWidget *dialog, *label, *image, *check;
1140 gchar *file_size, *title, *base_name, *time_changed, *time_modified, *time_accessed, *enctext;
1141 gchar *short_name;
1142 #ifdef HAVE_SYS_TYPES_H
1143 struct stat st;
1144 off_t filesize;
1145 mode_t mode;
1146 gchar *locale_filename;
1147 #else
1148 gint filesize = 0;
1149 gint mode = 0;
1150 #endif
1152 /* define this ones, to avoid later trouble */
1153 #ifndef S_IRUSR
1154 # define S_IRUSR 0
1155 # define S_IWUSR 0
1156 # define S_IXUSR 0
1157 #endif
1158 #ifndef S_IRGRP
1159 # define S_IRGRP 0
1160 # define S_IWGRP 0
1161 # define S_IXGRP 0
1162 # define S_IROTH 0
1163 # define S_IWOTH 0
1164 # define S_IXOTH 0
1165 #endif
1167 g_return_if_fail(doc == NULL || doc->is_valid);
1169 if (doc == NULL || doc->file_name == NULL)
1171 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1172 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1173 return;
1177 #ifdef HAVE_SYS_TYPES_H
1178 locale_filename = utils_get_locale_from_utf8(doc->file_name);
1179 if (g_stat(locale_filename, &st) == 0)
1181 /* first copy the returned string and the trim it, to not modify the static glibc string
1182 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1183 time_changed = g_strchomp(g_strdup(ctime(&st.st_ctime)));
1184 time_modified = g_strchomp(g_strdup(ctime(&st.st_mtime)));
1185 time_accessed = g_strchomp(g_strdup(ctime(&st.st_atime)));
1186 filesize = st.st_size;
1187 mode = st.st_mode;
1189 else
1191 time_changed = g_strdup(_("unknown"));
1192 time_modified = g_strdup(_("unknown"));
1193 time_accessed = g_strdup(_("unknown"));
1194 filesize = (off_t) 0;
1195 mode = (mode_t) 0;
1197 g_free(locale_filename);
1198 #else
1199 time_changed = g_strdup(_("unknown"));
1200 time_modified = g_strdup(_("unknown"));
1201 time_accessed = g_strdup(_("unknown"));
1202 #endif
1204 base_name = g_path_get_basename(doc->file_name);
1205 short_name = utils_str_middle_truncate(base_name, 30);
1206 title = g_strdup_printf(_("%s Properties"), short_name);
1207 dialog = ui_builder_get_object("properties_dialog");
1208 gtk_window_set_title(GTK_WINDOW(dialog), title);
1209 g_free(short_name);
1210 g_free(title);
1211 gtk_widget_set_name(dialog, "GeanyDialog");
1213 label = ui_lookup_widget(dialog, "file_name_label");
1214 gtk_label_set_text(GTK_LABEL(label), base_name);
1216 image = ui_lookup_widget(dialog, "file_type_image");
1217 gtk_image_set_from_gicon(GTK_IMAGE(image), doc->file_type->icon,
1218 GTK_ICON_SIZE_BUTTON);
1220 label = ui_lookup_widget(dialog, "file_type_label");
1221 gtk_label_set_text(GTK_LABEL(label), doc->file_type->title);
1223 label = ui_lookup_widget(dialog, "file_size_label");
1224 file_size = utils_make_human_readable_str(filesize, 1, 0);
1225 gtk_label_set_text(GTK_LABEL(label), file_size);
1226 g_free(file_size);
1228 label = ui_lookup_widget(dialog, "file_location_label");
1229 gtk_label_set_text(GTK_LABEL(label), doc->file_name);
1231 check = ui_lookup_widget(dialog, "file_read_only_check");
1232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), doc->readonly);
1234 label = ui_lookup_widget(dialog, "file_encoding_label");
1235 enctext = g_strdup_printf("%s %s",
1236 doc->encoding,
1237 (encodings_is_unicode_charset(doc->encoding)) ?
1238 ((doc->has_bom) ? _("(with BOM)") : _("(without BOM)")) : "");
1239 gtk_label_set_text(GTK_LABEL(label), enctext);
1240 g_free(enctext);
1242 label = ui_lookup_widget(dialog, "file_modified_label");
1243 gtk_label_set_text(GTK_LABEL(label), time_modified);
1244 label = ui_lookup_widget(dialog, "file_changed_label");
1245 gtk_label_set_text(GTK_LABEL(label), time_changed);
1246 label = ui_lookup_widget(dialog, "file_accessed_label");
1247 gtk_label_set_text(GTK_LABEL(label), time_accessed);
1249 /* permissions */
1250 check = ui_lookup_widget(dialog, "file_perm_owner_r_check");
1251 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRUSR);
1252 check = ui_lookup_widget(dialog, "file_perm_owner_w_check");
1253 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWUSR);
1254 check = ui_lookup_widget(dialog, "file_perm_owner_x_check");
1255 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXUSR);
1256 check = ui_lookup_widget(dialog, "file_perm_group_r_check");
1257 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRGRP);
1258 check = ui_lookup_widget(dialog, "file_perm_group_w_check");
1259 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWGRP);
1260 check = ui_lookup_widget(dialog, "file_perm_group_x_check");
1261 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXGRP);
1262 check = ui_lookup_widget(dialog, "file_perm_other_r_check");
1263 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IROTH);
1264 check = ui_lookup_widget(dialog, "file_perm_other_w_check");
1265 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWOTH);
1266 check = ui_lookup_widget(dialog, "file_perm_other_x_check");
1267 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXOTH);
1269 g_free(base_name);
1270 g_free(time_changed);
1271 g_free(time_modified);
1272 g_free(time_accessed);
1274 gtk_widget_show(dialog);
1278 /* extra_text can be NULL; otherwise it is displayed below main_text.
1279 * if parent is NULL, main_widgets.window will be used
1280 * btn_1, btn_2, btn_3 can be NULL.
1281 * returns response_1, response_2 or response_3 */
1282 static gint show_prompt(GtkWidget *parent,
1283 const gchar *btn_1, GtkResponseType response_1,
1284 const gchar *btn_2, GtkResponseType response_2,
1285 const gchar *btn_3, GtkResponseType response_3,
1286 const gchar *question_text, const gchar *extra_text)
1288 gboolean ret = FALSE;
1289 GtkWidget *dialog;
1290 GtkWidget *btn;
1292 if (btn_2 == NULL)
1294 btn_2 = GTK_STOCK_NO;
1295 response_2 = GTK_RESPONSE_NO;
1297 if (btn_3 == NULL)
1299 btn_3 = GTK_STOCK_YES;
1300 response_3 = GTK_RESPONSE_YES;
1303 #ifdef G_OS_WIN32
1304 /* our native dialog code doesn't support custom buttons */
1305 if (utils_str_equal(btn_3, GTK_STOCK_YES) &&
1306 utils_str_equal(btn_2, GTK_STOCK_NO) && btn_1 == NULL)
1308 gchar *string = (extra_text == NULL) ? g_strdup(question_text) :
1309 g_strconcat(question_text, "\n\n", extra_text, NULL);
1311 ret = win32_message_dialog(parent, GTK_MESSAGE_QUESTION, string);
1312 ret = ret ? response_3 : response_2;
1313 g_free(string);
1314 return ret;
1316 #endif
1317 if (parent == NULL && main_status.main_window_realized)
1318 parent = main_widgets.window;
1320 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
1321 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1322 GTK_BUTTONS_NONE, "%s", question_text);
1323 gtk_widget_set_name(dialog, "GeanyDialog");
1324 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1325 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
1327 /* question_text will be in bold if optional extra_text used */
1328 if (extra_text != NULL)
1329 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1330 "%s", extra_text);
1332 if (btn_1 != NULL)
1333 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_1, response_1);
1335 /* For a cancel button, use cancel response so user can press escape to cancel */
1336 btn = gtk_dialog_add_button(GTK_DIALOG(dialog), btn_2,
1337 utils_str_equal(btn_2, GTK_STOCK_CANCEL) ? GTK_RESPONSE_CANCEL : response_2);
1338 /* we don't want a default, but we need to override the apply button as default */
1339 gtk_widget_grab_default(btn);
1340 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_3, response_3);
1342 ret = gtk_dialog_run(GTK_DIALOG(dialog));
1343 gtk_widget_destroy(dialog);
1345 if (ret == GTK_RESPONSE_CANCEL)
1346 ret = response_2;
1347 return ret;
1352 * Shows a question message box with @a text and Yes/No buttons.
1353 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1354 * message dialog box is shown.
1356 * @param text Printf()-style format string.
1357 * @param ... Arguments for the @a text format string.
1359 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1361 gboolean dialogs_show_question(const gchar *text, ...)
1363 gchar *string;
1364 va_list args;
1365 GtkWidget *parent = (main_status.main_window_realized) ? main_widgets.window : NULL;
1366 gint result;
1368 va_start(args, text);
1369 string = g_strdup_vprintf(text, args);
1370 va_end(args);
1371 result = show_prompt(parent,
1372 NULL, GTK_RESPONSE_NONE,
1373 GTK_STOCK_NO, GTK_RESPONSE_NO,
1374 GTK_STOCK_YES, GTK_RESPONSE_YES,
1375 string, NULL);
1376 g_free(string);
1377 return (result == GTK_RESPONSE_YES);
1381 /* extra_text can be NULL; otherwise it is displayed below main_text.
1382 * if parent is NULL, main_widgets.window will be used
1383 * yes_btn, no_btn can be NULL. */
1384 gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn,
1385 const gchar *extra_text, const gchar *main_text, ...)
1387 gint result;
1388 gchar *string;
1389 va_list args;
1391 va_start(args, main_text);
1392 string = g_strdup_vprintf(main_text, args);
1393 va_end(args);
1394 result = show_prompt(parent,
1395 NULL, GTK_RESPONSE_NONE,
1396 no_btn, GTK_RESPONSE_NO,
1397 yes_btn, GTK_RESPONSE_YES,
1398 string, extra_text);
1399 g_free(string);
1400 return (result == GTK_RESPONSE_YES);
1404 /* extra_text can be NULL; otherwise it is displayed below main_text.
1405 * if parent is NULL, main_widgets.window will be used
1406 * btn_1, btn_2, btn_3 can be NULL.
1407 * returns response_1, response_2 or response_3 */
1408 gint dialogs_show_prompt(GtkWidget *parent,
1409 const gchar *btn_1, GtkResponseType response_1,
1410 const gchar *btn_2, GtkResponseType response_2,
1411 const gchar *btn_3, GtkResponseType response_3,
1412 const gchar *extra_text, const gchar *main_text, ...)
1414 gchar *string;
1415 va_list args;
1416 gint result;
1418 va_start(args, main_text);
1419 string = g_strdup_vprintf(main_text, args);
1420 va_end(args);
1421 result = show_prompt(parent, btn_1, response_1, btn_2, response_2, btn_3, response_3,
1422 string, extra_text);
1423 g_free(string);
1424 return result;