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.
28 #include <gdk/gdkkeysyms.h>
31 #ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
36 #ifdef HAVE_SYS_TYPES_H
37 # include <sys/types.h>
40 /* gstdio.h also includes sys/stat.h */
41 #include <glib/gstdio.h>
45 #include "callbacks.h"
47 #include "filetypes.h"
49 #include "sciwrappers.h"
53 #include "keybindings.h"
54 #include "encodings.h"
58 #include "gtkcompat.h"
63 GEANY_RESPONSE_RENAME
,
68 static struct FileSelState
76 gboolean more_options_visible
;
82 GEANY_ENCODINGS_MAX
, /* default encoding is detect from file */
90 /* gets the ID of the current file filter */
91 static guint
file_chooser_get_filter_idx(GtkFileChooser
*chooser
)
94 GtkFileFilter
*current
;
95 GSList
*filters
, *item
;
97 current
= gtk_file_chooser_get_filter(chooser
);
98 filters
= gtk_file_chooser_list_filters(chooser
);
99 foreach_slist(item
, filters
)
101 if (item
->data
== current
)
105 g_slist_free(filters
);
110 /* sets the current file filter from its ID */
111 static void file_chooser_set_filter_idx(GtkFileChooser
*chooser
, guint idx
)
113 GtkFileFilter
*current
;
116 filters
= gtk_file_chooser_list_filters(chooser
);
117 current
= g_slist_nth_data(filters
, idx
);
118 g_slist_free(filters
);
119 gtk_file_chooser_set_filter(chooser
, current
);
123 static void open_file_dialog_handle_response(GtkWidget
*dialog
, gint response
)
125 if (response
== GTK_RESPONSE_ACCEPT
|| response
== GEANY_RESPONSE_VIEW
)
128 GtkTreeModel
*encoding_model
;
129 GtkTreeIter encoding_iter
;
130 GeanyFiletype
*ft
= NULL
;
131 const gchar
*charset
= NULL
;
132 GtkWidget
*expander
= ui_lookup_widget(dialog
, "more_options_expander");
133 GtkWidget
*filetype_combo
= ui_lookup_widget(dialog
, "filetype_combo");
134 GtkWidget
*encoding_combo
= ui_lookup_widget(dialog
, "encoding_combo");
135 gboolean ro
= (response
== GEANY_RESPONSE_VIEW
); /* View clicked */
137 filesel_state
.open
.more_options_visible
= gtk_expander_get_expanded(GTK_EXPANDER(expander
));
138 filesel_state
.open
.filter_idx
= file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog
));
139 filesel_state
.open
.filetype_idx
= gtk_combo_box_get_active(GTK_COMBO_BOX(filetype_combo
));
141 /* ignore detect from file item */
142 if (filesel_state
.open
.filetype_idx
> 0)
143 ft
= g_slist_nth_data(filetypes_by_title
, (guint
) filesel_state
.open
.filetype_idx
);
145 encoding_model
= gtk_combo_box_get_model(GTK_COMBO_BOX(encoding_combo
));
146 gtk_combo_box_get_active_iter(GTK_COMBO_BOX(encoding_combo
), &encoding_iter
);
147 gtk_tree_model_get(encoding_model
, &encoding_iter
, 0, &filesel_state
.open
.encoding_idx
, -1);
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 document_open_files(filelist
, ro
, ft
, charset
);
155 g_slist_foreach(filelist
, (GFunc
) g_free
, NULL
); /* free filenames */
157 g_slist_free(filelist
);
159 if (app
->project
&& !EMPTY(app
->project
->base_path
))
160 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
161 app
->project
->base_path
, NULL
);
165 static void on_file_open_show_hidden_notify(GObject
*filechooser
,
166 GParamSpec
*pspec
, gpointer data
)
168 GtkWidget
*toggle_button
;
170 toggle_button
= ui_lookup_widget(GTK_WIDGET(filechooser
), "check_hidden");
172 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button
),
173 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser
)));
178 on_file_open_check_hidden_toggled(GtkToggleButton
*togglebutton
, GtkWidget
*dialog
)
180 filesel_state
.open
.show_hidden
= gtk_toggle_button_get_active(togglebutton
);
181 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog
), filesel_state
.open
.show_hidden
);
185 static gint
encoding_combo_store_sort_func(GtkTreeModel
*model
,
190 gboolean a_has_child
= gtk_tree_model_iter_has_child(model
, a
);
191 gboolean b_has_child
= gtk_tree_model_iter_has_child(model
, b
);
196 if (a_has_child
!= b_has_child
)
197 return a_has_child
? -1 : 1;
199 gtk_tree_model_get(model
, a
, 1, &a_string
, -1);
200 gtk_tree_model_get(model
, b
, 1, &b_string
, -1);
201 cmp_res
= strcmp(a_string
, b_string
);
208 static GtkTreeStore
*create_encoding_combo_store(GtkTreeIter
*iter_detect
)
211 GtkTreeIter iter_current
, iter_westeuro
, iter_easteuro
, iter_eastasian
, iter_asian
,
212 iter_utf8
, iter_middleeast
;
213 GtkTreeIter
*iter_parent
;
214 gchar
*encoding_string
;
217 store
= gtk_tree_store_new(2, G_TYPE_INT
, G_TYPE_STRING
);
219 gtk_tree_store_append(store
, iter_detect
, NULL
);
220 gtk_tree_store_set(store
, iter_detect
, 0, GEANY_ENCODINGS_MAX
, 1, _("Detect from file"), -1);
222 gtk_tree_store_append(store
, &iter_westeuro
, NULL
);
223 gtk_tree_store_set(store
, &iter_westeuro
, 0, -1, 1, _("West European"), -1);
224 gtk_tree_store_append(store
, &iter_easteuro
, NULL
);
225 gtk_tree_store_set(store
, &iter_easteuro
, 0, -1, 1, _("East European"), -1);
226 gtk_tree_store_append(store
, &iter_eastasian
, NULL
);
227 gtk_tree_store_set(store
, &iter_eastasian
, 0, -1, 1, _("East Asian"), -1);
228 gtk_tree_store_append(store
, &iter_asian
, NULL
);
229 gtk_tree_store_set(store
, &iter_asian
, 0, -1, 1, _("SE & SW Asian"), -1);
230 gtk_tree_store_append(store
, &iter_middleeast
, NULL
);
231 gtk_tree_store_set(store
, &iter_middleeast
, 0, -1, 1, _("Middle Eastern"), -1);
232 gtk_tree_store_append(store
, &iter_utf8
, NULL
);
233 gtk_tree_store_set(store
, &iter_utf8
, 0, -1, 1, _("Unicode"), -1);
235 for (i
= 0; i
< GEANY_ENCODINGS_MAX
; i
++)
237 switch (encodings
[i
].group
)
239 case WESTEUROPEAN
: iter_parent
= &iter_westeuro
; break;
240 case EASTEUROPEAN
: iter_parent
= &iter_easteuro
; break;
241 case EASTASIAN
: iter_parent
= &iter_eastasian
; break;
242 case ASIAN
: iter_parent
= &iter_asian
; break;
243 case MIDDLEEASTERN
: iter_parent
= &iter_middleeast
; break;
244 case UNICODE
: iter_parent
= &iter_utf8
; break;
246 default: iter_parent
= NULL
;
248 gtk_tree_store_append(store
, &iter_current
, iter_parent
);
249 encoding_string
= encodings_to_string(&encodings
[i
]);
250 gtk_tree_store_set(store
, &iter_current
, 0, i
, 1, encoding_string
, -1);
251 g_free(encoding_string
);
252 /* restore the saved state */
253 if (i
== filesel_state
.open
.encoding_idx
)
254 *iter_detect
= iter_current
;
257 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store
), 1, GTK_SORT_ASCENDING
);
258 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store
), 1, encoding_combo_store_sort_func
, NULL
, NULL
);
264 static void encoding_combo_cell_data_func(GtkCellLayout
*cell_layout
,
265 GtkCellRenderer
*cell
,
266 GtkTreeModel
*tree_model
,
270 gboolean sensitive
= !gtk_tree_model_iter_has_child(tree_model
, iter
);
272 g_object_set(cell
, "sensitive", sensitive
, NULL
);
276 static GtkWidget
*add_file_open_extra_widget(GtkWidget
*dialog
)
278 GtkWidget
*expander
, *vbox
, *table
, *check_hidden
;
279 GtkWidget
*filetype_ebox
, *filetype_label
, *filetype_combo
;
280 GtkWidget
*encoding_ebox
, *encoding_label
, *encoding_combo
;
282 expander
= gtk_expander_new_with_mnemonic(_("_More Options"));
283 vbox
= gtk_vbox_new(FALSE
, 6);
284 gtk_container_add(GTK_CONTAINER(expander
), vbox
);
286 table
= gtk_table_new(2, 4, FALSE
);
288 /* line 1 with checkbox and encoding combo */
289 check_hidden
= gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
290 gtk_widget_show(check_hidden
);
291 gtk_table_attach(GTK_TABLE(table
), check_hidden
, 0, 1, 0, 1,
292 (GtkAttachOptions
) (GTK_FILL
| GTK_EXPAND
),
293 (GtkAttachOptions
) (0), 0, 5);
296 gtk_table_attach(GTK_TABLE(table
), gtk_label_new(""), 1, 2, 0, 1,
297 (GtkAttachOptions
) (GTK_FILL
),
298 (GtkAttachOptions
) (0), 5, 5);
300 encoding_label
= gtk_label_new(_("Set encoding:"));
301 gtk_misc_set_alignment(GTK_MISC(encoding_label
), 1, 0);
302 gtk_table_attach(GTK_TABLE(table
), encoding_label
, 2, 3, 0, 1,
303 (GtkAttachOptions
) (GTK_FILL
),
304 (GtkAttachOptions
) (0), 4, 5);
305 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
306 encoding_ebox
= gtk_event_box_new();
307 encoding_combo
= gtk_combo_box_new();
308 gtk_widget_set_tooltip_text(encoding_ebox
,
309 _("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."));
310 gtk_container_add(GTK_CONTAINER(encoding_ebox
), encoding_combo
);
311 gtk_table_attach(GTK_TABLE(table
), encoding_ebox
, 3, 4, 0, 1,
312 (GtkAttachOptions
) (GTK_FILL
),
313 (GtkAttachOptions
) (0), 0, 5);
315 /* line 2 with filetype combo */
316 filetype_label
= gtk_label_new(_("Set filetype:"));
317 gtk_misc_set_alignment(GTK_MISC(filetype_label
), 1, 0);
318 gtk_table_attach(GTK_TABLE(table
), filetype_label
, 2, 3, 1, 2,
319 (GtkAttachOptions
) (GTK_FILL
),
320 (GtkAttachOptions
) (0), 4, 5);
321 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
322 filetype_ebox
= gtk_event_box_new();
323 filetype_combo
= gtk_combo_box_text_new();
324 gtk_widget_set_tooltip_text(filetype_ebox
,
325 _("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."));
326 gtk_container_add(GTK_CONTAINER(filetype_ebox
), filetype_combo
);
327 gtk_table_attach(GTK_TABLE(table
), filetype_ebox
, 3, 4, 1, 2,
328 (GtkAttachOptions
) (GTK_FILL
),
329 (GtkAttachOptions
) (0), 0, 5);
331 gtk_box_pack_start(GTK_BOX(vbox
), table
, FALSE
, FALSE
, 0);
332 gtk_widget_show_all(vbox
);
334 g_signal_connect(check_hidden
, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled
), dialog
);
336 ui_hookup_widget(dialog
, expander
, "more_options_expander");
337 ui_hookup_widget(dialog
, check_hidden
, "check_hidden");
338 ui_hookup_widget(dialog
, filetype_combo
, "filetype_combo");
339 ui_hookup_widget(dialog
, encoding_combo
, "encoding_combo");
345 static GtkWidget
*create_open_file_dialog(void)
348 GtkWidget
*filetype_combo
, *encoding_combo
;
350 GtkCellRenderer
*encoding_renderer
;
351 GtkTreeIter encoding_iter
;
354 dialog
= gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets
.window
),
355 GTK_FILE_CHOOSER_ACTION_OPEN
, NULL
, NULL
);
356 gtk_widget_set_name(dialog
, "GeanyDialog");
358 viewbtn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("_View"), GEANY_RESPONSE_VIEW
);
359 gtk_widget_set_tooltip_text(viewbtn
,
360 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
362 gtk_dialog_add_buttons(GTK_DIALOG(dialog
),
363 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
364 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
, NULL
);
365 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
367 gtk_widget_set_size_request(dialog
, -1, 460);
368 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
369 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog
), TRUE
);
370 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog
), FALSE
);
371 gtk_window_set_type_hint(GTK_WINDOW(dialog
), GDK_WINDOW_TYPE_HINT_DIALOG
);
372 gtk_window_set_transient_for(GTK_WINDOW(dialog
), GTK_WINDOW(main_widgets
.window
));
373 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog
), TRUE
);
374 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog
), FALSE
);
376 /* add checkboxes and filename entry */
377 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog
), add_file_open_extra_widget(dialog
));
378 filetype_combo
= ui_lookup_widget(dialog
, "filetype_combo");
380 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(filetype_combo
), _("Detect by file extension"));
381 /* add FileFilters(start with "All Files") */
382 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),
383 filetypes_create_file_filter(filetypes
[GEANY_FILETYPES_NONE
]));
384 /* now create meta filter "All Source" */
385 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
),
386 filetypes_create_file_filter_all_source());
387 foreach_slist(node
, filetypes_by_title
)
389 GeanyFiletype
*ft
= node
->data
;
391 if (G_UNLIKELY(ft
->id
== GEANY_FILETYPES_NONE
))
393 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(filetype_combo
), ft
->title
);
394 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog
), filetypes_create_file_filter(ft
));
396 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(filetype_combo
), 3);
397 gtk_combo_box_set_active(GTK_COMBO_BOX(filetype_combo
), 0);
399 /* fill encoding combo box */
400 encoding_combo
= ui_lookup_widget(dialog
, "encoding_combo");
401 gtk_combo_box_set_model(GTK_COMBO_BOX(encoding_combo
), GTK_TREE_MODEL(
402 create_encoding_combo_store(&encoding_iter
)));
403 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(encoding_combo
), &encoding_iter
);
404 encoding_renderer
= gtk_cell_renderer_text_new();
405 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(encoding_combo
), encoding_renderer
, TRUE
);
406 gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(encoding_combo
), encoding_renderer
, "text", 1);
407 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(encoding_combo
), encoding_renderer
,
408 encoding_combo_cell_data_func
, NULL
, NULL
);
410 g_signal_connect(dialog
, "notify::show-hidden",
411 G_CALLBACK(on_file_open_show_hidden_notify
), NULL
);
417 static void open_file_dialog_apply_settings(GtkWidget
*dialog
)
419 static gboolean initialized
= FALSE
;
420 GtkWidget
*check_hidden
= ui_lookup_widget(dialog
, "check_hidden");
421 GtkWidget
*filetype_combo
= ui_lookup_widget(dialog
, "filetype_combo");
422 GtkWidget
*expander
= ui_lookup_widget(dialog
, "more_options_expander");
424 /* we can't know the initial position of combo boxes, so retreive it the first time */
427 filesel_state
.open
.filter_idx
= file_chooser_get_filter_idx(GTK_FILE_CHOOSER(dialog
));
428 filesel_state
.open
.filetype_idx
= gtk_combo_box_get_active(GTK_COMBO_BOX(filetype_combo
));
434 file_chooser_set_filter_idx(GTK_FILE_CHOOSER(dialog
), filesel_state
.open
.filter_idx
);
435 gtk_combo_box_set_active(GTK_COMBO_BOX(filetype_combo
), filesel_state
.open
.filetype_idx
);
437 gtk_expander_set_expanded(GTK_EXPANDER(expander
), filesel_state
.open
.more_options_visible
);
438 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden
), filesel_state
.open
.show_hidden
);
439 /* encoding combo is restored at creating time, see create_encoding_combo_store() */
443 /* This shows the file selection dialog to open a file. */
444 void dialogs_show_open_file(void)
448 /* set dialog directory to the current file's directory, if present */
449 initdir
= utils_get_current_file_dir_utf8();
451 /* use project or default startup directory (if set) if no files are open */
452 /** TODO should it only be used when initally open the dialog and not on every show? */
454 initdir
= g_strdup(utils_get_default_dir_utf8());
456 SETPTR(initdir
, utils_get_locale_from_utf8(initdir
));
459 if (interface_prefs
.use_native_windows_dialogs
)
460 win32_show_document_open_dialog(GTK_WINDOW(main_widgets
.window
), _("Open File"), initdir
);
464 GtkWidget
*dialog
= create_open_file_dialog();
467 open_file_dialog_apply_settings(dialog
);
469 if (initdir
!= NULL
&& g_path_is_absolute(initdir
))
470 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), initdir
);
472 if (app
->project
&& !EMPTY(app
->project
->base_path
))
473 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
474 app
->project
->base_path
, NULL
);
476 response
= gtk_dialog_run(GTK_DIALOG(dialog
));
477 open_file_dialog_handle_response(dialog
, response
);
478 gtk_widget_destroy(dialog
);
484 static gboolean
handle_save_as(const gchar
*utf8_filename
, gboolean rename_file
)
486 GeanyDocument
*doc
= document_get_current();
487 gboolean success
= FALSE
;
489 g_return_val_if_fail(!EMPTY(utf8_filename
), FALSE
);
491 if (doc
->file_name
!= NULL
)
495 document_rename_file(doc
, utf8_filename
);
497 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
498 tm_workspace_remove_object(doc
->tm_file
, TRUE
, TRUE
);
501 success
= document_save_file_as(doc
, utf8_filename
);
503 build_menu_update(doc
);
508 static gboolean
save_as_dialog_handle_response(GtkWidget
*dialog
, gint response
)
510 gboolean rename_file
= FALSE
;
511 gboolean success
= FALSE
;
512 gchar
*new_filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
516 case GEANY_RESPONSE_RENAME
:
517 /* rename doesn't check for empty filename or overwriting */
518 if (G_UNLIKELY(EMPTY(new_filename
)))
523 if (g_file_test(new_filename
, G_FILE_TEST_EXISTS
) &&
524 !dialogs_show_question_full(NULL
, NULL
, NULL
,
526 _("Filename already exists!")))
530 case GTK_RESPONSE_ACCEPT
:
532 gchar
*utf8_filename
;
534 utf8_filename
= utils_get_utf8_from_locale(new_filename
);
535 success
= handle_save_as(utf8_filename
, rename_file
);
536 g_free(utf8_filename
);
539 case GTK_RESPONSE_DELETE_EVENT
:
540 case GTK_RESPONSE_CANCEL
:
544 g_free(new_filename
);
550 static GtkWidget
*create_save_file_dialog(GeanyDocument
*doc
)
552 GtkWidget
*dialog
, *rename_btn
;
553 const gchar
*initdir
;
555 dialog
= gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets
.window
),
556 GTK_FILE_CHOOSER_ACTION_SAVE
, NULL
, NULL
);
557 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
558 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog
), TRUE
);
559 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog
), FALSE
);
560 gtk_window_set_type_hint(GTK_WINDOW(dialog
), GDK_WINDOW_TYPE_HINT_DIALOG
);
561 gtk_window_set_transient_for(GTK_WINDOW(dialog
), GTK_WINDOW(main_widgets
.window
));
562 gtk_widget_set_name(dialog
, "GeanyDialog");
564 rename_btn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), _("R_ename"), GEANY_RESPONSE_RENAME
);
565 gtk_widget_set_tooltip_text(rename_btn
, _("Save the file and rename it"));
566 /* disable rename unless file exists on disk */
567 gtk_widget_set_sensitive(rename_btn
, doc
->real_path
!= NULL
);
569 gtk_dialog_add_buttons(GTK_DIALOG(dialog
),
570 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
571 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
, NULL
);
572 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_ACCEPT
);
574 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog
), TRUE
);
575 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog
), FALSE
);
577 /* set the folder by default to the project base dir or the global pref for opening files */
578 initdir
= utils_get_default_dir_utf8();
581 gchar
*linitdir
= utils_get_locale_from_utf8(initdir
);
582 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), linitdir
);
589 static gboolean
show_save_as_gtk(GeanyDocument
*doc
)
594 g_return_val_if_fail(DOC_VALID(doc
), FALSE
);
596 dialog
= create_save_file_dialog(doc
);
598 if (doc
->file_name
!= NULL
)
600 if (g_path_is_absolute(doc
->file_name
))
602 gchar
*locale_filename
= utils_get_locale_from_utf8(doc
->file_name
);
603 gchar
*locale_basename
= g_path_get_basename(locale_filename
);
604 gchar
*locale_dirname
= g_path_get_dirname(locale_filename
);
606 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), locale_dirname
);
607 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), locale_basename
);
609 g_free(locale_filename
);
610 g_free(locale_basename
);
611 g_free(locale_dirname
);
614 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), doc
->file_name
);
620 if (doc
->file_type
!= NULL
&& doc
->file_type
->extension
!= NULL
)
621 fname
= g_strconcat(GEANY_STRING_UNTITLED
, ".",
622 doc
->file_type
->extension
, NULL
);
624 fname
= g_strdup(GEANY_STRING_UNTITLED
);
626 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog
), fname
);
631 if (app
->project
&& !EMPTY(app
->project
->base_path
))
632 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
633 app
->project
->base_path
, NULL
);
635 /* Run the dialog synchronously, pausing this function call */
638 resp
= gtk_dialog_run(GTK_DIALOG(dialog
));
640 while (! save_as_dialog_handle_response(dialog
, resp
));
642 if (app
->project
&& !EMPTY(app
->project
->base_path
))
643 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog
),
644 app
->project
->base_path
, NULL
);
646 gtk_widget_destroy(dialog
);
648 return (resp
== GTK_RESPONSE_ACCEPT
);
653 * Shows the Save As dialog for the current notebook page.
655 * @return @c TRUE if the file was saved, otherwise @c FALSE.
657 gboolean
dialogs_show_save_as(void)
659 GeanyDocument
*doc
= document_get_current();
660 gboolean result
= FALSE
;
662 g_return_val_if_fail(doc
, FALSE
);
665 if (interface_prefs
.use_native_windows_dialogs
)
667 gchar
*utf8_name
= win32_show_document_save_as_dialog(GTK_WINDOW(main_widgets
.window
),
668 _("Save File"), DOC_FILENAME(doc
));
669 if (utf8_name
!= NULL
)
670 result
= handle_save_as(utf8_name
, FALSE
);
674 result
= show_save_as_gtk(doc
);
680 static void show_msgbox_dialog(GtkWidget
*dialog
, GtkMessageType type
, GtkWindow
*parent
)
685 case GTK_MESSAGE_ERROR
:
688 case GTK_MESSAGE_QUESTION
:
689 title
= _("Question");
691 case GTK_MESSAGE_WARNING
:
692 title
= _("Warning");
695 title
= _("Information");
698 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
699 gtk_window_set_icon_name(GTK_WINDOW(dialog
), "geany");
700 gtk_widget_set_name(dialog
, "GeanyDialog");
702 gtk_dialog_run(GTK_DIALOG(dialog
));
703 gtk_widget_destroy(dialog
);
709 * Shows a message box of the type @a type with @a text.
710 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
711 * message dialog box is shown.
713 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
714 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
715 * @param text Printf()-style format string.
716 * @param ... Arguments for the @a text format string.
718 void dialogs_show_msgbox(GtkMessageType type
, const gchar
*text
, ...)
725 GtkWindow
*parent
= (main_status
.main_window_realized
) ? GTK_WINDOW(main_widgets
.window
) : NULL
;
727 va_start(args
, text
);
728 string
= g_strdup_vprintf(text
, args
);
732 win32_message_dialog(GTK_WIDGET(parent
), type
, string
);
734 dialog
= gtk_message_dialog_new(parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
735 type
, GTK_BUTTONS_OK
, "%s", string
);
736 show_msgbox_dialog(dialog
, type
, parent
);
742 void dialogs_show_msgbox_with_secondary(GtkMessageType type
, const gchar
*text
, const gchar
*secondary
)
744 GtkWindow
*parent
= (main_status
.main_window_realized
) ? GTK_WINDOW(main_widgets
.window
) : NULL
;
746 /* put the two strings together because Windows message boxes don't support secondary texts */
747 gchar
*string
= g_strconcat(text
, "\n", secondary
, NULL
);
748 win32_message_dialog(GTK_WIDGET(parent
), type
, string
);
752 dialog
= gtk_message_dialog_new(parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
753 type
, GTK_BUTTONS_OK
, "%s", text
);
754 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
), "%s", secondary
);
755 show_msgbox_dialog(dialog
, type
, parent
);
760 static gint
run_unsaved_dialog(const gchar
*msg
, const gchar
*msg2
)
762 GtkWidget
*dialog
, *button
;
765 dialog
= gtk_message_dialog_new(GTK_WINDOW(main_widgets
.window
), GTK_DIALOG_DESTROY_WITH_PARENT
,
766 GTK_MESSAGE_QUESTION
, GTK_BUTTONS_NONE
, "%s", msg
);
767 gtk_window_set_title(GTK_WINDOW(dialog
), _("Question"));
768 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
), "%s", msg2
);
769 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
771 button
= ui_button_new_with_image(GTK_STOCK_CLEAR
, _("_Don't save"));
772 gtk_dialog_add_action_widget(GTK_DIALOG(dialog
), button
, GTK_RESPONSE_NO
);
773 gtk_widget_show(button
);
775 gtk_dialog_add_button(GTK_DIALOG(dialog
), GTK_STOCK_SAVE
, GTK_RESPONSE_YES
);
777 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_YES
);
778 ret
= gtk_dialog_run(GTK_DIALOG(dialog
));
780 gtk_widget_destroy(dialog
);
786 gboolean
dialogs_show_unsaved_file(GeanyDocument
*doc
)
788 gchar
*msg
, *short_fn
= NULL
;
791 gboolean old_quitting_state
= main_status
.quitting
;
793 /* display the file tab to remind the user of the document */
794 main_status
.quitting
= FALSE
;
795 document_show_tab(doc
);
796 main_status
.quitting
= old_quitting_state
;
798 short_fn
= document_get_basename_for_display(doc
, -1);
800 msg
= g_strdup_printf(_("The file '%s' is not saved."), short_fn
);
801 msg2
= _("Do you want to save it before closing?");
804 response
= run_unsaved_dialog(msg
, msg2
);
809 case GTK_RESPONSE_YES
:
810 /* document_save_file() returns the status if the file could be saved */
811 return document_save_file(doc
, FALSE
);
813 case GTK_RESPONSE_NO
:
816 case GTK_RESPONSE_CANCEL
: /* fall through to default and leave the function */
824 /* Use GtkFontChooserDialog on GTK3.2 for consistency, and because
825 * GtkFontSelectionDialog is somewhat broken on 3.4 */
826 #if GTK_CHECK_VERSION(3, 2, 0)
827 # undef GTK_FONT_SELECTION_DIALOG
828 # define GTK_FONT_SELECTION_DIALOG GTK_FONT_CHOOSER_DIALOG
830 # define gtk_font_selection_dialog_new(title) \
831 gtk_font_chooser_dialog_new((title), NULL)
832 # define gtk_font_selection_dialog_get_font_name(dlg) \
833 gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dlg))
834 # define gtk_font_selection_dialog_set_font_name(dlg, font) \
835 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dlg), (font))
839 on_font_dialog_response(GtkDialog
*dialog
, gint response
, gpointer user_data
)
841 gboolean close
= TRUE
;
845 case GTK_RESPONSE_APPLY
:
846 case GTK_RESPONSE_OK
:
850 fontname
= gtk_font_selection_dialog_get_font_name(
851 GTK_FONT_SELECTION_DIALOG(ui_widgets
.open_fontsel
));
852 ui_set_editor_font(fontname
);
855 close
= (response
== GTK_RESPONSE_OK
);
861 gtk_widget_hide(ui_widgets
.open_fontsel
);
866 /* This shows the font selection dialog to choose a font. */
867 void dialogs_show_open_font(void)
870 win32_show_font_dialog();
873 if (ui_widgets
.open_fontsel
== NULL
)
875 GtkWidget
*apply_button
;
877 ui_widgets
.open_fontsel
= gtk_font_selection_dialog_new(_("Choose font"));;
878 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets
.open_fontsel
), 4);
879 gtk_window_set_modal(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
880 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
881 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets
.open_fontsel
), TRUE
);
882 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets
.open_fontsel
), GDK_WINDOW_TYPE_HINT_DIALOG
);
883 gtk_widget_set_name(ui_widgets
.open_fontsel
, "GeanyDialog");
885 #if GTK_CHECK_VERSION(2, 20, 0)
886 /* apply button doesn't have a getter and is hidden by default, but we'd like to show it */
887 apply_button
= gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets
.open_fontsel
), GTK_RESPONSE_APPLY
);
889 apply_button
= GTK_FONT_SELECTION_DIALOG(ui_widgets
.open_fontsel
)->apply_button
;
892 gtk_widget_show(apply_button
);
894 g_signal_connect(ui_widgets
.open_fontsel
,
895 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
896 g_signal_connect(ui_widgets
.open_fontsel
,
897 "response", G_CALLBACK(on_font_dialog_response
), NULL
);
899 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets
.open_fontsel
), GTK_WINDOW(main_widgets
.window
));
901 gtk_font_selection_dialog_set_font_name(
902 GTK_FONT_SELECTION_DIALOG(ui_widgets
.open_fontsel
), interface_prefs
.editor_font
);
903 /* We make sure the dialog is visible. */
904 gtk_window_present(GTK_WINDOW(ui_widgets
.open_fontsel
));
910 on_input_dialog_show(GtkDialog
*dialog
, GtkWidget
*entry
)
912 gtk_widget_grab_focus(entry
);
917 on_input_entry_activate(GtkEntry
*entry
, GtkDialog
*dialog
)
919 gtk_dialog_response(dialog
, GTK_RESPONSE_ACCEPT
);
924 on_input_numeric_activate(GtkEntry
*entry
, GtkDialog
*dialog
)
926 gtk_dialog_response(dialog
, GTK_RESPONSE_ACCEPT
);
931 on_input_dialog_response(GtkDialog
*dialog
, gint response
, GtkWidget
*entry
)
933 gboolean persistent
= (gboolean
) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dialog
), "has_combo"));
935 if (response
== GTK_RESPONSE_ACCEPT
)
937 const gchar
*str
= gtk_entry_get_text(GTK_ENTRY(entry
));
938 GeanyInputCallback input_cb
=
939 (GeanyInputCallback
) g_object_get_data(G_OBJECT(dialog
), "input_cb");
943 GtkWidget
*combo
= (GtkWidget
*) g_object_get_data(G_OBJECT(dialog
), "combo");
944 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(combo
), str
, 0);
948 gtk_widget_hide(GTK_WIDGET(dialog
));
952 static void add_input_widgets(GtkWidget
*dialog
, GtkWidget
*vbox
,
953 const gchar
*label_text
, const gchar
*default_text
, gboolean persistent
,
954 GCallback insert_text_cb
)
960 GtkWidget
*label
= gtk_label_new(label_text
);
961 gtk_label_set_line_wrap(GTK_LABEL(label
), TRUE
);
962 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
963 gtk_container_add(GTK_CONTAINER(vbox
), label
);
966 if (persistent
) /* remember previous entry text in a combo box */
968 GtkWidget
*combo
= gtk_combo_box_text_new_with_entry();
970 entry
= gtk_bin_get_child(GTK_BIN(combo
));
971 ui_entry_add_clear_icon(GTK_ENTRY(entry
));
972 g_object_set_data(G_OBJECT(dialog
), "combo", combo
);
973 gtk_container_add(GTK_CONTAINER(vbox
), combo
);
977 entry
= gtk_entry_new();
978 ui_entry_add_clear_icon(GTK_ENTRY(entry
));
979 gtk_container_add(GTK_CONTAINER(vbox
), entry
);
982 if (default_text
!= NULL
)
984 gtk_entry_set_text(GTK_ENTRY(entry
), default_text
);
986 gtk_entry_set_max_length(GTK_ENTRY(entry
), 255);
987 gtk_entry_set_width_chars(GTK_ENTRY(entry
), 30);
989 if (insert_text_cb
!= NULL
)
990 g_signal_connect(entry
, "insert-text", insert_text_cb
, NULL
);
991 g_signal_connect(entry
, "activate", G_CALLBACK(on_input_entry_activate
), dialog
);
992 g_signal_connect(dialog
, "show", G_CALLBACK(on_input_dialog_show
), entry
);
993 g_signal_connect(dialog
, "response", G_CALLBACK(on_input_dialog_response
), entry
);
997 /* Create and display an input dialog.
998 * persistent: whether to remember previous entry text in a combo box;
999 * in this case the dialog returned is not destroyed on a response,
1000 * and can be reshown.
1001 * Returns: the dialog widget. */
1003 dialogs_show_input_full(const gchar
*title
, GtkWindow
*parent
,
1004 const gchar
*label_text
, const gchar
*default_text
,
1005 gboolean persistent
, GeanyInputCallback input_cb
, GCallback insert_text_cb
)
1007 GtkWidget
*dialog
, *vbox
;
1009 dialog
= gtk_dialog_new_with_buttons(title
, parent
,
1010 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1011 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
1012 vbox
= ui_dialog_vbox_new(GTK_DIALOG(dialog
));
1013 gtk_widget_set_name(dialog
, "GeanyDialog");
1014 gtk_box_set_spacing(GTK_BOX(vbox
), 6);
1016 g_object_set_data(G_OBJECT(dialog
), "has_combo", GINT_TO_POINTER(persistent
));
1017 g_object_set_data(G_OBJECT(dialog
), "input_cb", (gpointer
) input_cb
);
1019 add_input_widgets(dialog
, vbox
, label_text
, default_text
, persistent
, insert_text_cb
);
1023 /* override default handler */
1024 g_signal_connect(dialog
, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
1025 gtk_widget_show_all(dialog
);
1028 gtk_widget_show_all(dialog
);
1029 gtk_dialog_run(GTK_DIALOG(dialog
));
1030 gtk_widget_destroy(dialog
);
1035 /* Remember previous entry text in a combo box.
1036 * Returns: the dialog widget. */
1038 dialogs_show_input_persistent(const gchar
*title
, GtkWindow
*parent
,
1039 const gchar
*label_text
, const gchar
*default_text
,
1040 GeanyInputCallback input_cb
)
1042 return dialogs_show_input_full(title
, parent
, label_text
, default_text
, TRUE
, input_cb
, NULL
);
1046 /* ugly hack - user_data not supported for callback */
1047 static gchar
*dialog_input
= NULL
;
1049 static void on_dialog_input(const gchar
*str
)
1051 dialog_input
= g_strdup(str
);
1055 /** Asks the user for text input.
1056 * @param title Dialog title.
1057 * @param parent The currently focused window, usually @c geany->main_widgets->window.
1058 * @c NULL can be used but is discouraged due to window manager effects.
1059 * @param label_text Label text, or @c NULL.
1060 * @param default_text Text to display in the input field, or @c NULL.
1061 * @return New copy of user input or @c NULL if cancelled.
1063 gchar
*dialogs_show_input(const gchar
*title
, GtkWindow
*parent
, const gchar
*label_text
,
1064 const gchar
*default_text
)
1066 dialog_input
= NULL
;
1067 dialogs_show_input_full(title
, parent
, label_text
, default_text
, FALSE
, on_dialog_input
, NULL
);
1068 return dialog_input
;
1072 /* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1073 /* Returns: newly allocated copy of the entry text or NULL on cancel.
1074 * Specialised variant for Goto Line dialog. */
1075 gchar
*dialogs_show_input_goto_line(const gchar
*title
, GtkWindow
*parent
, const gchar
*label_text
,
1076 const gchar
*default_text
)
1078 dialog_input
= NULL
;
1079 dialogs_show_input_full(
1080 title
, parent
, label_text
, default_text
, FALSE
, on_dialog_input
,
1081 G_CALLBACK(ui_editable_insert_text_callback
));
1082 return dialog_input
;
1087 * Shows an input box to enter a numerical value using a GtkSpinButton.
1088 * If the dialog is aborted, @a value remains untouched.
1090 * @param title The dialog title.
1091 * @param label_text The shown dialog label.
1092 * @param value The default value for the spin button and the return location of the entered value.
1094 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1095 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1096 * @param step Increment added or subtracted by spinning the widget
1097 * (see documentation for @c gtk_spin_button_new_with_range()).
1099 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1103 gboolean
dialogs_show_input_numeric(const gchar
*title
, const gchar
*label_text
,
1104 gdouble
*value
, gdouble min
, gdouble max
, gdouble step
)
1106 GtkWidget
*dialog
, *label
, *spin
, *vbox
;
1107 gboolean res
= FALSE
;
1109 g_return_val_if_fail(title
!= NULL
, FALSE
);
1110 g_return_val_if_fail(label_text
!= NULL
, FALSE
);
1111 g_return_val_if_fail(value
!= NULL
, FALSE
);
1113 dialog
= gtk_dialog_new_with_buttons(title
, GTK_WINDOW(main_widgets
.window
),
1114 GTK_DIALOG_DESTROY_WITH_PARENT
,
1115 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1116 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
1117 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_CANCEL
);
1118 vbox
= ui_dialog_vbox_new(GTK_DIALOG(dialog
));
1119 gtk_widget_set_name(dialog
, "GeanyDialog");
1121 label
= gtk_label_new(label_text
);
1122 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0.5);
1124 spin
= gtk_spin_button_new_with_range(min
, max
, step
);
1125 ui_entry_add_clear_icon(GTK_ENTRY(spin
));
1126 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), *value
);
1127 g_signal_connect(spin
, "activate", G_CALLBACK(on_input_numeric_activate
), dialog
);
1129 gtk_container_add(GTK_CONTAINER(vbox
), label
);
1130 gtk_container_add(GTK_CONTAINER(vbox
), spin
);
1131 gtk_widget_show_all(vbox
);
1133 if (gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_ACCEPT
)
1135 *value
= gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin
));
1138 gtk_widget_destroy(dialog
);
1144 void dialogs_show_file_properties(GeanyDocument
*doc
)
1146 GtkWidget
*dialog
, *label
, *image
, *check
;
1147 gchar
*file_size
, *title
, *base_name
, *time_changed
, *time_modified
, *time_accessed
, *enctext
;
1150 #ifdef HAVE_SYS_TYPES_H
1154 gchar
*locale_filename
;
1160 /* define this ones, to avoid later trouble */
1175 g_return_if_fail(doc
== NULL
|| doc
->is_valid
);
1177 if (doc
== NULL
|| doc
->file_name
== NULL
)
1179 dialogs_show_msgbox(GTK_MESSAGE_ERROR
,
1180 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1185 #ifdef HAVE_SYS_TYPES_H
1186 locale_filename
= utils_get_locale_from_utf8(doc
->file_name
);
1187 if (g_stat(locale_filename
, &st
) == 0)
1189 /* first copy the returned string and the trim it, to not modify the static glibc string
1190 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1191 time_changed
= g_strchomp(g_strdup(ctime(&st
.st_ctime
)));
1192 time_modified
= g_strchomp(g_strdup(ctime(&st
.st_mtime
)));
1193 time_accessed
= g_strchomp(g_strdup(ctime(&st
.st_atime
)));
1194 filesize
= st
.st_size
;
1199 time_changed
= g_strdup(_("unknown"));
1200 time_modified
= g_strdup(_("unknown"));
1201 time_accessed
= g_strdup(_("unknown"));
1202 filesize
= (off_t
) 0;
1205 g_free(locale_filename
);
1207 time_changed
= g_strdup(_("unknown"));
1208 time_modified
= g_strdup(_("unknown"));
1209 time_accessed
= g_strdup(_("unknown"));
1212 base_name
= g_path_get_basename(doc
->file_name
);
1213 short_name
= utils_str_middle_truncate(base_name
, 30);
1214 title
= g_strdup_printf(_("%s Properties"), short_name
);
1215 dialog
= ui_builder_get_object("properties_dialog");
1216 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
1219 gtk_widget_set_name(dialog
, "GeanyDialog");
1221 label
= ui_lookup_widget(dialog
, "file_name_label");
1222 gtk_label_set_text(GTK_LABEL(label
), base_name
);
1224 image
= ui_lookup_widget(dialog
, "file_type_image");
1225 pixbuf
= ui_get_mime_icon(doc
->file_type
->mime_type
, GTK_ICON_SIZE_BUTTON
);
1226 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pixbuf
);
1227 g_object_unref(pixbuf
);
1229 label
= ui_lookup_widget(dialog
, "file_type_label");
1230 gtk_label_set_text(GTK_LABEL(label
), doc
->file_type
->title
);
1232 label
= ui_lookup_widget(dialog
, "file_size_label");
1233 file_size
= utils_make_human_readable_str(filesize
, 1, 0);
1234 gtk_label_set_text(GTK_LABEL(label
), file_size
);
1237 label
= ui_lookup_widget(dialog
, "file_location_label");
1238 gtk_label_set_text(GTK_LABEL(label
), doc
->file_name
);
1240 check
= ui_lookup_widget(dialog
, "file_read_only_check");
1241 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), doc
->readonly
);
1243 label
= ui_lookup_widget(dialog
, "file_encoding_label");
1244 enctext
= g_strdup_printf("%s %s",
1246 (encodings_is_unicode_charset(doc
->encoding
)) ?
1247 ((doc
->has_bom
) ? _("(with BOM)") : _("(without BOM)")) : "");
1248 gtk_label_set_text(GTK_LABEL(label
), enctext
);
1251 label
= ui_lookup_widget(dialog
, "file_modified_label");
1252 gtk_label_set_text(GTK_LABEL(label
), time_modified
);
1253 label
= ui_lookup_widget(dialog
, "file_changed_label");
1254 gtk_label_set_text(GTK_LABEL(label
), time_changed
);
1255 label
= ui_lookup_widget(dialog
, "file_accessed_label");
1256 gtk_label_set_text(GTK_LABEL(label
), time_accessed
);
1259 check
= ui_lookup_widget(dialog
, "file_perm_owner_r_check");
1260 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IRUSR
);
1261 check
= ui_lookup_widget(dialog
, "file_perm_owner_w_check");
1262 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWUSR
);
1263 check
= ui_lookup_widget(dialog
, "file_perm_owner_x_check");
1264 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXUSR
);
1265 check
= ui_lookup_widget(dialog
, "file_perm_group_r_check");
1266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IRGRP
);
1267 check
= ui_lookup_widget(dialog
, "file_perm_group_w_check");
1268 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWGRP
);
1269 check
= ui_lookup_widget(dialog
, "file_perm_group_x_check");
1270 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXGRP
);
1271 check
= ui_lookup_widget(dialog
, "file_perm_other_r_check");
1272 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IROTH
);
1273 check
= ui_lookup_widget(dialog
, "file_perm_other_w_check");
1274 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IWOTH
);
1275 check
= ui_lookup_widget(dialog
, "file_perm_other_x_check");
1276 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
), mode
& S_IXOTH
);
1279 g_free(time_changed
);
1280 g_free(time_modified
);
1281 g_free(time_accessed
);
1283 gtk_widget_show(dialog
);
1287 /* extra_text can be NULL; otherwise it is displayed below main_text.
1288 * if parent is NULL, main_widgets.window will be used
1289 * btn_1, btn_2, btn_3 can be NULL.
1290 * returns response_1, response_2 or response_3 */
1291 static gint
show_prompt(GtkWidget
*parent
,
1292 const gchar
*btn_1
, GtkResponseType response_1
,
1293 const gchar
*btn_2
, GtkResponseType response_2
,
1294 const gchar
*btn_3
, GtkResponseType response_3
,
1295 const gchar
*question_text
, const gchar
*extra_text
)
1297 gboolean ret
= FALSE
;
1303 btn_2
= GTK_STOCK_NO
;
1304 response_2
= GTK_RESPONSE_NO
;
1308 btn_3
= GTK_STOCK_YES
;
1309 response_3
= GTK_RESPONSE_YES
;
1313 /* our native dialog code doesn't support custom buttons */
1314 if (utils_str_equal(btn_3
, GTK_STOCK_YES
) &&
1315 utils_str_equal(btn_2
, GTK_STOCK_NO
) && btn_1
== NULL
)
1317 gchar
*string
= (extra_text
== NULL
) ? g_strdup(question_text
) :
1318 g_strconcat(question_text
, "\n\n", extra_text
, NULL
);
1320 ret
= win32_message_dialog(parent
, GTK_MESSAGE_QUESTION
, string
);
1321 ret
= ret
? response_3
: response_2
;
1326 if (parent
== NULL
&& main_status
.main_window_realized
)
1327 parent
= main_widgets
.window
;
1329 dialog
= gtk_message_dialog_new(GTK_WINDOW(parent
),
1330 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_QUESTION
,
1331 GTK_BUTTONS_NONE
, "%s", question_text
);
1332 gtk_widget_set_name(dialog
, "GeanyDialog");
1333 gtk_window_set_title(GTK_WINDOW(dialog
), _("Question"));
1334 gtk_window_set_icon_name(GTK_WINDOW(dialog
), "geany");
1336 /* question_text will be in bold if optional extra_text used */
1337 if (extra_text
!= NULL
)
1338 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog
),
1342 gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_1
, response_1
);
1344 /* For a cancel button, use cancel response so user can press escape to cancel */
1345 btn
= gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_2
,
1346 utils_str_equal(btn_2
, GTK_STOCK_CANCEL
) ? GTK_RESPONSE_CANCEL
: response_2
);
1347 /* we don't want a default, but we need to override the apply button as default */
1348 gtk_widget_grab_default(btn
);
1349 gtk_dialog_add_button(GTK_DIALOG(dialog
), btn_3
, response_3
);
1351 ret
= gtk_dialog_run(GTK_DIALOG(dialog
));
1352 gtk_widget_destroy(dialog
);
1354 if (ret
== GTK_RESPONSE_CANCEL
)
1361 * Shows a question message box with @a text and Yes/No buttons.
1362 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1363 * message dialog box is shown.
1365 * @param text Printf()-style format string.
1366 * @param ... Arguments for the @a text format string.
1368 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1370 gboolean
dialogs_show_question(const gchar
*text
, ...)
1374 GtkWidget
*parent
= (main_status
.main_window_realized
) ? main_widgets
.window
: NULL
;
1377 va_start(args
, text
);
1378 string
= g_strdup_vprintf(text
, args
);
1380 result
= show_prompt(parent
,
1381 NULL
, GTK_RESPONSE_NONE
,
1382 GTK_STOCK_NO
, GTK_RESPONSE_NO
,
1383 GTK_STOCK_YES
, GTK_RESPONSE_YES
,
1386 return (result
== GTK_RESPONSE_YES
);
1390 /* extra_text can be NULL; otherwise it is displayed below main_text.
1391 * if parent is NULL, main_widgets.window will be used
1392 * yes_btn, no_btn can be NULL. */
1393 gboolean
dialogs_show_question_full(GtkWidget
*parent
, const gchar
*yes_btn
, const gchar
*no_btn
,
1394 const gchar
*extra_text
, const gchar
*main_text
, ...)
1400 va_start(args
, main_text
);
1401 string
= g_strdup_vprintf(main_text
, args
);
1403 result
= show_prompt(parent
,
1404 NULL
, GTK_RESPONSE_NONE
,
1405 no_btn
, GTK_RESPONSE_NO
,
1406 yes_btn
, GTK_RESPONSE_YES
,
1407 string
, extra_text
);
1409 return (result
== GTK_RESPONSE_YES
);
1413 /* extra_text can be NULL; otherwise it is displayed below main_text.
1414 * if parent is NULL, main_widgets.window will be used
1415 * btn_1, btn_2, btn_3 can be NULL.
1416 * returns response_1, response_2 or response_3 */
1417 gint
dialogs_show_prompt(GtkWidget
*parent
,
1418 const gchar
*btn_1
, GtkResponseType response_1
,
1419 const gchar
*btn_2
, GtkResponseType response_2
,
1420 const gchar
*btn_3
, GtkResponseType response_3
,
1421 const gchar
*extra_text
, const gchar
*main_text
, ...)
1427 va_start(args
, main_text
);
1428 string
= g_strdup_vprintf(main_text
, args
);
1430 result
= show_prompt(parent
, btn_1
, response_1
, btn_2
, response_2
, btn_3
, response_3
,
1431 string
, extra_text
);