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.
34 #include "encodings.h"
35 #include "encodingsprivate.h"
36 #include "filetypes.h"
44 #include <gdk/gdkkeysyms.h>
47 #ifdef HAVE_SYS_TIME_H
48 # include <sys/time.h>
52 #ifdef HAVE_SYS_TYPES_H
53 # include <sys/types.h>
56 /* gstdio.h also includes sys/stat.h */
57 #include <glib/gstdio.h>
62 GEANY_RESPONSE_RENAME
,
67 static struct FileSelState
75 gboolean more_options_visible
;
81 GEANY_ENCODINGS_MAX
, /* default encoding is detect from file */
82 -1, /* default filetype is detect from extension */
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
)
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
)
107 g_slist_free(filters
);
112 /* sets the current file filter from its ID */
113 static void file_chooser_set_filter_idx(GtkFileChooser
*chooser
, guint idx
)
115 GtkFileFilter
*current
;
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
)
129 if (response
== GTK_RESPONSE_ACCEPT
|| response
== GEANY_RESPONSE_VIEW
)
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
);
164 document_open_files(filelist
, ro
, ft
, charset
);
166 g_slist_foreach(filelist
, (GFunc
) g_free
, NULL
); /* free filenames */
168 g_slist_free(filelist
);
170 if (app
->project
&& !EMPTY(app
->project
->base_path
))
171 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
172 app
->project
->base_path
, NULL
);
177 static void on_file_open_show_hidden_notify(GObject
*filechooser
,
178 GParamSpec
*pspec
, gpointer data
)
180 GtkWidget
*toggle_button
;
182 toggle_button
= ui_lookup_widget(GTK_WIDGET(filechooser
), "check_hidden");
184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button
),
185 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser
)));
190 on_file_open_check_hidden_toggled(GtkToggleButton
*togglebutton
, GtkWidget
*dialog
)
192 filesel_state
.open
.show_hidden
= gtk_toggle_button_get_active(togglebutton
);
193 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog
), filesel_state
.open
.show_hidden
);
197 static void filetype_combo_cell_data_func(GtkCellLayout
*cell_layout
, GtkCellRenderer
*cell
,
198 GtkTreeModel
*tree_model
, GtkTreeIter
*iter
, gpointer data
)
200 gboolean sensitive
= !gtk_tree_model_iter_has_child(tree_model
, iter
);
203 gtk_tree_model_get(tree_model
, iter
, 1, &text
, -1);
204 g_object_set(cell
, "sensitive", sensitive
, "text", text
, NULL
);
209 static GtkWidget
*create_filetype_combo_box(void)
212 GtkTreeIter iter_compiled
, iter_script
, iter_markup
, iter_misc
, iter_detect
;
213 GtkTreeIter
*iter_parent
;
215 GtkCellRenderer
*renderer
;
218 store
= gtk_tree_store_new(2, G_TYPE_INT
, G_TYPE_STRING
);
220 gtk_tree_store_insert_with_values(store
, &iter_detect
, NULL
, -1,
221 0, -1 /* auto-detect */, 1, _("Detect from file"), -1);
223 gtk_tree_store_insert_with_values(store
, &iter_compiled
, NULL
, -1,
224 0, -1, 1, _("Programming Languages"), -1);
225 gtk_tree_store_insert_with_values(store
, &iter_script
, NULL
, -1,
226 0, -1, 1, _("Scripting Languages"), -1);
227 gtk_tree_store_insert_with_values(store
, &iter_markup
, NULL
, -1,
228 0, -1, 1, _("Markup Languages"), -1);
229 gtk_tree_store_insert_with_values(store
, &iter_misc
, NULL
, -1,
230 0, -1, 1, _("Miscellaneous"), -1);
232 foreach_slist (node
, filetypes_by_title
)
234 GeanyFiletype
*ft
= node
->data
;
238 case GEANY_FILETYPE_GROUP_COMPILED
: iter_parent
= &iter_compiled
; break;
239 case GEANY_FILETYPE_GROUP_SCRIPT
: iter_parent
= &iter_script
; break;
240 case GEANY_FILETYPE_GROUP_MARKUP
: iter_parent
= &iter_markup
; break;
241 case GEANY_FILETYPE_GROUP_MISC
: iter_parent
= &iter_misc
; break;
242 case GEANY_FILETYPE_GROUP_NONE
:
243 default: iter_parent
= NULL
;
245 gtk_tree_store_insert_with_values(store
, NULL
, iter_parent
, -1,
246 0, (gint
) ft
->id
, 1, ft
->title
, -1);
249 combo
= gtk_combo_box_new_with_model(GTK_TREE_MODEL(store
));
250 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo
), &iter_detect
);
251 renderer
= gtk_cell_renderer_text_new();
252 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo
), renderer
, TRUE
);
253 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo
), renderer
,
254 filetype_combo_cell_data_func
, NULL
, NULL
);
256 g_object_unref(store
);
262 /* the filetype, or -1 for auto-detect */
263 static gint
filetype_combo_box_get_active_filetype(GtkComboBox
*combo
)
268 if (gtk_combo_box_get_active_iter(combo
, &iter
))
270 GtkTreeModel
*model
= gtk_combo_box_get_model(combo
);
271 gtk_tree_model_get(model
, &iter
, 0, &id
, -1);
277 static gboolean
filetype_combo_box_set_active_filetype(GtkComboBox
*combo
, const gint id
)
279 GtkTreeModel
*model
= gtk_combo_box_get_model(combo
);
282 if (gtk_tree_model_get_iter_first(model
, &iter
))
287 gtk_tree_model_get(model
, &iter
, 0, &row_id
, -1);
290 gtk_combo_box_set_active_iter(combo
, &iter
);
294 while (ui_tree_model_iter_any_next(model
, &iter
, TRUE
));
300 static GtkWidget
*add_file_open_extra_widget(GtkWidget
*dialog
)
302 GtkWidget
*expander
, *vbox
, *table
, *check_hidden
;
303 GtkWidget
*filetype_ebox
, *filetype_label
, *filetype_combo
;
304 GtkWidget
*encoding_ebox
, *encoding_label
, *encoding_combo
;
306 expander
= gtk_expander_new_with_mnemonic(_("_More Options"));
307 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6);
308 gtk_container_add(GTK_CONTAINER(expander
), vbox
);
310 table
= gtk_table_new(2, 4, FALSE
);
312 /* line 1 with checkbox and encoding combo */
313 check_hidden
= gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
314 gtk_widget_show(check_hidden
);
315 gtk_table_attach(GTK_TABLE(table
), check_hidden
, 0, 1, 0, 1,
316 (GtkAttachOptions
) (GTK_FILL
| GTK_EXPAND
),
317 (GtkAttachOptions
) (0), 0, 5);
320 gtk_table_attach(GTK_TABLE(table
), gtk_label_new(""), 1, 2, 0, 1,
321 (GtkAttachOptions
) (GTK_FILL
),
322 (GtkAttachOptions
) (0), 5, 5);
324 encoding_label
= gtk_label_new(_("Set encoding:"));
325 gtk_misc_set_alignment(GTK_MISC(encoding_label
), 1, 0);
326 gtk_table_attach(GTK_TABLE(table
), encoding_label
, 2, 3, 0, 1,
327 (GtkAttachOptions
) (GTK_FILL
),
328 (GtkAttachOptions
) (0), 4, 5);
329 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
330 encoding_ebox
= gtk_event_box_new();
331 encoding_combo
= ui_create_encodings_combo_box(TRUE
, GEANY_ENCODINGS_MAX
);
332 gtk_widget_set_tooltip_text(encoding_ebox
,
333 _("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."));
334 gtk_container_add(GTK_CONTAINER(encoding_ebox
), encoding_combo
);
335 gtk_table_attach(GTK_TABLE(table
), encoding_ebox
, 3, 4, 0, 1,
336 (GtkAttachOptions
) (GTK_FILL
),
337 (GtkAttachOptions
) (0), 0, 5);
339 /* line 2 with filetype combo */
340 filetype_label
= gtk_label_new(_("Set filetype:"));
341 gtk_misc_set_alignment(GTK_MISC(filetype_label
), 1, 0);
342 gtk_table_attach(GTK_TABLE(table
), filetype_label
, 2, 3, 1, 2,
343 (GtkAttachOptions
) (GTK_FILL
),
344 (GtkAttachOptions
) (0), 4, 5);
345 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
346 filetype_ebox
= gtk_event_box_new();
347 filetype_combo
= create_filetype_combo_box();
348 gtk_widget_set_tooltip_text(filetype_ebox
,
349 _("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."));
350 gtk_container_add(GTK_CONTAINER(filetype_ebox
), filetype_combo
);
351 gtk_table_attach(GTK_TABLE(table
), filetype_ebox
, 3, 4, 1, 2,
352 (GtkAttachOptions
) (GTK_FILL
),
353 (GtkAttachOptions
) (0), 0, 5);
355 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
356 gtk_widget_show_all(vbox
);
358 g_signal_connect(check_hidden
, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled
), dialog
);
360 ui_hookup_widget(dialog
, expander
, "more_options_expander");
361 ui_hookup_widget(dialog
, check_hidden
, "check_hidden");
362 ui_hookup_widget(dialog
, filetype_combo
, "filetype_combo");
363 ui_hookup_widget(dialog
, encoding_combo
, "encoding_combo");
369 static GtkWidget
*create_open_file_dialog(void)
375 dialog
= gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets
.window
),
376 GTK_FILE_CHOOSER_ACTION_OPEN
, NULL
, NULL
);
377 gtk_widget_set_name(dialog
, "GeanyDialog");
379 viewbtn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), C_("Open dialog action", "_View"), GEANY_RESPONSE_VIEW
);
380 gtk_widget_set_tooltip_text(viewbtn
,
381 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
383 gtk_dialog_add_buttons(GTK_DIALOG(dialog
),
384 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
385 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
, NULL
);
386 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
388 gtk_widget_set_size_request(dialog
, -1, 460);
389 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
390 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog
), TRUE
);
391 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog
), FALSE
);
392 gtk_window_set_type_hint(GTK_WINDOW(dialog
), GDK_WINDOW_TYPE_HINT_DIALOG
);
393 gtk_window_set_transient_for(GTK_WINDOW(dialog
), GTK_WINDOW(main_widgets
.window
));
394 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog
), TRUE
);
395 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog
), FALSE
);
397 /* add checkboxes and filename entry */
398 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog
), add_file_open_extra_widget(dialog
));
400 /* add FileFilters(start with "All Files") */
401 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),
402 filetypes_create_file_filter(filetypes
[GEANY_FILETYPES_NONE
]));
403 /* now create meta filter "All Source" */
404 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),
405 filetypes_create_file_filter_all_source());
406 foreach_slist(node
, filetypes_by_title
)
408 GeanyFiletype
*ft
= node
->data
;
410 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
412 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
), filetypes_create_file_filter(ft
));
415 g_signal_connect(dialog
, "notify::show-hidden",
416 G_CALLBACK(on_file_open_show_hidden_notify
), NULL
);
422 static void open_file_dialog_apply_settings(GtkWidget
*dialog
)
424 static gboolean initialized
= FALSE
;
425 GtkWidget
*check_hidden
= ui_lookup_widget(dialog
, "check_hidden");
426 GtkWidget
*filetype_combo
= ui_lookup_widget(dialog
, "filetype_combo");
427 GtkWidget
*encoding_combo
= ui_lookup_widget(dialog
, "encoding_combo");
428 GtkWidget
*expander
= ui_lookup_widget(dialog
, "more_options_expander");
430 /* we can't know the initial position of combo boxes, so retrieve it the first time */
433 filesel_state
.open
.filter_idx
= file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog
));
439 file_chooser_set_filter_idx(GTK_FILE_CHOOSER(dialog
), filesel_state
.open
.filter_idx
);
441 gtk_expander_set_expanded(GTK_EXPANDER(expander
), filesel_state
.open
.more_options_visible
);
442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden
), filesel_state
.open
.show_hidden
);
443 ui_encodings_combo_box_set_active_encoding(GTK_COMBO_BOX(encoding_combo
), filesel_state
.open
.encoding_idx
);
444 filetype_combo_box_set_active_filetype(GTK_COMBO_BOX(filetype_combo
), filesel_state
.open
.filetype_idx
);
448 /* This shows the file selection dialog to open a file. */
449 void dialogs_show_open_file(void)
454 /* set dialog directory to the current file's directory, if present */
455 initdir
= utils_get_current_file_dir_utf8();
457 /* use project or default startup directory (if set) if no files are open */
458 /** TODO should it only be used when initially open the dialog and not on every show? */
460 initdir
= g_strdup(utils_get_default_dir_utf8());
462 SETPTR(initdir
, utils_get_locale_from_utf8(initdir
));
464 dialog
= create_open_file_dialog();
465 open_file_dialog_apply_settings(dialog
);
467 if (initdir
!= NULL
&& g_path_is_absolute(initdir
))
468 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), initdir
);
470 if (app
->project
&& !EMPTY(app
->project
->base_path
))
471 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
472 app
->project
->base_path
, NULL
);
474 while (!open_file_dialog_handle_response(dialog
,
475 gtk_dialog_run(GTK_DIALOG(dialog
))));
476 gtk_widget_destroy(dialog
);
482 static gboolean
handle_save_as(GeanyDocument
*doc
,
483 const gchar
*utf8_filename
, gboolean rename_file
)
485 gboolean success
= FALSE
;
486 g_return_val_if_fail(DOC_VALID(doc
), FALSE
);
487 g_return_val_if_fail(!EMPTY(utf8_filename
), FALSE
);
489 if (doc
->file_name
!= NULL
)
493 document_rename_file(doc
, utf8_filename
);
497 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
498 tm_workspace_remove_source_file(doc
->tm_file
);
499 tm_source_file_free(doc
->tm_file
);
503 success
= document_save_file_as(doc
, utf8_filename
);
505 build_menu_update(doc
);
510 static gboolean
save_as_dialog_handle_response(GeanyDocument
*doc
,
511 GtkWidget
*dialog
, gint response
)
513 gboolean rename_file
= FALSE
;
514 gboolean success
= FALSE
;
515 gchar
*new_filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
519 case GEANY_RESPONSE_RENAME
:
520 /* rename doesn't check for empty filename or overwriting */
521 if (G_UNLIKELY(EMPTY(new_filename
)))
526 if (g_file_test(new_filename
, G_FILE_TEST_EXISTS
) &&
527 !dialogs_show_question_full(NULL
, NULL
, NULL
,
529 _("Filename already exists!")))
533 case GTK_RESPONSE_ACCEPT
:
535 gchar
*utf8_filename
;
537 utf8_filename
= utils_get_utf8_from_locale(new_filename
);
538 success
= handle_save_as(doc
, utf8_filename
, rename_file
);
539 g_free(utf8_filename
);
542 case GTK_RESPONSE_DELETE_EVENT
:
543 case GTK_RESPONSE_CANCEL
:
547 g_free(new_filename
);
553 static GtkWidget
*create_save_file_dialog(GeanyDocument
*doc
)
555 GtkWidget
*dialog
, *rename_btn
;
556 const gchar
*initdir
;
558 dialog
= gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets
.window
),
559 GTK_FILE_CHOOSER_ACTION_SAVE
, NULL
, NULL
);
560 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
561 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog
), TRUE
);
562 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog
), FALSE
);
563 gtk_window_set_type_hint(GTK_WINDOW(dialog
), GDK_WINDOW_TYPE_HINT_DIALOG
);
564 gtk_window_set_transient_for(GTK_WINDOW(dialog
), GTK_WINDOW(main_widgets
.window
));
565 gtk_widget_set_name(dialog
, "GeanyDialog");
567 rename_btn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("R_ename"), GEANY_RESPONSE_RENAME
);
568 gtk_widget_set_tooltip_text(rename_btn
, _("Save the file and rename it"));
569 /* disable rename unless file exists on disk */
570 gtk_widget_set_sensitive(rename_btn
, doc
->real_path
!= NULL
);
572 gtk_dialog_add_buttons(GTK_DIALOG(dialog
),
573 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
574 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
, NULL
);
575 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
577 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog
), TRUE
);
578 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog
), FALSE
);
580 /* set the folder by default to the project base dir or the global pref for opening files */
581 initdir
= utils_get_default_dir_utf8();
584 gchar
*linitdir
= utils_get_locale_from_utf8(initdir
);
585 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), linitdir
);
592 static gboolean
show_save_as_gtk(GeanyDocument
*doc
)
597 g_return_val_if_fail(DOC_VALID(doc
), FALSE
);
599 dialog
= create_save_file_dialog(doc
);
601 if (doc
->file_name
!= NULL
)
603 if (g_path_is_absolute(doc
->file_name
))
605 gchar
*locale_filename
= utils_get_locale_from_utf8(doc
->file_name
);
606 gchar
*locale_basename
= g_path_get_basename(locale_filename
);
607 gchar
*locale_dirname
= g_path_get_dirname(locale_filename
);
609 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), locale_dirname
);
610 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), locale_basename
);
612 g_free(locale_filename
);
613 g_free(locale_basename
);
614 g_free(locale_dirname
);
617 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), doc
->file_name
);
623 if (doc
->file_type
!= NULL
&& doc
->file_type
->extension
!= NULL
)
624 fname
= g_strconcat(GEANY_STRING_UNTITLED
, ".",
625 doc
->file_type
->extension
, NULL
);
627 fname
= g_strdup(GEANY_STRING_UNTITLED
);
629 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), fname
);
634 if (app
->project
&& !EMPTY(app
->project
->base_path
))
635 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
636 app
->project
->base_path
, NULL
);
638 /* Run the dialog synchronously, pausing this function call */
641 resp
= gtk_dialog_run(GTK_DIALOG(dialog
));
643 while (! save_as_dialog_handle_response(doc
, dialog
, resp
));
645 if (app
->project
&& !EMPTY(app
->project
->base_path
))
646 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
647 app
->project
->base_path
, NULL
);
649 gtk_widget_destroy(dialog
);
651 return (resp
== GTK_RESPONSE_ACCEPT
);
656 * Shows the Save As dialog for the current notebook page.
658 * @return @c TRUE if the file was saved, otherwise @c FALSE.
661 gboolean
dialogs_show_save_as(void)
663 GeanyDocument
*doc
= document_get_current();
664 gboolean result
= FALSE
;
666 g_return_val_if_fail(doc
, FALSE
);
668 result
= show_save_as_gtk(doc
);
674 static void show_msgbox_dialog(GtkWidget
*dialog
, GtkMessageType type
, GtkWindow
*parent
)
679 case GTK_MESSAGE_ERROR
:
682 case GTK_MESSAGE_QUESTION
:
683 title
= _("Question");
685 case GTK_MESSAGE_WARNING
:
686 title
= _("Warning");
689 title
= _("Information");
692 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
693 gtk_window_set_icon_name(GTK_WINDOW(dialog
), "geany");
694 gtk_widget_set_name(dialog
, "GeanyDialog");
696 gtk_dialog_run(GTK_DIALOG(dialog
));
697 gtk_widget_destroy(dialog
);
703 * Shows a message box of the type @a type with @a text.
704 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
705 * message dialog box is shown.
707 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
708 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
709 * @param text Printf()-style format string.
710 * @param ... Arguments for the @a text format string.
713 void dialogs_show_msgbox(GtkMessageType type
, const gchar
*text
, ...)
720 GtkWindow
*parent
= (main_status
.main_window_realized
) ? GTK_WINDOW(main_widgets
.window
) : NULL
;
722 va_start(args
, text
);
723 string
= g_strdup_vprintf(text
, args
);
727 win32_message_dialog(GTK_WIDGET(parent
), type
, string
);
729 dialog
= gtk_message_dialog_new(parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
730 type
, GTK_BUTTONS_OK
, "%s", string
);
731 show_msgbox_dialog(dialog
, type
, parent
);
737 void dialogs_show_msgbox_with_secondary(GtkMessageType type
, const gchar
*text
, const gchar
*secondary
)
739 GtkWindow
*parent
= (main_status
.main_window_realized
) ? GTK_WINDOW(main_widgets
.window
) : NULL
;
741 /* put the two strings together because Windows message boxes don't support secondary texts */
742 gchar
*string
= g_strconcat(text
, "\n", secondary
, NULL
);
743 win32_message_dialog(GTK_WIDGET(parent
), type
, string
);
747 dialog
= gtk_message_dialog_new(parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
748 type
, GTK_BUTTONS_OK
, "%s", text
);
749 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
), "%s", secondary
);
750 show_msgbox_dialog(dialog
, type
, parent
);
755 static gint
run_unsaved_dialog(const gchar
*msg
, const gchar
*msg2
)
757 GtkWidget
*dialog
, *button
;
760 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_widgets
.window
), GTK_DIALOG_DESTROY_WITH_PARENT
,
761 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
, "%s", msg
);
762 gtk_window_set_title(GTK_WINDOW(dialog
), _("Question"));
763 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
), "%s", msg2
);
764 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
766 button
= ui_button_new_with_image(GTK_STOCK_CLEAR
, _("_Don't save"));
767 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
, GTK_RESPONSE_NO
);
768 gtk_widget_show(button
);
770 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_SAVE
, GTK_RESPONSE_YES
);
772 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_YES
);
773 ret
= gtk_dialog_run(GTK_DIALOG(dialog
));
775 gtk_widget_destroy(dialog
);
781 gboolean
dialogs_show_unsaved_file(GeanyDocument
*doc
)
783 gchar
*msg
, *short_fn
= NULL
;
786 gboolean old_quitting_state
= main_status
.quitting
;
788 /* display the file tab to remind the user of the document */
789 main_status
.quitting
= FALSE
;
790 document_show_tab(doc
);
791 main_status
.quitting
= old_quitting_state
;
793 short_fn
= document_get_basename_for_display(doc
, -1);
795 msg
= g_strdup_printf(_("The file '%s' is not saved."), short_fn
);
796 msg2
= _("Do you want to save it before closing?");
799 response
= run_unsaved_dialog(msg
, msg2
);
804 case GTK_RESPONSE_YES
:
805 /* document_save_file() returns the status if the file could be saved */
806 return document_save_file(doc
, FALSE
);
808 case GTK_RESPONSE_NO
:
811 case GTK_RESPONSE_CANCEL
: /* fall through to default and leave the function */
819 on_font_dialog_response(GtkDialog
*dialog
, gint response
, gpointer user_data
)
821 gboolean close
= TRUE
;
825 case GTK_RESPONSE_APPLY
:
826 case GTK_RESPONSE_OK
:
830 fontname
= gtk_font_chooser_get_font(
831 GTK_FONT_CHOOSER(ui_widgets
.open_fontsel
));
832 ui_set_editor_font(fontname
);
835 close
= (response
== GTK_RESPONSE_OK
);
841 gtk_widget_hide(ui_widgets
.open_fontsel
);
845 /* This shows the font selection dialog to choose a font. */
846 void dialogs_show_open_font(void)
848 if (ui_widgets
.open_fontsel
== NULL
)
850 GtkWidget
*apply_button
;
852 ui_widgets
.open_fontsel
= gtk_font_chooser_dialog_new(_("Choose font"), NULL
);
853 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets
.open_fontsel
), 4);
854 gtk_window_set_modal(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
855 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
856 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
857 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets
.open_fontsel
), GDK_WINDOW_TYPE_HINT_DIALOG
);
858 gtk_widget_set_name(ui_widgets
.open_fontsel
, "GeanyDialog");
860 apply_button
= gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets
.open_fontsel
), GTK_RESPONSE_APPLY
);
863 gtk_widget_show(apply_button
);
865 g_signal_connect(ui_widgets
.open_fontsel
,
866 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
867 g_signal_connect(ui_widgets
.open_fontsel
,
868 "response", G_CALLBACK(on_font_dialog_response
), NULL
);
870 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets
.open_fontsel
), GTK_WINDOW(main_widgets
.window
));
872 gtk_font_chooser_set_font(
873 GTK_FONT_CHOOSER(ui_widgets
.open_fontsel
), interface_prefs
.editor_font
);
874 /* We make sure the dialog is visible. */
875 gtk_window_present(GTK_WINDOW(ui_widgets
.open_fontsel
));
880 on_input_dialog_show(GtkDialog
*dialog
, GtkWidget
*entry
)
882 gtk_widget_grab_focus(entry
);
887 on_input_entry_activate(GtkEntry
*entry
, GtkDialog
*dialog
)
889 gtk_dialog_response(dialog
, GTK_RESPONSE_ACCEPT
);
894 on_input_numeric_activate(GtkEntry
*entry
, GtkDialog
*dialog
)
896 gtk_dialog_response(dialog
, GTK_RESPONSE_ACCEPT
);
905 GeanyInputCallback callback
;
912 on_input_dialog_response(GtkDialog
*dialog
, gint response
, InputDialogData
*data
)
914 if (response
== GTK_RESPONSE_ACCEPT
)
916 const gchar
*str
= gtk_entry_get_text(GTK_ENTRY(data
->entry
));
918 if (data
->combo
!= NULL
)
919 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(data
->combo
), str
, 0);
921 data
->callback(str
, data
->data
);
923 gtk_widget_hide(GTK_WIDGET(dialog
));
927 /* Create and display an input dialog.
928 * persistent: whether to remember previous entry text in a combo box;
929 * in this case the dialog returned is not destroyed on a response,
930 * and can be reshown.
931 * Returns: the dialog widget. */
933 dialogs_show_input_full(const gchar
*title
, GtkWindow
*parent
,
934 const gchar
*label_text
, const gchar
*default_text
,
935 gboolean persistent
, GeanyInputCallback input_cb
, gpointer input_cb_data
,
936 GCallback insert_text_cb
, gpointer insert_text_cb_data
)
938 GtkWidget
*dialog
, *vbox
;
939 InputDialogData
*data
= g_malloc(sizeof *data
);
941 dialog
= gtk_dialog_new_with_buttons(title
, parent
,
942 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
943 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
944 vbox
= ui_dialog_vbox_new(GTK_DIALOG(dialog
));
945 gtk_widget_set_name(dialog
, "GeanyDialog");
946 gtk_box_set_spacing(GTK_BOX(vbox
), 6);
950 data
->callback
= input_cb
;
951 data
->data
= input_cb_data
;
955 GtkWidget
*label
= gtk_label_new(label_text
);
956 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
957 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
958 gtk_container_add(GTK_CONTAINER(vbox
), label
);
961 if (persistent
) /* remember previous entry text in a combo box */
963 data
->combo
= gtk_combo_box_text_new_with_entry();
964 data
->entry
= gtk_bin_get_child(GTK_BIN(data
->combo
));
965 ui_entry_add_clear_icon(GTK_ENTRY(data
->entry
));
966 gtk_container_add(GTK_CONTAINER(vbox
), data
->combo
);
970 data
->entry
= gtk_entry_new();
971 ui_entry_add_clear_icon(GTK_ENTRY(data
->entry
));
972 gtk_container_add(GTK_CONTAINER(vbox
), data
->entry
);
975 if (default_text
!= NULL
)
977 gtk_entry_set_text(GTK_ENTRY(data
->entry
), default_text
);
979 gtk_entry_set_max_length(GTK_ENTRY(data
->entry
), 255);
980 gtk_entry_set_width_chars(GTK_ENTRY(data
->entry
), 30);
982 if (insert_text_cb
!= NULL
)
983 g_signal_connect(data
->entry
, "insert-text", insert_text_cb
, insert_text_cb_data
);
984 g_signal_connect(data
->entry
, "activate", G_CALLBACK(on_input_entry_activate
), dialog
);
985 g_signal_connect(dialog
, "show", G_CALLBACK(on_input_dialog_show
), data
->entry
);
986 g_signal_connect_data(dialog
, "response", G_CALLBACK(on_input_dialog_response
), data
, (GClosureNotify
)g_free
, 0);
990 /* override default handler */
991 g_signal_connect(dialog
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
992 gtk_widget_show_all(dialog
);
995 gtk_widget_show_all(dialog
);
996 gtk_dialog_run(GTK_DIALOG(dialog
));
997 gtk_widget_destroy(dialog
);
1002 /* Remember previous entry text in a combo box.
1003 * Returns: the dialog widget. */
1005 dialogs_show_input_persistent(const gchar
*title
, GtkWindow
*parent
,
1006 const gchar
*label_text
, const gchar
*default_text
,
1007 GeanyInputCallback input_cb
, gpointer input_cb_data
)
1009 return dialogs_show_input_full(title
, parent
, label_text
, default_text
, TRUE
, input_cb
, input_cb_data
, NULL
, NULL
);
1013 static void on_dialog_input(const gchar
*str
, gpointer data
)
1015 gchar
**dialog_input
= data
;
1016 *dialog_input
= g_strdup(str
);
1020 /** Asks the user for text input.
1021 * @param title Dialog title.
1022 * @param parent @nullable The currently focused window, usually @c geany->main_widgets->window.
1023 * @c NULL can be used but is discouraged due to window manager effects.
1024 * @param label_text @nullable Label text, or @c NULL.
1025 * @param default_text @nullable Text to display in the input field, or @c NULL.
1026 * @return @nullable New copy of user input or @c NULL if cancelled.
1029 gchar
*dialogs_show_input(const gchar
*title
, GtkWindow
*parent
, const gchar
*label_text
,
1030 const gchar
*default_text
)
1032 gchar
*dialog_input
= NULL
;
1033 dialogs_show_input_full(title
, parent
, label_text
, default_text
, FALSE
, on_dialog_input
, &dialog_input
, NULL
, NULL
);
1034 return dialog_input
;
1038 /* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1039 /* Returns: newly allocated copy of the entry text or NULL on cancel.
1040 * Specialised variant for Goto Line dialog. */
1041 gchar
*dialogs_show_input_goto_line(const gchar
*title
, GtkWindow
*parent
, const gchar
*label_text
,
1042 const gchar
*default_text
)
1044 gchar
*dialog_input
= NULL
;
1045 dialogs_show_input_full(
1046 title
, parent
, label_text
, default_text
, FALSE
, on_dialog_input
, &dialog_input
,
1047 G_CALLBACK(ui_editable_insert_text_callback
), NULL
);
1048 return dialog_input
;
1053 * Shows an input box to enter a numerical value using a GtkSpinButton.
1054 * If the dialog is aborted, @a value remains untouched.
1056 * @param title The dialog title.
1057 * @param label_text The shown dialog label.
1058 * @param value The default value for the spin button and the return location of the entered value.
1060 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1061 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1062 * @param step Increment added or subtracted by spinning the widget
1063 * (see documentation for @c gtk_spin_button_new_with_range()).
1065 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1070 gboolean
dialogs_show_input_numeric(const gchar
*title
, const gchar
*label_text
,
1071 gdouble
*value
, gdouble min
, gdouble max
, gdouble step
)
1073 GtkWidget
*dialog
, *label
, *spin
, *vbox
;
1074 gboolean res
= FALSE
;
1076 g_return_val_if_fail(title
!= NULL
, FALSE
);
1077 g_return_val_if_fail(label_text
!= NULL
, FALSE
);
1078 g_return_val_if_fail(value
!= NULL
, FALSE
);
1080 dialog
= gtk_dialog_new_with_buttons(title
, GTK_WINDOW(main_widgets
.window
),
1081 GTK_DIALOG_DESTROY_WITH_PARENT
,
1082 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1083 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
1084 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CANCEL
);
1085 vbox
= ui_dialog_vbox_new(GTK_DIALOG(dialog
));
1086 gtk_widget_set_name(dialog
, "GeanyDialog");
1088 label
= gtk_label_new(label_text
);
1089 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
1091 spin
= gtk_spin_button_new_with_range(min
, max
, step
);
1092 ui_entry_add_clear_icon(GTK_ENTRY(spin
));
1093 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), *value
);
1094 g_signal_connect(spin
, "activate", G_CALLBACK(on_input_numeric_activate
), dialog
);
1096 gtk_container_add(GTK_CONTAINER(vbox
), label
);
1097 gtk_container_add(GTK_CONTAINER(vbox
), spin
);
1098 gtk_widget_show_all(vbox
);
1100 if (gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_ACCEPT
)
1102 *value
= gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin
));
1105 gtk_widget_destroy(dialog
);
1111 void dialogs_show_file_properties(GeanyDocument
*doc
)
1113 GtkWidget
*dialog
, *label
, *image
, *check
;
1114 gchar
*file_size
, *title
, *base_name
, *time_changed
, *time_modified
, *time_accessed
, *enctext
;
1116 #ifdef HAVE_SYS_TYPES_H
1120 gchar
*locale_filename
;
1126 /* define this ones, to avoid later trouble */
1141 g_return_if_fail(doc
== NULL
|| doc
->is_valid
);
1143 if (doc
== NULL
|| doc
->file_name
== NULL
)
1145 dialogs_show_msgbox(GTK_MESSAGE_ERROR
,
1146 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1151 #ifdef HAVE_SYS_TYPES_H
1152 locale_filename
= utils_get_locale_from_utf8(doc
->file_name
);
1153 if (g_stat(locale_filename
, &st
) == 0)
1155 /* first copy the returned string and the trim it, to not modify the static glibc string
1156 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1157 time_changed
= g_strchomp(g_strdup(ctime(&st
.st_ctime
)));
1158 time_modified
= g_strchomp(g_strdup(ctime(&st
.st_mtime
)));
1159 time_accessed
= g_strchomp(g_strdup(ctime(&st
.st_atime
)));
1160 filesize
= st
.st_size
;
1165 time_changed
= g_strdup(_("unknown"));
1166 time_modified
= g_strdup(_("unknown"));
1167 time_accessed
= g_strdup(_("unknown"));
1168 filesize
= (off_t
) 0;
1171 g_free(locale_filename
);
1173 time_changed
= g_strdup(_("unknown"));
1174 time_modified
= g_strdup(_("unknown"));
1175 time_accessed
= g_strdup(_("unknown"));
1178 base_name
= g_path_get_basename(doc
->file_name
);
1179 short_name
= utils_str_middle_truncate(base_name
, 30);
1180 title
= g_strdup_printf(_("%s Properties"), short_name
);
1181 dialog
= ui_builder_get_object("properties_dialog");
1182 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
1185 gtk_widget_set_name(dialog
, "GeanyDialog");
1187 label
= ui_lookup_widget(dialog
, "file_name_label");
1188 gtk_label_set_text(GTK_LABEL(label
), base_name
);
1190 image
= ui_lookup_widget(dialog
, "file_type_image");
1191 gtk_image_set_from_gicon(GTK_IMAGE(image
), doc
->file_type
->icon
,
1192 GTK_ICON_SIZE_BUTTON
);
1194 label
= ui_lookup_widget(dialog
, "file_type_label");
1195 gtk_label_set_text(GTK_LABEL(label
), doc
->file_type
->title
);
1197 label
= ui_lookup_widget(dialog
, "file_size_label");
1198 file_size
= utils_make_human_readable_str(filesize
, 1, 0);
1199 gtk_label_set_text(GTK_LABEL(label
), file_size
);
1202 label
= ui_lookup_widget(dialog
, "file_location_label");
1203 gtk_label_set_text(GTK_LABEL(label
), doc
->file_name
);
1205 check
= ui_lookup_widget(dialog
, "file_read_only_check");
1206 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), doc
->readonly
);
1208 label
= ui_lookup_widget(dialog
, "file_encoding_label");
1209 enctext
= g_strdup_printf("%s %s",
1211 (encodings_is_unicode_charset(doc
->encoding
)) ?
1212 ((doc
->has_bom
) ? _("(with BOM)") : _("(without BOM)")) : "");
1213 gtk_label_set_text(GTK_LABEL(label
), enctext
);
1216 label
= ui_lookup_widget(dialog
, "file_modified_label");
1217 gtk_label_set_text(GTK_LABEL(label
), time_modified
);
1218 label
= ui_lookup_widget(dialog
, "file_changed_label");
1219 gtk_label_set_text(GTK_LABEL(label
), time_changed
);
1220 label
= ui_lookup_widget(dialog
, "file_accessed_label");
1221 gtk_label_set_text(GTK_LABEL(label
), time_accessed
);
1224 check
= ui_lookup_widget(dialog
, "file_perm_owner_r_check");
1225 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IRUSR
);
1226 check
= ui_lookup_widget(dialog
, "file_perm_owner_w_check");
1227 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWUSR
);
1228 check
= ui_lookup_widget(dialog
, "file_perm_owner_x_check");
1229 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXUSR
);
1230 check
= ui_lookup_widget(dialog
, "file_perm_group_r_check");
1231 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IRGRP
);
1232 check
= ui_lookup_widget(dialog
, "file_perm_group_w_check");
1233 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWGRP
);
1234 check
= ui_lookup_widget(dialog
, "file_perm_group_x_check");
1235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXGRP
);
1236 check
= ui_lookup_widget(dialog
, "file_perm_other_r_check");
1237 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IROTH
);
1238 check
= ui_lookup_widget(dialog
, "file_perm_other_w_check");
1239 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWOTH
);
1240 check
= ui_lookup_widget(dialog
, "file_perm_other_x_check");
1241 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXOTH
);
1244 g_free(time_changed
);
1245 g_free(time_modified
);
1246 g_free(time_accessed
);
1248 gtk_widget_show(dialog
);
1252 /* extra_text can be NULL; otherwise it is displayed below main_text.
1253 * if parent is NULL, main_widgets.window will be used
1254 * btn_1, btn_2, btn_3 can be NULL.
1255 * returns response_1, response_2, response_3, or GTK_RESPONSE_DELETE_EVENT if the dialog was discarded */
1256 static gint
show_prompt(GtkWidget
*parent
,
1257 const gchar
*btn_1
, GtkResponseType response_1
,
1258 const gchar
*btn_2
, GtkResponseType response_2
,
1259 const gchar
*btn_3
, GtkResponseType response_3
,
1260 const gchar
*question_text
, const gchar
*extra_text
)
1262 gboolean ret
= FALSE
;
1268 btn_2
= GTK_STOCK_NO
;
1269 response_2
= GTK_RESPONSE_NO
;
1273 btn_3
= GTK_STOCK_YES
;
1274 response_3
= GTK_RESPONSE_YES
;
1278 /* our native dialog code doesn't support custom buttons */
1279 if (utils_str_equal(btn_3
, GTK_STOCK_YES
) &&
1280 utils_str_equal(btn_2
, GTK_STOCK_NO
) && btn_1
== NULL
)
1282 gchar
*string
= (extra_text
== NULL
) ? g_strdup(question_text
) :
1283 g_strconcat(question_text
, "\n\n", extra_text
, NULL
);
1285 ret
= win32_message_dialog(parent
, GTK_MESSAGE_QUESTION
, string
);
1286 ret
= ret
? response_3
: response_2
;
1291 if (parent
== NULL
&& main_status
.main_window_realized
)
1292 parent
= main_widgets
.window
;
1294 dialog
= gtk_message_dialog_new(GTK_WINDOW(parent
),
1295 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_QUESTION
,
1296 GTK_BUTTONS_NONE
, "%s", question_text
);
1297 gtk_widget_set_name(dialog
, "GeanyDialog");
1298 gtk_window_set_title(GTK_WINDOW(dialog
), _("Question"));
1299 gtk_window_set_icon_name(GTK_WINDOW(dialog
), "geany");
1301 /* question_text will be in bold if optional extra_text used */
1302 if (extra_text
!= NULL
)
1303 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
),
1307 gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_1
, response_1
);
1309 btn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_2
, response_2
);
1310 /* we don't want a default, but we need to override the apply button as default */
1311 gtk_widget_grab_default(btn
);
1312 gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_3
, response_3
);
1314 ret
= gtk_dialog_run(GTK_DIALOG(dialog
));
1315 gtk_widget_destroy(dialog
);
1322 * Shows a question message box with @a text and Yes/No buttons.
1323 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1324 * message dialog box is shown.
1326 * @param text Printf()-style format string.
1327 * @param ... Arguments for the @a text format string.
1329 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1332 gboolean
dialogs_show_question(const gchar
*text
, ...)
1336 GtkWidget
*parent
= (main_status
.main_window_realized
) ? main_widgets
.window
: NULL
;
1339 va_start(args
, text
);
1340 string
= g_strdup_vprintf(text
, args
);
1342 result
= show_prompt(parent
,
1343 NULL
, GTK_RESPONSE_NONE
,
1344 GTK_STOCK_NO
, GTK_RESPONSE_NO
,
1345 GTK_STOCK_YES
, GTK_RESPONSE_YES
,
1348 return (result
== GTK_RESPONSE_YES
);
1352 /* extra_text can be NULL; otherwise it is displayed below main_text.
1353 * if parent is NULL, main_widgets.window will be used
1354 * yes_btn, no_btn can be NULL. */
1355 gboolean
dialogs_show_question_full(GtkWidget
*parent
, const gchar
*yes_btn
, const gchar
*no_btn
,
1356 const gchar
*extra_text
, const gchar
*main_text
, ...)
1362 va_start(args
, main_text
);
1363 string
= g_strdup_vprintf(main_text
, args
);
1365 result
= show_prompt(parent
,
1366 NULL
, GTK_RESPONSE_NONE
,
1367 no_btn
, GTK_RESPONSE_NO
,
1368 yes_btn
, GTK_RESPONSE_YES
,
1369 string
, extra_text
);
1371 return (result
== GTK_RESPONSE_YES
);
1375 /* extra_text can be NULL; otherwise it is displayed below main_text.
1376 * if parent is NULL, main_widgets.window will be used
1377 * btn_1, btn_2, btn_3 can be NULL.
1378 * returns response_1, response_2 or response_3 */
1379 gint
dialogs_show_prompt(GtkWidget
*parent
,
1380 const gchar
*btn_1
, GtkResponseType response_1
,
1381 const gchar
*btn_2
, GtkResponseType response_2
,
1382 const gchar
*btn_3
, GtkResponseType response_3
,
1383 const gchar
*extra_text
, const gchar
*main_text
, ...)
1389 va_start(args
, main_text
);
1390 string
= g_strdup_vprintf(main_text
, args
);
1392 result
= show_prompt(parent
, btn_1
, response_1
, btn_2
, response_2
, btn_3
, response_3
,
1393 string
, extra_text
);