Erlang: display module node in the symbols tree
[geany-mirror.git] / src / dialogs.c
blobccb54045ffeb63597bade01deeed945d5db421bd
1 /*
2 * dialogs.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005 The Geany contributors
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * File related dialogs, miscellaneous dialogs, font dialog.
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include "dialogs.h"
31 #include "app.h"
32 #include "build.h"
33 #include "document.h"
34 #include "encodings.h"
35 #include "encodingsprivate.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 <gtk/gtk.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 -1, /* 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 gboolean open_file_dialog_handle_response(GtkWidget *dialog, gint response)
127 gboolean ret = TRUE;
129 if (response == GTK_RESPONSE_ACCEPT || response == GEANY_RESPONSE_VIEW)
131 GSList *filelist;
132 GeanyFiletype *ft = NULL;
133 const gchar *charset = NULL;
134 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
135 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
136 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
137 gboolean ro = (response == GEANY_RESPONSE_VIEW); /* View clicked */
139 filesel_state.open.more_options_visible = gtk_expander_get_expanded(GTK_EXPANDER(expander));
140 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
141 filesel_state.open.filetype_idx = filetype_combo_box_get_active_filetype(GTK_COMBO_BOX(filetype_combo));
143 /* ignore detect from file item */
144 if (filesel_state.open.filetype_idx >= 0)
145 ft = filetypes_index(filesel_state.open.filetype_idx);
147 filesel_state.open.encoding_idx = ui_encodings_combo_box_get_active_encoding(GTK_COMBO_BOX(encoding_combo));
148 if (filesel_state.open.encoding_idx >= 0 && filesel_state.open.encoding_idx < GEANY_ENCODINGS_MAX)
149 charset = encodings[filesel_state.open.encoding_idx].charset;
151 filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
152 if (filelist != NULL)
154 const gchar *first = filelist->data;
156 // When there's only one filename it may have been typed manually
157 if (!filelist->next && !g_file_test(first, G_FILE_TEST_EXISTS))
159 dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("\"%s\" was not found."), first);
160 ret = FALSE;
162 else
164 document_open_files(filelist, ro, ft, charset);
166 g_slist_free_full(filelist, g_free);
169 if (app->project && !EMPTY(app->project->base_path))
170 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
171 app->project->base_path, NULL);
172 return ret;
176 static void on_file_open_show_hidden_notify(GObject *filechooser,
177 GParamSpec *pspec, gpointer data)
179 GtkWidget *toggle_button;
181 toggle_button = ui_lookup_widget(GTK_WIDGET(filechooser), "check_hidden");
183 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button),
184 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser)));
188 static void
189 on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, GtkWidget *dialog)
191 filesel_state.open.show_hidden = gtk_toggle_button_get_active(togglebutton);
192 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), filesel_state.open.show_hidden);
196 static void filetype_combo_cell_data_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell,
197 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
199 gboolean sensitive = !gtk_tree_model_iter_has_child(tree_model, iter);
200 gchar *text;
202 gtk_tree_model_get(tree_model, iter, 1, &text, -1);
203 g_object_set(cell, "sensitive", sensitive, "text", text, NULL);
204 g_free(text);
208 static GtkWidget *create_filetype_combo_box(void)
210 GtkTreeStore *store;
211 GtkTreeIter iter_compiled, iter_script, iter_markup, iter_misc, iter_detect;
212 GtkTreeIter *iter_parent;
213 GtkWidget *combo;
214 GtkCellRenderer *renderer;
215 GSList *node;
217 store = gtk_tree_store_new(2, G_TYPE_INT, G_TYPE_STRING);
219 gtk_tree_store_insert_with_values(store, &iter_detect, NULL, -1,
220 0, -1 /* auto-detect */, 1, _("Detect from file"), -1);
222 gtk_tree_store_insert_with_values(store, &iter_compiled, NULL, -1,
223 0, -1, 1, _("Programming Languages"), -1);
224 gtk_tree_store_insert_with_values(store, &iter_script, NULL, -1,
225 0, -1, 1, _("Scripting Languages"), -1);
226 gtk_tree_store_insert_with_values(store, &iter_markup, NULL, -1,
227 0, -1, 1, _("Markup Languages"), -1);
228 gtk_tree_store_insert_with_values(store, &iter_misc, NULL, -1,
229 0, -1, 1, _("Miscellaneous"), -1);
231 foreach_slist (node, filetypes_by_title)
233 GeanyFiletype *ft = node->data;
235 switch (ft->group)
237 case GEANY_FILETYPE_GROUP_COMPILED: iter_parent = &iter_compiled; break;
238 case GEANY_FILETYPE_GROUP_SCRIPT: iter_parent = &iter_script; break;
239 case GEANY_FILETYPE_GROUP_MARKUP: iter_parent = &iter_markup; break;
240 case GEANY_FILETYPE_GROUP_MISC: iter_parent = &iter_misc; break;
241 case GEANY_FILETYPE_GROUP_NONE:
242 default: iter_parent = NULL;
244 gtk_tree_store_insert_with_values(store, NULL, iter_parent, -1,
245 0, (gint) ft->id, 1, ft->title, -1);
248 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
249 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter_detect);
250 renderer = gtk_cell_renderer_text_new();
251 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
252 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
253 filetype_combo_cell_data_func, NULL, NULL);
255 g_object_unref(store);
257 return combo;
261 /* the filetype, or -1 for auto-detect */
262 static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo)
264 gint id = -1;
265 GtkTreeIter iter;
267 if (gtk_combo_box_get_active_iter(combo, &iter))
269 GtkTreeModel *model = gtk_combo_box_get_model(combo);
270 gtk_tree_model_get(model, &iter, 0, &id, -1);
272 return id;
276 static gboolean filetype_combo_box_set_active_filetype(GtkComboBox *combo, const gint id)
278 GtkTreeModel *model = gtk_combo_box_get_model(combo);
279 GtkTreeIter iter;
281 if (gtk_tree_model_get_iter_first(model, &iter))
285 gint row_id;
286 gtk_tree_model_get(model, &iter, 0, &row_id, -1);
287 if (id == row_id)
289 gtk_combo_box_set_active_iter(combo, &iter);
290 return TRUE;
293 while (ui_tree_model_iter_any_next(model, &iter, TRUE));
295 return FALSE;
299 static GtkWidget *add_file_open_extra_widget(GtkWidget *dialog)
301 GtkWidget *expander, *vbox, *table, *check_hidden;
302 GtkWidget *filetype_ebox, *filetype_label, *filetype_combo;
303 GtkWidget *encoding_ebox, *encoding_label, *encoding_combo;
305 expander = gtk_expander_new_with_mnemonic(_("_More Options"));
306 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
307 gtk_container_add(GTK_CONTAINER(expander), vbox);
309 table = gtk_table_new(2, 4, FALSE);
311 /* line 1 with checkbox and encoding combo */
312 check_hidden = gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
313 gtk_widget_show(check_hidden);
314 gtk_table_attach(GTK_TABLE(table), check_hidden, 0, 1, 0, 1,
315 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
316 (GtkAttachOptions) (0), 0, 5);
318 /* spacing */
319 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""), 1, 2, 0, 1,
320 (GtkAttachOptions) (GTK_FILL),
321 (GtkAttachOptions) (0), 5, 5);
323 encoding_label = gtk_label_new(_("Set encoding:"));
324 gtk_misc_set_alignment(GTK_MISC(encoding_label), 1, 0);
325 gtk_table_attach(GTK_TABLE(table), encoding_label, 2, 3, 0, 1,
326 (GtkAttachOptions) (GTK_FILL),
327 (GtkAttachOptions) (0), 4, 5);
328 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
329 encoding_ebox = gtk_event_box_new();
330 encoding_combo = ui_create_encodings_combo_box(TRUE, GEANY_ENCODINGS_MAX);
331 gtk_widget_set_tooltip_text(encoding_ebox,
332 _("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."));
333 gtk_container_add(GTK_CONTAINER(encoding_ebox), encoding_combo);
334 gtk_table_attach(GTK_TABLE(table), encoding_ebox, 3, 4, 0, 1,
335 (GtkAttachOptions) (GTK_FILL),
336 (GtkAttachOptions) (0), 0, 5);
338 /* line 2 with filetype combo */
339 filetype_label = gtk_label_new(_("Set filetype:"));
340 gtk_misc_set_alignment(GTK_MISC(filetype_label), 1, 0);
341 gtk_table_attach(GTK_TABLE(table), filetype_label, 2, 3, 1, 2,
342 (GtkAttachOptions) (GTK_FILL),
343 (GtkAttachOptions) (0), 4, 5);
344 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
345 filetype_ebox = gtk_event_box_new();
346 filetype_combo = create_filetype_combo_box();
347 gtk_widget_set_tooltip_text(filetype_ebox,
348 _("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."));
349 gtk_container_add(GTK_CONTAINER(filetype_ebox), filetype_combo);
350 gtk_table_attach(GTK_TABLE(table), filetype_ebox, 3, 4, 1, 2,
351 (GtkAttachOptions) (GTK_FILL),
352 (GtkAttachOptions) (0), 0, 5);
354 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
355 gtk_widget_show_all(vbox);
357 g_signal_connect(check_hidden, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled), dialog);
359 ui_hookup_widget(dialog, expander, "more_options_expander");
360 ui_hookup_widget(dialog, check_hidden, "check_hidden");
361 ui_hookup_widget(dialog, filetype_combo, "filetype_combo");
362 ui_hookup_widget(dialog, encoding_combo, "encoding_combo");
364 return expander;
368 static GtkWidget *create_open_file_dialog(void)
370 GtkWidget *dialog;
371 GtkWidget *viewbtn;
372 GSList *node;
374 dialog = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets.window),
375 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
376 gtk_widget_set_name(dialog, "GeanyDialog");
378 viewbtn = gtk_dialog_add_button(GTK_DIALOG(dialog), C_("Open dialog action", "_View"), GEANY_RESPONSE_VIEW);
379 gtk_widget_set_tooltip_text(viewbtn,
380 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
382 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
383 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
384 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
385 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
387 gtk_widget_set_size_request(dialog, -1, 460);
388 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
389 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
390 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
391 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
392 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
393 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
394 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
396 /* add checkboxes and filename entry */
397 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), add_file_open_extra_widget(dialog));
399 /* add FileFilters(start with "All Files") */
400 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
401 filetypes_create_file_filter(filetypes[GEANY_FILETYPES_NONE]));
402 /* now create meta filter "All Source" */
403 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
404 filetypes_create_file_filter_all_source());
405 foreach_slist(node, filetypes_by_title)
407 GeanyFiletype *ft = node->data;
409 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
410 continue;
411 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filetypes_create_file_filter(ft));
414 g_signal_connect(dialog, "notify::show-hidden",
415 G_CALLBACK(on_file_open_show_hidden_notify), NULL);
417 return dialog;
421 static void open_file_dialog_apply_settings(GtkWidget *dialog)
423 static gboolean initialized = FALSE;
424 GtkWidget *check_hidden = ui_lookup_widget(dialog, "check_hidden");
425 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
426 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
427 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
429 /* we can't know the initial position of combo boxes, so retrieve it the first time */
430 if (! initialized)
432 filesel_state.open.filter_idx = file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog));
434 initialized = TRUE;
436 else
438 file_chooser_set_filter_idx(GTK_FILE_CHOOSER(dialog), filesel_state.open.filter_idx);
440 gtk_expander_set_expanded(GTK_EXPANDER(expander), filesel_state.open.more_options_visible);
441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden), filesel_state.open.show_hidden);
442 ui_encodings_combo_box_set_active_encoding(GTK_COMBO_BOX(encoding_combo), filesel_state.open.encoding_idx);
443 filetype_combo_box_set_active_filetype(GTK_COMBO_BOX(filetype_combo), filesel_state.open.filetype_idx);
447 /* This shows the file selection dialog to open a file. */
448 void dialogs_show_open_file(void)
450 gchar *initdir;
451 GtkWidget *dialog;
453 /* set dialog directory to the current file's directory, if present */
454 initdir = utils_get_current_file_dir_utf8();
456 /* use project or default startup directory (if set) if no files are open */
457 /** TODO should it only be used when initially open the dialog and not on every show? */
458 if (! initdir)
459 initdir = g_strdup(utils_get_default_dir_utf8());
461 SETPTR(initdir, utils_get_locale_from_utf8(initdir));
463 dialog = create_open_file_dialog();
464 open_file_dialog_apply_settings(dialog);
466 if (initdir != NULL && g_path_is_absolute(initdir))
467 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), initdir);
469 if (app->project && !EMPTY(app->project->base_path))
470 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
471 app->project->base_path, NULL);
473 while (!open_file_dialog_handle_response(dialog,
474 gtk_dialog_run(GTK_DIALOG(dialog))));
475 gtk_widget_destroy(dialog);
477 g_free(initdir);
481 static gboolean handle_save_as(GeanyDocument *doc,
482 const gchar *utf8_filename, gboolean rename_file)
484 gboolean success = FALSE;
485 g_return_val_if_fail(DOC_VALID(doc), FALSE);
486 g_return_val_if_fail(!EMPTY(utf8_filename), FALSE);
488 if (doc->file_name != NULL)
490 if (rename_file)
492 document_rename_file(doc, utf8_filename);
494 if (doc->tm_file)
496 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
497 tm_workspace_remove_source_file(doc->tm_file);
498 tm_source_file_free(doc->tm_file);
499 doc->tm_file = NULL;
502 success = document_save_file_as(doc, utf8_filename);
504 build_menu_update(doc);
505 return success;
509 static gboolean save_as_dialog_handle_response(GeanyDocument *doc,
510 GtkWidget *dialog, gint response)
512 gboolean rename_file = FALSE;
513 gboolean success = FALSE;
514 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
516 switch (response)
518 case GEANY_RESPONSE_RENAME:
519 /* rename doesn't check for empty filename or overwriting */
520 if (G_UNLIKELY(EMPTY(new_filename)))
522 utils_beep();
523 break;
525 if (g_file_test(new_filename, G_FILE_TEST_EXISTS) &&
526 !dialogs_show_question_full(NULL, NULL, NULL,
527 _("Overwrite?"),
528 _("Filename already exists!")))
529 break;
530 rename_file = TRUE;
531 /* fall through */
532 case GTK_RESPONSE_ACCEPT:
534 gchar *utf8_filename;
536 utf8_filename = utils_get_utf8_from_locale(new_filename);
537 success = handle_save_as(doc, utf8_filename, rename_file);
538 g_free(utf8_filename);
539 break;
541 case GTK_RESPONSE_DELETE_EVENT:
542 case GTK_RESPONSE_CANCEL:
543 success = TRUE;
544 break;
546 g_free(new_filename);
548 return success;
552 static GtkWidget *create_save_file_dialog(GeanyDocument *doc)
554 GtkWidget *dialog, *rename_btn;
555 const gchar *initdir;
557 dialog = gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets.window),
558 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
559 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
560 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
561 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
562 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
563 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
564 gtk_widget_set_name(dialog, "GeanyDialog");
566 rename_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), _("R_ename"), GEANY_RESPONSE_RENAME);
567 gtk_widget_set_tooltip_text(rename_btn, _("Save the file and rename it"));
568 /* disable rename unless file exists on disk */
569 gtk_widget_set_sensitive(rename_btn, doc->real_path != NULL);
571 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
572 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
573 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
574 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
576 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
577 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
579 /* set the folder by default to the project base dir or the global pref for opening files */
580 initdir = utils_get_default_dir_utf8();
581 if (initdir)
583 gchar *linitdir = utils_get_locale_from_utf8(initdir);
584 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), linitdir);
585 g_free(linitdir);
587 return dialog;
591 static gboolean show_save_as_gtk(GeanyDocument *doc)
593 GtkWidget *dialog;
594 gint resp;
596 g_return_val_if_fail(DOC_VALID(doc), FALSE);
598 dialog = create_save_file_dialog(doc);
600 if (doc->file_name != NULL)
602 if (g_path_is_absolute(doc->file_name))
604 gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name);
605 gchar *locale_basename = g_path_get_basename(locale_filename);
606 gchar *locale_dirname = g_path_get_dirname(locale_filename);
608 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
609 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), locale_basename);
611 g_free(locale_filename);
612 g_free(locale_basename);
613 g_free(locale_dirname);
615 else
616 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), doc->file_name);
618 else
620 gchar *fname = NULL;
622 if (doc->file_type != NULL && doc->file_type->extension != NULL)
623 fname = g_strconcat(GEANY_STRING_UNTITLED, ".",
624 doc->file_type->extension, NULL);
625 else
626 fname = g_strdup(GEANY_STRING_UNTITLED);
628 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
630 g_free(fname);
633 if (app->project && !EMPTY(app->project->base_path))
634 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
635 app->project->base_path, NULL);
637 /* Run the dialog synchronously, pausing this function call */
640 resp = gtk_dialog_run(GTK_DIALOG(dialog));
642 while (! save_as_dialog_handle_response(doc, dialog, resp));
644 if (app->project && !EMPTY(app->project->base_path))
645 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
646 app->project->base_path, NULL);
648 gtk_widget_destroy(dialog);
650 return (resp == GTK_RESPONSE_ACCEPT);
655 * Shows the Save As dialog for the current notebook page.
657 * @return @c TRUE if the file was saved, otherwise @c FALSE.
659 GEANY_API_SYMBOL
660 gboolean dialogs_show_save_as(void)
662 GeanyDocument *doc = document_get_current();
663 gboolean result = FALSE;
665 g_return_val_if_fail(doc, FALSE);
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 GEANY_API_SYMBOL
712 void dialogs_show_msgbox(GtkMessageType type, const gchar *text, ...)
714 #ifndef G_OS_WIN32
715 GtkWidget *dialog;
716 #endif
717 gchar *string;
718 va_list args;
719 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
721 va_start(args, text);
722 string = g_strdup_vprintf(text, args);
723 va_end(args);
725 #ifdef G_OS_WIN32
726 win32_message_dialog(GTK_WIDGET(parent), type, string);
727 #else
728 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
729 type, GTK_BUTTONS_OK, "%s", string);
730 show_msgbox_dialog(dialog, type, parent);
731 #endif
732 g_free(string);
736 void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
738 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
739 #ifdef G_OS_WIN32
740 /* put the two strings together because Windows message boxes don't support secondary texts */
741 gchar *string = g_strconcat(text, "\n", secondary, NULL);
742 win32_message_dialog(GTK_WIDGET(parent), type, string);
743 g_free(string);
744 #else
745 GtkWidget *dialog;
746 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
747 type, GTK_BUTTONS_OK, "%s", text);
748 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
749 show_msgbox_dialog(dialog, type, parent);
750 #endif
754 static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
756 GtkWidget *dialog, *button;
757 gint ret;
759 dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
760 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
761 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
762 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg2);
763 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
765 button = ui_button_new_with_image(GTK_STOCK_CLEAR, _("_Don't save"));
766 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
767 gtk_widget_show(button);
769 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
771 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
772 ret = gtk_dialog_run(GTK_DIALOG(dialog));
774 gtk_widget_destroy(dialog);
776 return ret;
780 gboolean dialogs_show_unsaved_file(GeanyDocument *doc)
782 gchar *msg, *short_fn = NULL;
783 const gchar *msg2;
784 gint response;
785 gboolean old_quitting_state = main_status.quitting;
787 /* display the file tab to remind the user of the document */
788 main_status.quitting = FALSE;
789 document_show_tab(doc);
790 main_status.quitting = old_quitting_state;
792 short_fn = document_get_basename_for_display(doc, -1);
794 msg = g_strdup_printf(_("The file '%s' is not saved."), short_fn);
795 msg2 = _("Do you want to save it before closing?");
796 g_free(short_fn);
798 response = run_unsaved_dialog(msg, msg2);
799 g_free(msg);
801 switch (response)
803 case GTK_RESPONSE_YES:
804 /* document_save_file() returns the status if the file could be saved */
805 return document_save_file(doc, FALSE);
807 case GTK_RESPONSE_NO:
808 return TRUE;
810 case GTK_RESPONSE_CANCEL: /* fall through to default and leave the function */
811 default:
812 return FALSE;
817 static void
818 on_font_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
820 gboolean close = TRUE;
822 switch (response)
824 case GTK_RESPONSE_APPLY:
825 case GTK_RESPONSE_OK:
827 gchar *fontname;
829 fontname = gtk_font_chooser_get_font(
830 GTK_FONT_CHOOSER(ui_widgets.open_fontsel));
831 ui_set_editor_font(fontname);
832 g_free(fontname);
834 close = (response == GTK_RESPONSE_OK);
835 break;
839 if (close)
840 gtk_widget_hide(ui_widgets.open_fontsel);
844 /* This shows the font selection dialog to choose a font. */
845 void dialogs_show_open_font(void)
847 if (ui_widgets.open_fontsel == NULL)
849 GtkWidget *apply_button;
851 ui_widgets.open_fontsel = gtk_font_chooser_dialog_new(_("Choose font"), NULL);
852 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets.open_fontsel), 4);
853 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
854 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
855 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
856 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_fontsel), GDK_WINDOW_TYPE_HINT_DIALOG);
857 gtk_widget_set_name(ui_widgets.open_fontsel, "GeanyDialog");
859 apply_button = gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets.open_fontsel), GTK_RESPONSE_APPLY);
861 if (apply_button)
862 gtk_widget_show(apply_button);
864 g_signal_connect(ui_widgets.open_fontsel,
865 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
866 g_signal_connect(ui_widgets.open_fontsel,
867 "response", G_CALLBACK(on_font_dialog_response), NULL);
869 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_fontsel), GTK_WINDOW(main_widgets.window));
871 gtk_font_chooser_set_font(
872 GTK_FONT_CHOOSER(ui_widgets.open_fontsel), interface_prefs.editor_font);
873 /* We make sure the dialog is visible. */
874 gtk_window_present(GTK_WINDOW(ui_widgets.open_fontsel));
878 static void
879 on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
881 gtk_widget_grab_focus(entry);
885 static void
886 on_input_entry_activate(GtkEntry *entry, GtkDialog *dialog)
888 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
892 static void
893 on_input_numeric_activate(GtkEntry *entry, GtkDialog *dialog)
895 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
899 typedef struct
901 GtkWidget *entry;
902 GtkWidget *combo;
904 GeanyInputCallback callback;
905 gpointer data;
907 InputDialogData;
910 static void
911 on_input_dialog_response(GtkDialog *dialog, gint response, InputDialogData *data)
913 if (response == GTK_RESPONSE_ACCEPT)
915 const gchar *str = gtk_entry_get_text(GTK_ENTRY(data->entry));
917 if (data->combo != NULL)
918 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(data->combo), str, 0);
920 data->callback(str, data->data);
922 gtk_widget_hide(GTK_WIDGET(dialog));
926 /* Create and display an input dialog.
927 * persistent: whether to remember previous entry text in a combo box;
928 * in this case the dialog returned is not destroyed on a response,
929 * and can be reshown.
930 * Returns: the dialog widget. */
931 static GtkWidget *
932 dialogs_show_input_full(const gchar *title, GtkWindow *parent,
933 const gchar *label_text, const gchar *default_text,
934 gboolean persistent, GeanyInputCallback input_cb, gpointer input_cb_data,
935 GCallback insert_text_cb, gpointer insert_text_cb_data)
937 GtkWidget *dialog, *vbox;
938 InputDialogData *data = g_malloc(sizeof *data);
940 dialog = gtk_dialog_new_with_buttons(title, parent,
941 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
942 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
943 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
944 gtk_widget_set_name(dialog, "GeanyDialog");
945 gtk_box_set_spacing(GTK_BOX(vbox), 6);
947 data->combo = NULL;
948 data->entry = NULL;
949 data->callback = input_cb;
950 data->data = input_cb_data;
952 if (label_text)
954 GtkWidget *label = gtk_label_new(label_text);
955 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
956 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
957 gtk_container_add(GTK_CONTAINER(vbox), label);
960 if (persistent) /* remember previous entry text in a combo box */
962 data->combo = gtk_combo_box_text_new_with_entry();
963 data->entry = gtk_bin_get_child(GTK_BIN(data->combo));
964 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
965 gtk_container_add(GTK_CONTAINER(vbox), data->combo);
967 else
969 data->entry = gtk_entry_new();
970 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
971 gtk_container_add(GTK_CONTAINER(vbox), data->entry);
974 if (default_text != NULL)
976 gtk_entry_set_text(GTK_ENTRY(data->entry), default_text);
978 gtk_entry_set_max_length(GTK_ENTRY(data->entry), 255);
979 gtk_entry_set_width_chars(GTK_ENTRY(data->entry), 30);
981 if (insert_text_cb != NULL)
982 g_signal_connect(data->entry, "insert-text", insert_text_cb, insert_text_cb_data);
983 g_signal_connect(data->entry, "activate", G_CALLBACK(on_input_entry_activate), dialog);
984 g_signal_connect(dialog, "show", G_CALLBACK(on_input_dialog_show), data->entry);
985 g_signal_connect_data(dialog, "response", G_CALLBACK(on_input_dialog_response), data, CLOSURE_NOTIFY(g_free), 0);
987 if (persistent)
989 /* override default handler */
990 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
991 gtk_widget_show_all(dialog);
992 return dialog;
994 gtk_widget_show_all(dialog);
995 gtk_dialog_run(GTK_DIALOG(dialog));
996 gtk_widget_destroy(dialog);
997 return NULL;
1001 /* Remember previous entry text in a combo box.
1002 * Returns: the dialog widget. */
1003 GtkWidget *
1004 dialogs_show_input_persistent(const gchar *title, GtkWindow *parent,
1005 const gchar *label_text, const gchar *default_text,
1006 GeanyInputCallback input_cb, gpointer input_cb_data)
1008 return dialogs_show_input_full(title, parent, label_text, default_text, TRUE, input_cb, input_cb_data, NULL, NULL);
1012 static void on_dialog_input(const gchar *str, gpointer data)
1014 gchar **dialog_input = data;
1015 *dialog_input = g_strdup(str);
1019 /** Asks the user for text input.
1020 * @param title Dialog title.
1021 * @param parent @nullable The currently focused window, usually @c geany->main_widgets->window.
1022 * @c NULL can be used but is discouraged due to window manager effects.
1023 * @param label_text @nullable Label text, or @c NULL.
1024 * @param default_text @nullable Text to display in the input field, or @c NULL.
1025 * @return @nullable New copy of user input or @c NULL if cancelled.
1026 * @since 0.20. */
1027 GEANY_API_SYMBOL
1028 gchar *dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text,
1029 const gchar *default_text)
1031 gchar *dialog_input = NULL;
1032 dialogs_show_input_full(title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input, NULL, NULL);
1033 return dialog_input;
1037 /* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1038 /* Returns: newly allocated copy of the entry text or NULL on cancel.
1039 * Specialised variant for Goto Line dialog. */
1040 gchar *dialogs_show_input_goto_line(const gchar *title, GtkWindow *parent, const gchar *label_text,
1041 const gchar *default_text)
1043 gchar *dialog_input = NULL;
1044 dialogs_show_input_full(
1045 title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input,
1046 G_CALLBACK(ui_editable_insert_text_callback), NULL);
1047 return dialog_input;
1052 * Shows an input box to enter a numerical value using a GtkSpinButton.
1053 * If the dialog is aborted, @a value remains untouched.
1055 * @param title The dialog title.
1056 * @param label_text The shown dialog label.
1057 * @param value The default value for the spin button and the return location of the entered value.
1058 * Must be non-NULL.
1059 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1060 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1061 * @param step Increment added or subtracted by spinning the widget
1062 * (see documentation for @c gtk_spin_button_new_with_range()).
1064 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1066 * @since 0.16
1068 GEANY_API_SYMBOL
1069 gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text,
1070 gdouble *value, gdouble min, gdouble max, gdouble step)
1072 GtkWidget *dialog, *label, *spin, *vbox;
1073 gboolean res = FALSE;
1075 g_return_val_if_fail(title != NULL, FALSE);
1076 g_return_val_if_fail(label_text != NULL, FALSE);
1077 g_return_val_if_fail(value != NULL, FALSE);
1079 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
1080 GTK_DIALOG_DESTROY_WITH_PARENT,
1081 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1082 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1083 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1084 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1085 gtk_widget_set_name(dialog, "GeanyDialog");
1087 label = gtk_label_new(label_text);
1088 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1090 spin = gtk_spin_button_new_with_range(min, max, step);
1091 ui_entry_add_clear_icon(GTK_ENTRY(spin));
1092 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), *value);
1093 g_signal_connect(spin, "activate", G_CALLBACK(on_input_numeric_activate), dialog);
1095 gtk_container_add(GTK_CONTAINER(vbox), label);
1096 gtk_container_add(GTK_CONTAINER(vbox), spin);
1097 gtk_widget_show_all(vbox);
1099 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1101 *value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1102 res = TRUE;
1104 gtk_widget_destroy(dialog);
1106 return res;
1110 void dialogs_show_file_properties(GeanyDocument *doc)
1112 GtkWidget *dialog, *label, *image, *check;
1113 gchar *file_size, *title, *base_name, *time_changed, *time_modified, *time_accessed, *enctext;
1114 gchar *short_name;
1115 #ifdef HAVE_SYS_TYPES_H
1116 GStatBuf st;
1117 off_t filesize;
1118 mode_t mode;
1119 gchar *locale_filename;
1120 #else
1121 gint filesize = 0;
1122 gint mode = 0;
1123 #endif
1125 /* define this ones, to avoid later trouble */
1126 #ifndef S_IRUSR
1127 # define S_IRUSR 0
1128 # define S_IWUSR 0
1129 # define S_IXUSR 0
1130 #endif
1131 #ifndef S_IRGRP
1132 # define S_IRGRP 0
1133 # define S_IWGRP 0
1134 # define S_IXGRP 0
1135 # define S_IROTH 0
1136 # define S_IWOTH 0
1137 # define S_IXOTH 0
1138 #endif
1140 g_return_if_fail(doc == NULL || doc->is_valid);
1142 if (doc == NULL || doc->file_name == NULL)
1144 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1145 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1146 return;
1150 #ifdef HAVE_SYS_TYPES_H
1151 locale_filename = utils_get_locale_from_utf8(doc->file_name);
1152 if (g_stat(locale_filename, &st) == 0)
1154 /* first copy the returned string and the trim it, to not modify the static glibc string
1155 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1156 time_changed = g_strchomp(g_strdup(ctime(&st.st_ctime)));
1157 time_modified = g_strchomp(g_strdup(ctime(&st.st_mtime)));
1158 time_accessed = g_strchomp(g_strdup(ctime(&st.st_atime)));
1159 filesize = st.st_size;
1160 mode = st.st_mode;
1162 else
1164 time_changed = g_strdup(_("unknown"));
1165 time_modified = g_strdup(_("unknown"));
1166 time_accessed = g_strdup(_("unknown"));
1167 filesize = (off_t) 0;
1168 mode = (mode_t) 0;
1170 g_free(locale_filename);
1171 #else
1172 time_changed = g_strdup(_("unknown"));
1173 time_modified = g_strdup(_("unknown"));
1174 time_accessed = g_strdup(_("unknown"));
1175 #endif
1177 base_name = g_path_get_basename(doc->file_name);
1178 short_name = utils_str_middle_truncate(base_name, 30);
1179 title = g_strdup_printf(_("%s Properties"), short_name);
1180 dialog = ui_builder_get_object("properties_dialog");
1181 gtk_window_set_title(GTK_WINDOW(dialog), title);
1182 g_free(short_name);
1183 g_free(title);
1184 gtk_widget_set_name(dialog, "GeanyDialog");
1186 label = ui_lookup_widget(dialog, "file_name_label");
1187 gtk_label_set_text(GTK_LABEL(label), base_name);
1189 image = ui_lookup_widget(dialog, "file_type_image");
1190 gtk_image_set_from_gicon(GTK_IMAGE(image), doc->file_type->icon,
1191 GTK_ICON_SIZE_BUTTON);
1193 label = ui_lookup_widget(dialog, "file_type_label");
1194 gtk_label_set_text(GTK_LABEL(label), doc->file_type->title);
1196 label = ui_lookup_widget(dialog, "file_size_label");
1197 file_size = g_format_size(filesize);
1198 gtk_label_set_text(GTK_LABEL(label), file_size);
1199 g_free(file_size);
1201 label = ui_lookup_widget(dialog, "file_location_label");
1202 gtk_label_set_text(GTK_LABEL(label), doc->file_name);
1204 check = ui_lookup_widget(dialog, "file_read_only_check");
1205 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), doc->readonly);
1207 label = ui_lookup_widget(dialog, "file_encoding_label");
1208 enctext = g_strdup_printf("%s %s",
1209 doc->encoding,
1210 (encodings_is_unicode_charset(doc->encoding)) ?
1211 ((doc->has_bom) ? _("(with BOM)") : _("(without BOM)")) : "");
1212 gtk_label_set_text(GTK_LABEL(label), enctext);
1213 g_free(enctext);
1215 label = ui_lookup_widget(dialog, "file_modified_label");
1216 gtk_label_set_text(GTK_LABEL(label), time_modified);
1217 label = ui_lookup_widget(dialog, "file_changed_label");
1218 gtk_label_set_text(GTK_LABEL(label), time_changed);
1219 label = ui_lookup_widget(dialog, "file_accessed_label");
1220 gtk_label_set_text(GTK_LABEL(label), time_accessed);
1222 /* permissions */
1223 check = ui_lookup_widget(dialog, "file_perm_owner_r_check");
1224 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRUSR);
1225 check = ui_lookup_widget(dialog, "file_perm_owner_w_check");
1226 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWUSR);
1227 check = ui_lookup_widget(dialog, "file_perm_owner_x_check");
1228 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXUSR);
1229 check = ui_lookup_widget(dialog, "file_perm_group_r_check");
1230 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRGRP);
1231 check = ui_lookup_widget(dialog, "file_perm_group_w_check");
1232 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWGRP);
1233 check = ui_lookup_widget(dialog, "file_perm_group_x_check");
1234 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXGRP);
1235 check = ui_lookup_widget(dialog, "file_perm_other_r_check");
1236 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IROTH);
1237 check = ui_lookup_widget(dialog, "file_perm_other_w_check");
1238 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWOTH);
1239 check = ui_lookup_widget(dialog, "file_perm_other_x_check");
1240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXOTH);
1242 g_free(base_name);
1243 g_free(time_changed);
1244 g_free(time_modified);
1245 g_free(time_accessed);
1247 gtk_widget_show(dialog);
1251 /* extra_text can be NULL; otherwise it is displayed below main_text.
1252 * if parent is NULL, main_widgets.window will be used
1253 * btn_1, btn_2, btn_3 can be NULL.
1254 * returns response_1, response_2, response_3, or GTK_RESPONSE_DELETE_EVENT if the dialog was discarded */
1255 static gint show_prompt(GtkWidget *parent,
1256 const gchar *btn_1, GtkResponseType response_1,
1257 const gchar *btn_2, GtkResponseType response_2,
1258 const gchar *btn_3, GtkResponseType response_3,
1259 const gchar *question_text, const gchar *extra_text)
1261 gboolean ret = FALSE;
1262 GtkWidget *dialog;
1263 GtkWidget *btn;
1265 if (btn_2 == NULL)
1267 btn_2 = GTK_STOCK_NO;
1268 response_2 = GTK_RESPONSE_NO;
1270 if (btn_3 == NULL)
1272 btn_3 = GTK_STOCK_YES;
1273 response_3 = GTK_RESPONSE_YES;
1276 #ifdef G_OS_WIN32
1277 /* our native dialog code doesn't support custom buttons */
1278 if (utils_str_equal(btn_3, GTK_STOCK_YES) &&
1279 utils_str_equal(btn_2, GTK_STOCK_NO) && btn_1 == NULL)
1281 gchar *string = (extra_text == NULL) ? g_strdup(question_text) :
1282 g_strconcat(question_text, "\n\n", extra_text, NULL);
1284 ret = win32_message_dialog(parent, GTK_MESSAGE_QUESTION, string);
1285 ret = ret ? response_3 : response_2;
1286 g_free(string);
1287 return ret;
1289 #endif
1290 if (parent == NULL && main_status.main_window_realized)
1291 parent = main_widgets.window;
1293 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
1294 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1295 GTK_BUTTONS_NONE, "%s", question_text);
1296 gtk_widget_set_name(dialog, "GeanyDialog");
1297 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1298 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
1300 /* question_text will be in bold if optional extra_text used */
1301 if (extra_text != NULL)
1302 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1303 "%s", extra_text);
1305 if (btn_1 != NULL)
1306 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_1, response_1);
1308 btn = gtk_dialog_add_button(GTK_DIALOG(dialog), btn_2, response_2);
1309 /* we don't want a default, but we need to override the apply button as default */
1310 gtk_widget_grab_default(btn);
1311 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_3, response_3);
1313 ret = gtk_dialog_run(GTK_DIALOG(dialog));
1314 gtk_widget_destroy(dialog);
1316 return ret;
1321 * Shows a question message box with @a text and Yes/No buttons.
1322 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1323 * message dialog box is shown.
1325 * @param text Printf()-style format string.
1326 * @param ... Arguments for the @a text format string.
1328 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1330 GEANY_API_SYMBOL
1331 gboolean dialogs_show_question(const gchar *text, ...)
1333 gchar *string;
1334 va_list args;
1335 GtkWidget *parent = (main_status.main_window_realized) ? main_widgets.window : NULL;
1336 gint result;
1338 va_start(args, text);
1339 string = g_strdup_vprintf(text, args);
1340 va_end(args);
1341 result = show_prompt(parent,
1342 NULL, GTK_RESPONSE_NONE,
1343 GTK_STOCK_NO, GTK_RESPONSE_NO,
1344 GTK_STOCK_YES, GTK_RESPONSE_YES,
1345 string, NULL);
1346 g_free(string);
1347 return (result == GTK_RESPONSE_YES);
1351 /* extra_text can be NULL; otherwise it is displayed below main_text.
1352 * if parent is NULL, main_widgets.window will be used
1353 * yes_btn, no_btn can be NULL. */
1354 gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn,
1355 const gchar *extra_text, const gchar *main_text, ...)
1357 gint result;
1358 gchar *string;
1359 va_list args;
1361 va_start(args, main_text);
1362 string = g_strdup_vprintf(main_text, args);
1363 va_end(args);
1364 result = show_prompt(parent,
1365 NULL, GTK_RESPONSE_NONE,
1366 no_btn, GTK_RESPONSE_NO,
1367 yes_btn, GTK_RESPONSE_YES,
1368 string, extra_text);
1369 g_free(string);
1370 return (result == GTK_RESPONSE_YES);
1374 /* extra_text can be NULL; otherwise it is displayed below main_text.
1375 * if parent is NULL, main_widgets.window will be used
1376 * btn_1, btn_2, btn_3 can be NULL.
1377 * returns response_1, response_2 or response_3 */
1378 gint dialogs_show_prompt(GtkWidget *parent,
1379 const gchar *btn_1, GtkResponseType response_1,
1380 const gchar *btn_2, GtkResponseType response_2,
1381 const gchar *btn_3, GtkResponseType response_3,
1382 const gchar *extra_text, const gchar *main_text, ...)
1384 gchar *string;
1385 va_list args;
1386 gint result;
1388 va_start(args, main_text);
1389 string = g_strdup_vprintf(main_text, args);
1390 va_end(args);
1391 result = show_prompt(parent, btn_1, response_1, btn_2, response_2, btn_3, response_3,
1392 string, extra_text);
1393 g_free(string);
1394 return result;