The mkstemps special case for windows is not necessary
[geany-mirror.git] / src / dialogs.c
blob7648b7bac63bcf735697caa9ea496358d6ef25b1
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"
41 #include "win32.h"
43 #include "gtkcompat.h"
45 #include <gdk/gdkkeysyms.h>
46 #include <string.h>
48 #ifdef HAVE_SYS_TIME_H
49 # include <sys/time.h>
50 #endif
51 #include <time.h>
53 #ifdef HAVE_SYS_TYPES_H
54 # include <sys/types.h>
55 #endif
57 /* gstdio.h also includes sys/stat.h */
58 #include <glib/gstdio.h>
61 enum
63 GEANY_RESPONSE_RENAME,
64 GEANY_RESPONSE_VIEW
68 static struct FileSelState
70 struct
72 guint filter_idx;
73 gint encoding_idx;
74 gint filetype_idx;
75 gboolean show_hidden;
76 gboolean more_options_visible;
77 } open;
79 filesel_state = {
82 GEANY_ENCODINGS_MAX, /* default encoding is detect from file */
83 GEANY_FILETYPES_NONE, /* default filetype is detect from extension */
84 FALSE,
85 FALSE
90 static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo);
93 /* gets the ID of the current file filter */
94 static guint file_chooser_get_filter_idx(GtkFileChooser *chooser)
96 guint idx = 0;
97 GtkFileFilter *current;
98 GSList *filters, *item;
100 current = gtk_file_chooser_get_filter(chooser);
101 filters = gtk_file_chooser_list_filters(chooser);
102 foreach_slist(item, filters)
104 if (item->data == current)
105 break;
106 idx ++;
108 g_slist_free(filters);
109 return idx;
113 /* sets the current file filter from its ID */
114 static void file_chooser_set_filter_idx(GtkFileChooser *chooser, guint idx)
116 GtkFileFilter *current;
117 GSList *filters;
119 filters = gtk_file_chooser_list_filters(chooser);
120 current = g_slist_nth_data(filters, idx);
121 g_slist_free(filters);
122 gtk_file_chooser_set_filter(chooser, current);
126 static gboolean open_file_dialog_handle_response(GtkWidget *dialog, gint response)
128 gboolean ret = TRUE;
130 if (response == GTK_RESPONSE_ACCEPT || response == GEANY_RESPONSE_VIEW)
132 GSList *filelist;
133 GeanyFiletype *ft = NULL;
134 const gchar *charset = NULL;
135 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
136 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
137 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
138 gboolean ro = (response == GEANY_RESPONSE_VIEW); /* View clicked */
140 filesel_state.open.more_options_visible = gtk_expander_get_expanded(GTK_EXPANDER(expander));
141 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
142 filesel_state.open.filetype_idx = filetype_combo_box_get_active_filetype(GTK_COMBO_BOX(filetype_combo));
144 /* ignore detect from file item */
145 if (filesel_state.open.filetype_idx > 0)
146 ft = filetypes_index(filesel_state.open.filetype_idx);
148 filesel_state.open.encoding_idx = ui_encodings_combo_box_get_active_encoding(GTK_COMBO_BOX(encoding_combo));
149 if (filesel_state.open.encoding_idx >= 0 && filesel_state.open.encoding_idx < GEANY_ENCODINGS_MAX)
150 charset = encodings[filesel_state.open.encoding_idx].charset;
152 filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
153 if (filelist != NULL)
155 const gchar *first = filelist->data;
157 // When there's only one filename it may have been typed manually
158 if (!filelist->next && !g_file_test(first, G_FILE_TEST_EXISTS))
160 dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("\"%s\" was not found."), first);
161 ret = FALSE;
163 else
165 document_open_files(filelist, ro, ft, charset);
167 g_slist_foreach(filelist, (GFunc) g_free, NULL); /* free filenames */
169 g_slist_free(filelist);
171 if (app->project && !EMPTY(app->project->base_path))
172 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
173 app->project->base_path, NULL);
174 return ret;
178 static void on_file_open_show_hidden_notify(GObject *filechooser,
179 GParamSpec *pspec, gpointer data)
181 GtkWidget *toggle_button;
183 toggle_button = ui_lookup_widget(GTK_WIDGET(filechooser), "check_hidden");
185 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button),
186 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser)));
190 static void
191 on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, GtkWidget *dialog)
193 filesel_state.open.show_hidden = gtk_toggle_button_get_active(togglebutton);
194 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), filesel_state.open.show_hidden);
198 static void filetype_combo_cell_data_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell,
199 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
201 gboolean sensitive = !gtk_tree_model_iter_has_child(tree_model, iter);
202 gchar *text;
204 gtk_tree_model_get(tree_model, iter, 1, &text, -1);
205 g_object_set(cell, "sensitive", sensitive, "text", text, NULL);
206 g_free(text);
210 static GtkWidget *create_filetype_combo_box(void)
212 GtkTreeStore *store;
213 GtkTreeIter iter_compiled, iter_script, iter_markup, iter_misc, iter_detect;
214 GtkTreeIter *iter_parent;
215 GtkWidget *combo;
216 GtkCellRenderer *renderer;
217 GSList *node;
219 store = gtk_tree_store_new(2, G_TYPE_INT, G_TYPE_STRING);
221 gtk_tree_store_insert_with_values(store, &iter_detect, NULL, -1,
222 0, GEANY_FILETYPES_NONE, 1, _("Detect from file"), -1);
224 gtk_tree_store_insert_with_values(store, &iter_compiled, NULL, -1,
225 0, -1, 1, _("Programming Languages"), -1);
226 gtk_tree_store_insert_with_values(store, &iter_script, NULL, -1,
227 0, -1, 1, _("Scripting Languages"), -1);
228 gtk_tree_store_insert_with_values(store, &iter_markup, NULL, -1,
229 0, -1, 1, _("Markup Languages"), -1);
230 gtk_tree_store_insert_with_values(store, &iter_misc, NULL, -1,
231 0, -1, 1, _("Miscellaneous"), -1);
233 foreach_slist (node, filetypes_by_title)
235 GeanyFiletype *ft = node->data;
237 if (ft->id == GEANY_FILETYPES_NONE)
238 continue;
240 switch (ft->group)
242 case GEANY_FILETYPE_GROUP_COMPILED: iter_parent = &iter_compiled; break;
243 case GEANY_FILETYPE_GROUP_SCRIPT: iter_parent = &iter_script; break;
244 case GEANY_FILETYPE_GROUP_MARKUP: iter_parent = &iter_markup; break;
245 case GEANY_FILETYPE_GROUP_MISC: iter_parent = &iter_misc; break;
246 case GEANY_FILETYPE_GROUP_NONE:
247 default: iter_parent = NULL;
249 gtk_tree_store_insert_with_values(store, NULL, iter_parent, -1,
250 0, (gint) ft->id, 1, ft->title, -1);
253 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
254 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter_detect);
255 renderer = gtk_cell_renderer_text_new();
256 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
257 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
258 filetype_combo_cell_data_func, NULL, NULL);
260 g_object_unref(store);
262 return combo;
266 static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo)
268 gint id = 0;
269 GtkTreeIter iter;
271 if (gtk_combo_box_get_active_iter(combo, &iter))
273 GtkTreeModel *model = gtk_combo_box_get_model(combo);
274 gtk_tree_model_get(model, &iter, 0, &id, -1);
276 return id;
280 static gboolean filetype_combo_box_set_active_filetype(GtkComboBox *combo, const gint id)
282 GtkTreeModel *model = gtk_combo_box_get_model(combo);
283 GtkTreeIter iter;
285 if (gtk_tree_model_get_iter_first(model, &iter))
289 gint row_id;
290 gtk_tree_model_get(model, &iter, 0, &row_id, -1);
291 if (id == row_id)
293 gtk_combo_box_set_active_iter(combo, &iter);
294 return TRUE;
297 while (ui_tree_model_iter_any_next(model, &iter, TRUE));
299 return FALSE;
303 static GtkWidget *add_file_open_extra_widget(GtkWidget *dialog)
305 GtkWidget *expander, *vbox, *table, *check_hidden;
306 GtkWidget *filetype_ebox, *filetype_label, *filetype_combo;
307 GtkWidget *encoding_ebox, *encoding_label, *encoding_combo;
309 expander = gtk_expander_new_with_mnemonic(_("_More Options"));
310 vbox = gtk_vbox_new(FALSE, 6);
311 gtk_container_add(GTK_CONTAINER(expander), vbox);
313 table = gtk_table_new(2, 4, FALSE);
315 /* line 1 with checkbox and encoding combo */
316 check_hidden = gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
317 gtk_widget_show(check_hidden);
318 gtk_table_attach(GTK_TABLE(table), check_hidden, 0, 1, 0, 1,
319 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
320 (GtkAttachOptions) (0), 0, 5);
322 /* spacing */
323 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""), 1, 2, 0, 1,
324 (GtkAttachOptions) (GTK_FILL),
325 (GtkAttachOptions) (0), 5, 5);
327 encoding_label = gtk_label_new(_("Set encoding:"));
328 gtk_misc_set_alignment(GTK_MISC(encoding_label), 1, 0);
329 gtk_table_attach(GTK_TABLE(table), encoding_label, 2, 3, 0, 1,
330 (GtkAttachOptions) (GTK_FILL),
331 (GtkAttachOptions) (0), 4, 5);
332 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
333 encoding_ebox = gtk_event_box_new();
334 encoding_combo = ui_create_encodings_combo_box(TRUE, GEANY_ENCODINGS_MAX);
335 gtk_widget_set_tooltip_text(encoding_ebox,
336 _("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."));
337 gtk_container_add(GTK_CONTAINER(encoding_ebox), encoding_combo);
338 gtk_table_attach(GTK_TABLE(table), encoding_ebox, 3, 4, 0, 1,
339 (GtkAttachOptions) (GTK_FILL),
340 (GtkAttachOptions) (0), 0, 5);
342 /* line 2 with filetype combo */
343 filetype_label = gtk_label_new(_("Set filetype:"));
344 gtk_misc_set_alignment(GTK_MISC(filetype_label), 1, 0);
345 gtk_table_attach(GTK_TABLE(table), filetype_label, 2, 3, 1, 2,
346 (GtkAttachOptions) (GTK_FILL),
347 (GtkAttachOptions) (0), 4, 5);
348 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
349 filetype_ebox = gtk_event_box_new();
350 filetype_combo = create_filetype_combo_box();
351 gtk_widget_set_tooltip_text(filetype_ebox,
352 _("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."));
353 gtk_container_add(GTK_CONTAINER(filetype_ebox), filetype_combo);
354 gtk_table_attach(GTK_TABLE(table), filetype_ebox, 3, 4, 1, 2,
355 (GtkAttachOptions) (GTK_FILL),
356 (GtkAttachOptions) (0), 0, 5);
358 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
359 gtk_widget_show_all(vbox);
361 g_signal_connect(check_hidden, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled), dialog);
363 ui_hookup_widget(dialog, expander, "more_options_expander");
364 ui_hookup_widget(dialog, check_hidden, "check_hidden");
365 ui_hookup_widget(dialog, filetype_combo, "filetype_combo");
366 ui_hookup_widget(dialog, encoding_combo, "encoding_combo");
368 return expander;
372 static GtkWidget *create_open_file_dialog(void)
374 GtkWidget *dialog;
375 GtkWidget *viewbtn;
376 GSList *node;
378 dialog = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets.window),
379 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
380 gtk_widget_set_name(dialog, "GeanyDialog");
382 viewbtn = gtk_dialog_add_button(GTK_DIALOG(dialog), C_("Open dialog action", "_View"), GEANY_RESPONSE_VIEW);
383 gtk_widget_set_tooltip_text(viewbtn,
384 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
386 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
387 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
388 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
389 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
391 gtk_widget_set_size_request(dialog, -1, 460);
392 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
393 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
394 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
395 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
396 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
397 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
398 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
400 /* add checkboxes and filename entry */
401 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), add_file_open_extra_widget(dialog));
403 /* add FileFilters(start with "All Files") */
404 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
405 filetypes_create_file_filter(filetypes[GEANY_FILETYPES_NONE]));
406 /* now create meta filter "All Source" */
407 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
408 filetypes_create_file_filter_all_source());
409 foreach_slist(node, filetypes_by_title)
411 GeanyFiletype *ft = node->data;
413 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
414 continue;
415 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filetypes_create_file_filter(ft));
418 g_signal_connect(dialog, "notify::show-hidden",
419 G_CALLBACK(on_file_open_show_hidden_notify), NULL);
421 return dialog;
425 static void open_file_dialog_apply_settings(GtkWidget *dialog)
427 static gboolean initialized = FALSE;
428 GtkWidget *check_hidden = ui_lookup_widget(dialog, "check_hidden");
429 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
430 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
431 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
433 /* we can't know the initial position of combo boxes, so retreive it the first time */
434 if (! initialized)
436 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
438 initialized = TRUE;
440 else
442 file_chooser_set_filter_idx(GTK_FILE_CHOOSER(dialog), filesel_state.open.filter_idx);
444 gtk_expander_set_expanded(GTK_EXPANDER(expander), filesel_state.open.more_options_visible);
445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden), filesel_state.open.show_hidden);
446 ui_encodings_combo_box_set_active_encoding(GTK_COMBO_BOX(encoding_combo), filesel_state.open.encoding_idx);
447 filetype_combo_box_set_active_filetype(GTK_COMBO_BOX(filetype_combo), filesel_state.open.filetype_idx);
451 /* This shows the file selection dialog to open a file. */
452 void dialogs_show_open_file(void)
454 gchar *initdir;
456 /* set dialog directory to the current file's directory, if present */
457 initdir = utils_get_current_file_dir_utf8();
459 /* use project or default startup directory (if set) if no files are open */
460 /** TODO should it only be used when initally open the dialog and not on every show? */
461 if (! initdir)
462 initdir = g_strdup(utils_get_default_dir_utf8());
464 SETPTR(initdir, utils_get_locale_from_utf8(initdir));
466 #ifdef G_OS_WIN32
467 if (interface_prefs.use_native_windows_dialogs)
468 win32_show_document_open_dialog(GTK_WINDOW(main_widgets.window), _("Open File"), initdir);
469 else
470 #endif
472 GtkWidget *dialog = create_open_file_dialog();
474 open_file_dialog_apply_settings(dialog);
476 if (initdir != NULL && g_path_is_absolute(initdir))
477 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), initdir);
479 if (app->project && !EMPTY(app->project->base_path))
480 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
481 app->project->base_path, NULL);
483 while (!open_file_dialog_handle_response(dialog,
484 gtk_dialog_run(GTK_DIALOG(dialog))));
485 gtk_widget_destroy(dialog);
487 g_free(initdir);
491 static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file)
493 GeanyDocument *doc = document_get_current();
494 gboolean success = FALSE;
496 g_return_val_if_fail(!EMPTY(utf8_filename), FALSE);
498 if (doc->file_name != NULL)
500 if (rename_file)
502 document_rename_file(doc, utf8_filename);
504 if (doc->tm_file)
506 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
507 tm_workspace_remove_source_file(doc->tm_file);
508 tm_source_file_free(doc->tm_file);
509 doc->tm_file = NULL;
512 success = document_save_file_as(doc, utf8_filename);
514 build_menu_update(doc);
515 return success;
519 static gboolean save_as_dialog_handle_response(GtkWidget *dialog, gint response)
521 gboolean rename_file = FALSE;
522 gboolean success = FALSE;
523 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
525 switch (response)
527 case GEANY_RESPONSE_RENAME:
528 /* rename doesn't check for empty filename or overwriting */
529 if (G_UNLIKELY(EMPTY(new_filename)))
531 utils_beep();
532 break;
534 if (g_file_test(new_filename, G_FILE_TEST_EXISTS) &&
535 !dialogs_show_question_full(NULL, NULL, NULL,
536 _("Overwrite?"),
537 _("Filename already exists!")))
538 break;
539 rename_file = TRUE;
540 /* fall through */
541 case GTK_RESPONSE_ACCEPT:
543 gchar *utf8_filename;
545 utf8_filename = utils_get_utf8_from_locale(new_filename);
546 success = handle_save_as(utf8_filename, rename_file);
547 g_free(utf8_filename);
548 break;
550 case GTK_RESPONSE_DELETE_EVENT:
551 case GTK_RESPONSE_CANCEL:
552 success = TRUE;
553 break;
555 g_free(new_filename);
557 return success;
561 static GtkWidget *create_save_file_dialog(GeanyDocument *doc)
563 GtkWidget *dialog, *rename_btn;
564 const gchar *initdir;
566 dialog = gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets.window),
567 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
568 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
569 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
570 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
571 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
572 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
573 gtk_widget_set_name(dialog, "GeanyDialog");
575 rename_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), _("R_ename"), GEANY_RESPONSE_RENAME);
576 gtk_widget_set_tooltip_text(rename_btn, _("Save the file and rename it"));
577 /* disable rename unless file exists on disk */
578 gtk_widget_set_sensitive(rename_btn, doc->real_path != NULL);
580 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
581 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
582 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
583 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
585 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
586 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
588 /* set the folder by default to the project base dir or the global pref for opening files */
589 initdir = utils_get_default_dir_utf8();
590 if (initdir)
592 gchar *linitdir = utils_get_locale_from_utf8(initdir);
593 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), linitdir);
594 g_free(linitdir);
596 return dialog;
600 static gboolean show_save_as_gtk(GeanyDocument *doc)
602 GtkWidget *dialog;
603 gint resp;
605 g_return_val_if_fail(DOC_VALID(doc), FALSE);
607 dialog = create_save_file_dialog(doc);
609 if (doc->file_name != NULL)
611 if (g_path_is_absolute(doc->file_name))
613 gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name);
614 gchar *locale_basename = g_path_get_basename(locale_filename);
615 gchar *locale_dirname = g_path_get_dirname(locale_filename);
617 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
618 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), locale_basename);
620 g_free(locale_filename);
621 g_free(locale_basename);
622 g_free(locale_dirname);
624 else
625 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), doc->file_name);
627 else
629 gchar *fname = NULL;
631 if (doc->file_type != NULL && doc->file_type->extension != NULL)
632 fname = g_strconcat(GEANY_STRING_UNTITLED, ".",
633 doc->file_type->extension, NULL);
634 else
635 fname = g_strdup(GEANY_STRING_UNTITLED);
637 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
639 g_free(fname);
642 if (app->project && !EMPTY(app->project->base_path))
643 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
644 app->project->base_path, NULL);
646 /* Run the dialog synchronously, pausing this function call */
649 resp = gtk_dialog_run(GTK_DIALOG(dialog));
651 while (! save_as_dialog_handle_response(dialog, resp));
653 if (app->project && !EMPTY(app->project->base_path))
654 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
655 app->project->base_path, NULL);
657 gtk_widget_destroy(dialog);
659 return (resp == GTK_RESPONSE_ACCEPT);
664 * Shows the Save As dialog for the current notebook page.
666 * @return @c TRUE if the file was saved, otherwise @c FALSE.
668 GEANY_API_SYMBOL
669 gboolean dialogs_show_save_as(void)
671 GeanyDocument *doc = document_get_current();
672 gboolean result = FALSE;
674 g_return_val_if_fail(doc, FALSE);
676 #ifdef G_OS_WIN32
677 if (interface_prefs.use_native_windows_dialogs)
679 gchar *utf8_name = win32_show_document_save_as_dialog(GTK_WINDOW(main_widgets.window),
680 _("Save File"), doc);
681 if (utf8_name != NULL)
682 result = handle_save_as(utf8_name, FALSE);
684 else
685 #endif
686 result = show_save_as_gtk(doc);
687 return result;
691 #ifndef G_OS_WIN32
692 static void show_msgbox_dialog(GtkWidget *dialog, GtkMessageType type, GtkWindow *parent)
694 const gchar *title;
695 switch (type)
697 case GTK_MESSAGE_ERROR:
698 title = _("Error");
699 break;
700 case GTK_MESSAGE_QUESTION:
701 title = _("Question");
702 break;
703 case GTK_MESSAGE_WARNING:
704 title = _("Warning");
705 break;
706 default:
707 title = _("Information");
708 break;
710 gtk_window_set_title(GTK_WINDOW(dialog), title);
711 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
712 gtk_widget_set_name(dialog, "GeanyDialog");
714 gtk_dialog_run(GTK_DIALOG(dialog));
715 gtk_widget_destroy(dialog);
717 #endif
721 * Shows a message box of the type @a type with @a text.
722 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
723 * message dialog box is shown.
725 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
726 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
727 * @param text Printf()-style format string.
728 * @param ... Arguments for the @a text format string.
730 GEANY_API_SYMBOL
731 void dialogs_show_msgbox(GtkMessageType type, const gchar *text, ...)
733 #ifndef G_OS_WIN32
734 GtkWidget *dialog;
735 #endif
736 gchar *string;
737 va_list args;
738 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
740 va_start(args, text);
741 string = g_strdup_vprintf(text, args);
742 va_end(args);
744 #ifdef G_OS_WIN32
745 win32_message_dialog(GTK_WIDGET(parent), type, string);
746 #else
747 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
748 type, GTK_BUTTONS_OK, "%s", string);
749 show_msgbox_dialog(dialog, type, parent);
750 #endif
751 g_free(string);
755 void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
757 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
758 #ifdef G_OS_WIN32
759 /* put the two strings together because Windows message boxes don't support secondary texts */
760 gchar *string = g_strconcat(text, "\n", secondary, NULL);
761 win32_message_dialog(GTK_WIDGET(parent), type, string);
762 g_free(string);
763 #else
764 GtkWidget *dialog;
765 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
766 type, GTK_BUTTONS_OK, "%s", text);
767 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
768 show_msgbox_dialog(dialog, type, parent);
769 #endif
773 static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
775 GtkWidget *dialog, *button;
776 gint ret;
778 dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
779 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
780 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
781 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg2);
782 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
784 button = ui_button_new_with_image(GTK_STOCK_CLEAR, _("_Don't save"));
785 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
786 gtk_widget_show(button);
788 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
790 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
791 ret = gtk_dialog_run(GTK_DIALOG(dialog));
793 gtk_widget_destroy(dialog);
795 return ret;
799 gboolean dialogs_show_unsaved_file(GeanyDocument *doc)
801 gchar *msg, *short_fn = NULL;
802 const gchar *msg2;
803 gint response;
804 gboolean old_quitting_state = main_status.quitting;
806 /* display the file tab to remind the user of the document */
807 main_status.quitting = FALSE;
808 document_show_tab(doc);
809 main_status.quitting = old_quitting_state;
811 short_fn = document_get_basename_for_display(doc, -1);
813 msg = g_strdup_printf(_("The file '%s' is not saved."), short_fn);
814 msg2 = _("Do you want to save it before closing?");
815 g_free(short_fn);
817 response = run_unsaved_dialog(msg, msg2);
818 g_free(msg);
820 switch (response)
822 case GTK_RESPONSE_YES:
823 /* document_save_file() returns the status if the file could be saved */
824 return document_save_file(doc, FALSE);
826 case GTK_RESPONSE_NO:
827 return TRUE;
829 case GTK_RESPONSE_CANCEL: /* fall through to default and leave the function */
830 default:
831 return FALSE;
836 /* Use GtkFontChooserDialog on GTK3.2 for consistency, and because
837 * GtkFontSelectionDialog is somewhat broken on 3.4 */
838 #if GTK_CHECK_VERSION(3, 2, 0)
839 # undef GTK_FONT_SELECTION_DIALOG
840 # define GTK_FONT_SELECTION_DIALOG GTK_FONT_CHOOSER_DIALOG
842 # define gtk_font_selection_dialog_new(title) \
843 gtk_font_chooser_dialog_new((title), NULL)
844 # define gtk_font_selection_dialog_get_font_name(dlg) \
845 gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dlg))
846 # define gtk_font_selection_dialog_set_font_name(dlg, font) \
847 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dlg), (font))
848 #endif
850 static void
851 on_font_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
853 gboolean close = TRUE;
855 switch (response)
857 case GTK_RESPONSE_APPLY:
858 case GTK_RESPONSE_OK:
860 gchar *fontname;
862 fontname = gtk_font_selection_dialog_get_font_name(
863 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel));
864 ui_set_editor_font(fontname);
865 g_free(fontname);
867 close = (response == GTK_RESPONSE_OK);
868 break;
872 if (close)
873 gtk_widget_hide(ui_widgets.open_fontsel);
877 /* This shows the font selection dialog to choose a font. */
878 void dialogs_show_open_font(void)
880 #ifdef G_OS_WIN32
881 if (interface_prefs.use_native_windows_dialogs)
883 win32_show_font_dialog();
884 return;
886 #endif
888 if (ui_widgets.open_fontsel == NULL)
890 GtkWidget *apply_button;
892 ui_widgets.open_fontsel = gtk_font_selection_dialog_new(_("Choose font"));;
893 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets.open_fontsel), 4);
894 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
895 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
896 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
897 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_fontsel), GDK_WINDOW_TYPE_HINT_DIALOG);
898 gtk_widget_set_name(ui_widgets.open_fontsel, "GeanyDialog");
900 #if GTK_CHECK_VERSION(2, 20, 0)
901 /* apply button doesn't have a getter and is hidden by default, but we'd like to show it */
902 apply_button = gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets.open_fontsel), GTK_RESPONSE_APPLY);
903 #else
904 apply_button = GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->apply_button;
905 #endif
906 if (apply_button)
907 gtk_widget_show(apply_button);
909 g_signal_connect(ui_widgets.open_fontsel,
910 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
911 g_signal_connect(ui_widgets.open_fontsel,
912 "response", G_CALLBACK(on_font_dialog_response), NULL);
914 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_fontsel), GTK_WINDOW(main_widgets.window));
916 gtk_font_selection_dialog_set_font_name(
917 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel), interface_prefs.editor_font);
918 /* We make sure the dialog is visible. */
919 gtk_window_present(GTK_WINDOW(ui_widgets.open_fontsel));
923 static void
924 on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
926 gtk_widget_grab_focus(entry);
930 static void
931 on_input_entry_activate(GtkEntry *entry, GtkDialog *dialog)
933 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
937 static void
938 on_input_numeric_activate(GtkEntry *entry, GtkDialog *dialog)
940 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
944 typedef struct
946 GtkWidget *entry;
947 GtkWidget *combo;
949 GeanyInputCallback callback;
950 gpointer data;
952 InputDialogData;
955 static void
956 on_input_dialog_response(GtkDialog *dialog, gint response, InputDialogData *data)
958 if (response == GTK_RESPONSE_ACCEPT)
960 const gchar *str = gtk_entry_get_text(GTK_ENTRY(data->entry));
962 if (data->combo != NULL)
963 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(data->combo), str, 0);
965 data->callback(str, data->data);
967 gtk_widget_hide(GTK_WIDGET(dialog));
971 /* Create and display an input dialog.
972 * persistent: whether to remember previous entry text in a combo box;
973 * in this case the dialog returned is not destroyed on a response,
974 * and can be reshown.
975 * Returns: the dialog widget. */
976 static GtkWidget *
977 dialogs_show_input_full(const gchar *title, GtkWindow *parent,
978 const gchar *label_text, const gchar *default_text,
979 gboolean persistent, GeanyInputCallback input_cb, gpointer input_cb_data,
980 GCallback insert_text_cb, gpointer insert_text_cb_data)
982 GtkWidget *dialog, *vbox;
983 InputDialogData *data = g_malloc(sizeof *data);
985 dialog = gtk_dialog_new_with_buttons(title, parent,
986 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
987 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
988 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
989 gtk_widget_set_name(dialog, "GeanyDialog");
990 gtk_box_set_spacing(GTK_BOX(vbox), 6);
992 data->combo = NULL;
993 data->entry = NULL;
994 data->callback = input_cb;
995 data->data = input_cb_data;
997 if (label_text)
999 GtkWidget *label = gtk_label_new(label_text);
1000 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
1001 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1002 gtk_container_add(GTK_CONTAINER(vbox), label);
1005 if (persistent) /* remember previous entry text in a combo box */
1007 data->combo = gtk_combo_box_text_new_with_entry();
1008 data->entry = gtk_bin_get_child(GTK_BIN(data->combo));
1009 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
1010 gtk_container_add(GTK_CONTAINER(vbox), data->combo);
1012 else
1014 data->entry = gtk_entry_new();
1015 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
1016 gtk_container_add(GTK_CONTAINER(vbox), data->entry);
1019 if (default_text != NULL)
1021 gtk_entry_set_text(GTK_ENTRY(data->entry), default_text);
1023 gtk_entry_set_max_length(GTK_ENTRY(data->entry), 255);
1024 gtk_entry_set_width_chars(GTK_ENTRY(data->entry), 30);
1026 if (insert_text_cb != NULL)
1027 g_signal_connect(data->entry, "insert-text", insert_text_cb, insert_text_cb_data);
1028 g_signal_connect(data->entry, "activate", G_CALLBACK(on_input_entry_activate), dialog);
1029 g_signal_connect(dialog, "show", G_CALLBACK(on_input_dialog_show), data->entry);
1030 g_signal_connect_data(dialog, "response", G_CALLBACK(on_input_dialog_response), data, (GClosureNotify)g_free, 0);
1032 if (persistent)
1034 /* override default handler */
1035 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1036 gtk_widget_show_all(dialog);
1037 return dialog;
1039 gtk_widget_show_all(dialog);
1040 gtk_dialog_run(GTK_DIALOG(dialog));
1041 gtk_widget_destroy(dialog);
1042 return NULL;
1046 /* Remember previous entry text in a combo box.
1047 * Returns: the dialog widget. */
1048 GtkWidget *
1049 dialogs_show_input_persistent(const gchar *title, GtkWindow *parent,
1050 const gchar *label_text, const gchar *default_text,
1051 GeanyInputCallback input_cb, gpointer input_cb_data)
1053 return dialogs_show_input_full(title, parent, label_text, default_text, TRUE, input_cb, input_cb_data, NULL, NULL);
1057 static void on_dialog_input(const gchar *str, gpointer data)
1059 gchar **dialog_input = data;
1060 *dialog_input = g_strdup(str);
1064 /** Asks the user for text input.
1065 * @param title Dialog title.
1066 * @param parent The currently focused window, usually @c geany->main_widgets->window.
1067 * @c NULL can be used but is discouraged due to window manager effects.
1068 * @param label_text Label text, or @c NULL.
1069 * @param default_text Text to display in the input field, or @c NULL.
1070 * @return New copy of user input or @c NULL if cancelled.
1071 * @since 0.20. */
1072 GEANY_API_SYMBOL
1073 gchar *dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text,
1074 const gchar *default_text)
1076 gchar *dialog_input = NULL;
1077 dialogs_show_input_full(title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input, NULL, NULL);
1078 return dialog_input;
1082 /* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1083 /* Returns: newly allocated copy of the entry text or NULL on cancel.
1084 * Specialised variant for Goto Line dialog. */
1085 gchar *dialogs_show_input_goto_line(const gchar *title, GtkWindow *parent, const gchar *label_text,
1086 const gchar *default_text)
1088 gchar *dialog_input = NULL;
1089 dialogs_show_input_full(
1090 title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input,
1091 G_CALLBACK(ui_editable_insert_text_callback), NULL);
1092 return dialog_input;
1097 * Shows an input box to enter a numerical value using a GtkSpinButton.
1098 * If the dialog is aborted, @a value remains untouched.
1100 * @param title The dialog title.
1101 * @param label_text The shown dialog label.
1102 * @param value The default value for the spin button and the return location of the entered value.
1103 * Must be non-NULL.
1104 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1105 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1106 * @param step Increment added or subtracted by spinning the widget
1107 * (see documentation for @c gtk_spin_button_new_with_range()).
1109 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1111 * @since 0.16
1113 GEANY_API_SYMBOL
1114 gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text,
1115 gdouble *value, gdouble min, gdouble max, gdouble step)
1117 GtkWidget *dialog, *label, *spin, *vbox;
1118 gboolean res = FALSE;
1120 g_return_val_if_fail(title != NULL, FALSE);
1121 g_return_val_if_fail(label_text != NULL, FALSE);
1122 g_return_val_if_fail(value != NULL, FALSE);
1124 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
1125 GTK_DIALOG_DESTROY_WITH_PARENT,
1126 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1127 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1128 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1129 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1130 gtk_widget_set_name(dialog, "GeanyDialog");
1132 label = gtk_label_new(label_text);
1133 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1135 spin = gtk_spin_button_new_with_range(min, max, step);
1136 ui_entry_add_clear_icon(GTK_ENTRY(spin));
1137 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), *value);
1138 g_signal_connect(spin, "activate", G_CALLBACK(on_input_numeric_activate), dialog);
1140 gtk_container_add(GTK_CONTAINER(vbox), label);
1141 gtk_container_add(GTK_CONTAINER(vbox), spin);
1142 gtk_widget_show_all(vbox);
1144 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1146 *value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1147 res = TRUE;
1149 gtk_widget_destroy(dialog);
1151 return res;
1155 void dialogs_show_file_properties(GeanyDocument *doc)
1157 GtkWidget *dialog, *label, *image, *check;
1158 gchar *file_size, *title, *base_name, *time_changed, *time_modified, *time_accessed, *enctext;
1159 gchar *short_name;
1160 #ifdef HAVE_SYS_TYPES_H
1161 struct stat st;
1162 off_t filesize;
1163 mode_t mode;
1164 gchar *locale_filename;
1165 #else
1166 gint filesize = 0;
1167 gint mode = 0;
1168 #endif
1170 /* define this ones, to avoid later trouble */
1171 #ifndef S_IRUSR
1172 # define S_IRUSR 0
1173 # define S_IWUSR 0
1174 # define S_IXUSR 0
1175 #endif
1176 #ifndef S_IRGRP
1177 # define S_IRGRP 0
1178 # define S_IWGRP 0
1179 # define S_IXGRP 0
1180 # define S_IROTH 0
1181 # define S_IWOTH 0
1182 # define S_IXOTH 0
1183 #endif
1185 g_return_if_fail(doc == NULL || doc->is_valid);
1187 if (doc == NULL || doc->file_name == NULL)
1189 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1190 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1191 return;
1195 #ifdef HAVE_SYS_TYPES_H
1196 locale_filename = utils_get_locale_from_utf8(doc->file_name);
1197 if (g_stat(locale_filename, &st) == 0)
1199 /* first copy the returned string and the trim it, to not modify the static glibc string
1200 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1201 time_changed = g_strchomp(g_strdup(ctime(&st.st_ctime)));
1202 time_modified = g_strchomp(g_strdup(ctime(&st.st_mtime)));
1203 time_accessed = g_strchomp(g_strdup(ctime(&st.st_atime)));
1204 filesize = st.st_size;
1205 mode = st.st_mode;
1207 else
1209 time_changed = g_strdup(_("unknown"));
1210 time_modified = g_strdup(_("unknown"));
1211 time_accessed = g_strdup(_("unknown"));
1212 filesize = (off_t) 0;
1213 mode = (mode_t) 0;
1215 g_free(locale_filename);
1216 #else
1217 time_changed = g_strdup(_("unknown"));
1218 time_modified = g_strdup(_("unknown"));
1219 time_accessed = g_strdup(_("unknown"));
1220 #endif
1222 base_name = g_path_get_basename(doc->file_name);
1223 short_name = utils_str_middle_truncate(base_name, 30);
1224 title = g_strdup_printf(_("%s Properties"), short_name);
1225 dialog = ui_builder_get_object("properties_dialog");
1226 gtk_window_set_title(GTK_WINDOW(dialog), title);
1227 g_free(short_name);
1228 g_free(title);
1229 gtk_widget_set_name(dialog, "GeanyDialog");
1231 label = ui_lookup_widget(dialog, "file_name_label");
1232 gtk_label_set_text(GTK_LABEL(label), base_name);
1234 image = ui_lookup_widget(dialog, "file_type_image");
1235 gtk_image_set_from_gicon(GTK_IMAGE(image), doc->file_type->icon,
1236 GTK_ICON_SIZE_BUTTON);
1238 label = ui_lookup_widget(dialog, "file_type_label");
1239 gtk_label_set_text(GTK_LABEL(label), doc->file_type->title);
1241 label = ui_lookup_widget(dialog, "file_size_label");
1242 file_size = utils_make_human_readable_str(filesize, 1, 0);
1243 gtk_label_set_text(GTK_LABEL(label), file_size);
1244 g_free(file_size);
1246 label = ui_lookup_widget(dialog, "file_location_label");
1247 gtk_label_set_text(GTK_LABEL(label), doc->file_name);
1249 check = ui_lookup_widget(dialog, "file_read_only_check");
1250 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), doc->readonly);
1252 label = ui_lookup_widget(dialog, "file_encoding_label");
1253 enctext = g_strdup_printf("%s %s",
1254 doc->encoding,
1255 (encodings_is_unicode_charset(doc->encoding)) ?
1256 ((doc->has_bom) ? _("(with BOM)") : _("(without BOM)")) : "");
1257 gtk_label_set_text(GTK_LABEL(label), enctext);
1258 g_free(enctext);
1260 label = ui_lookup_widget(dialog, "file_modified_label");
1261 gtk_label_set_text(GTK_LABEL(label), time_modified);
1262 label = ui_lookup_widget(dialog, "file_changed_label");
1263 gtk_label_set_text(GTK_LABEL(label), time_changed);
1264 label = ui_lookup_widget(dialog, "file_accessed_label");
1265 gtk_label_set_text(GTK_LABEL(label), time_accessed);
1267 /* permissions */
1268 check = ui_lookup_widget(dialog, "file_perm_owner_r_check");
1269 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRUSR);
1270 check = ui_lookup_widget(dialog, "file_perm_owner_w_check");
1271 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWUSR);
1272 check = ui_lookup_widget(dialog, "file_perm_owner_x_check");
1273 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXUSR);
1274 check = ui_lookup_widget(dialog, "file_perm_group_r_check");
1275 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRGRP);
1276 check = ui_lookup_widget(dialog, "file_perm_group_w_check");
1277 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWGRP);
1278 check = ui_lookup_widget(dialog, "file_perm_group_x_check");
1279 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXGRP);
1280 check = ui_lookup_widget(dialog, "file_perm_other_r_check");
1281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IROTH);
1282 check = ui_lookup_widget(dialog, "file_perm_other_w_check");
1283 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWOTH);
1284 check = ui_lookup_widget(dialog, "file_perm_other_x_check");
1285 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXOTH);
1287 g_free(base_name);
1288 g_free(time_changed);
1289 g_free(time_modified);
1290 g_free(time_accessed);
1292 gtk_widget_show(dialog);
1296 /* extra_text can be NULL; otherwise it is displayed below main_text.
1297 * if parent is NULL, main_widgets.window will be used
1298 * btn_1, btn_2, btn_3 can be NULL.
1299 * returns response_1, response_2 or response_3 */
1300 static gint show_prompt(GtkWidget *parent,
1301 const gchar *btn_1, GtkResponseType response_1,
1302 const gchar *btn_2, GtkResponseType response_2,
1303 const gchar *btn_3, GtkResponseType response_3,
1304 const gchar *question_text, const gchar *extra_text)
1306 gboolean ret = FALSE;
1307 GtkWidget *dialog;
1308 GtkWidget *btn;
1310 if (btn_2 == NULL)
1312 btn_2 = GTK_STOCK_NO;
1313 response_2 = GTK_RESPONSE_NO;
1315 if (btn_3 == NULL)
1317 btn_3 = GTK_STOCK_YES;
1318 response_3 = GTK_RESPONSE_YES;
1321 #ifdef G_OS_WIN32
1322 /* our native dialog code doesn't support custom buttons */
1323 if (utils_str_equal(btn_3, GTK_STOCK_YES) &&
1324 utils_str_equal(btn_2, GTK_STOCK_NO) && btn_1 == NULL)
1326 gchar *string = (extra_text == NULL) ? g_strdup(question_text) :
1327 g_strconcat(question_text, "\n\n", extra_text, NULL);
1329 ret = win32_message_dialog(parent, GTK_MESSAGE_QUESTION, string);
1330 ret = ret ? response_3 : response_2;
1331 g_free(string);
1332 return ret;
1334 #endif
1335 if (parent == NULL && main_status.main_window_realized)
1336 parent = main_widgets.window;
1338 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
1339 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1340 GTK_BUTTONS_NONE, "%s", question_text);
1341 gtk_widget_set_name(dialog, "GeanyDialog");
1342 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1343 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
1345 /* question_text will be in bold if optional extra_text used */
1346 if (extra_text != NULL)
1347 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1348 "%s", extra_text);
1350 if (btn_1 != NULL)
1351 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_1, response_1);
1353 /* For a cancel button, use cancel response so user can press escape to cancel */
1354 btn = gtk_dialog_add_button(GTK_DIALOG(dialog), btn_2,
1355 utils_str_equal(btn_2, GTK_STOCK_CANCEL) ? GTK_RESPONSE_CANCEL : response_2);
1356 /* we don't want a default, but we need to override the apply button as default */
1357 gtk_widget_grab_default(btn);
1358 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_3, response_3);
1360 ret = gtk_dialog_run(GTK_DIALOG(dialog));
1361 gtk_widget_destroy(dialog);
1363 if (ret == GTK_RESPONSE_CANCEL)
1364 ret = response_2;
1365 return ret;
1370 * Shows a question message box with @a text and Yes/No buttons.
1371 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1372 * message dialog box is shown.
1374 * @param text Printf()-style format string.
1375 * @param ... Arguments for the @a text format string.
1377 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1379 GEANY_API_SYMBOL
1380 gboolean dialogs_show_question(const gchar *text, ...)
1382 gchar *string;
1383 va_list args;
1384 GtkWidget *parent = (main_status.main_window_realized) ? main_widgets.window : NULL;
1385 gint result;
1387 va_start(args, text);
1388 string = g_strdup_vprintf(text, args);
1389 va_end(args);
1390 result = show_prompt(parent,
1391 NULL, GTK_RESPONSE_NONE,
1392 GTK_STOCK_NO, GTK_RESPONSE_NO,
1393 GTK_STOCK_YES, GTK_RESPONSE_YES,
1394 string, NULL);
1395 g_free(string);
1396 return (result == GTK_RESPONSE_YES);
1400 /* extra_text can be NULL; otherwise it is displayed below main_text.
1401 * if parent is NULL, main_widgets.window will be used
1402 * yes_btn, no_btn can be NULL. */
1403 gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn,
1404 const gchar *extra_text, const gchar *main_text, ...)
1406 gint result;
1407 gchar *string;
1408 va_list args;
1410 va_start(args, main_text);
1411 string = g_strdup_vprintf(main_text, args);
1412 va_end(args);
1413 result = show_prompt(parent,
1414 NULL, GTK_RESPONSE_NONE,
1415 no_btn, GTK_RESPONSE_NO,
1416 yes_btn, GTK_RESPONSE_YES,
1417 string, extra_text);
1418 g_free(string);
1419 return (result == GTK_RESPONSE_YES);
1423 /* extra_text can be NULL; otherwise it is displayed below main_text.
1424 * if parent is NULL, main_widgets.window will be used
1425 * btn_1, btn_2, btn_3 can be NULL.
1426 * returns response_1, response_2 or response_3 */
1427 gint dialogs_show_prompt(GtkWidget *parent,
1428 const gchar *btn_1, GtkResponseType response_1,
1429 const gchar *btn_2, GtkResponseType response_2,
1430 const gchar *btn_3, GtkResponseType response_3,
1431 const gchar *extra_text, const gchar *main_text, ...)
1433 gchar *string;
1434 va_list args;
1435 gint result;
1437 va_start(args, main_text);
1438 string = g_strdup_vprintf(main_text, args);
1439 va_end(args);
1440 result = show_prompt(parent, btn_1, response_1, btn_2, response_2, btn_3, response_3,
1441 string, extra_text);
1442 g_free(string);
1443 return result;