Create branch for 0.19.1 release (copied from 0.19).
[geany-mirror.git] / src / dialogs.c
blobe43ea8fa6a319cca038aad5eaacff9a7d3a6be8a
1 /*
2 * dialogs.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $Id$
25 * File related dialogs, miscellaneous dialogs, font dialog.
28 #include "geany.h"
30 #include <gdk/gdkkeysyms.h>
31 #include <string.h>
33 #ifdef HAVE_SYS_TIME_H
34 # include <sys/time.h>
35 #endif
36 #include <time.h>
38 #ifdef HAVE_SYS_TYPES_H
39 # include <sys/types.h>
40 #endif
42 /* gstdio.h also includes sys/stat.h */
43 #include <glib/gstdio.h>
45 #include "dialogs.h"
47 #include "callbacks.h"
48 #include "document.h"
49 #include "filetypes.h"
50 #include "win32.h"
51 #include "sciwrappers.h"
52 #include "support.h"
53 #include "utils.h"
54 #include "ui_utils.h"
55 #include "keybindings.h"
56 #include "encodings.h"
57 #include "build.h"
58 #include "main.h"
59 #include "project.h"
62 enum
64 GEANY_RESPONSE_RENAME,
65 GEANY_RESPONSE_VIEW
69 static gboolean handle_save_as(const gchar *utf8_filename, gboolean open_new_tab,
70 gboolean rename_file);
73 static GtkWidget *add_file_open_extra_widget(void);
76 static void
77 on_file_open_dialog_response (GtkDialog *dialog,
78 gint response,
79 gpointer user_data)
81 gtk_widget_hide(ui_widgets.open_filesel);
83 if (response == GTK_RESPONSE_ACCEPT || response == GEANY_RESPONSE_VIEW)
85 GSList *filelist;
86 gint filetype_idx = gtk_combo_box_get_active(GTK_COMBO_BOX(
87 ui_lookup_widget(GTK_WIDGET(dialog), "filetype_combo")));
88 gint encoding_idx = gtk_combo_box_get_active(GTK_COMBO_BOX(
89 ui_lookup_widget(GTK_WIDGET(dialog), "encoding_combo")));
90 GeanyFiletype *ft = NULL;
91 const gchar *charset = NULL;
92 gboolean ro = (response == GEANY_RESPONSE_VIEW); /* View clicked */
94 /* ignore detect from file item */
95 if (filetype_idx > 0)
96 ft = g_slist_nth_data(filetypes_by_title, filetype_idx);
97 if (encoding_idx >= 0 && encoding_idx < GEANY_ENCODINGS_MAX)
98 charset = encodings[encoding_idx].charset;
100 filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(ui_widgets.open_filesel));
101 if (filelist != NULL)
103 document_open_files(filelist, ro, ft, charset);
104 g_slist_foreach(filelist, (GFunc) g_free, NULL); /* free filenames */
106 g_slist_free(filelist);
108 if (app->project && NZV(app->project->base_path))
109 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
110 app->project->base_path, NULL);
114 static void on_file_open_notify(GObject *filechooser, GParamSpec *pspec, gpointer data)
116 GValue *value;
118 value = g_new0(GValue, 1);
119 g_value_init(value, pspec->value_type);
120 g_object_get_property(filechooser, pspec->name, value);
122 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
123 ui_lookup_widget(GTK_WIDGET(filechooser), "check_hidden")), g_value_get_boolean(value));
127 static void
128 on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, gpointer user_data)
130 gboolean is_on = gtk_toggle_button_get_active(togglebutton);
132 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(ui_widgets.open_filesel), is_on);
136 static void create_open_file_dialog(void)
138 GtkWidget *filetype_combo, *encoding_combo;
139 GtkWidget *viewbtn;
140 guint i;
141 gchar *encoding_string;
142 GSList *node;
144 ui_widgets.open_filesel = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets.window),
145 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
146 gtk_widget_set_name(ui_widgets.open_filesel, "GeanyDialog");
148 viewbtn = gtk_dialog_add_button(GTK_DIALOG(ui_widgets.open_filesel), _("_View"),
149 GEANY_RESPONSE_VIEW);
150 ui_widget_set_tooltip_text(viewbtn,
151 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
153 gtk_dialog_add_buttons(GTK_DIALOG(ui_widgets.open_filesel),
154 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
155 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
156 gtk_dialog_set_default_response(GTK_DIALOG(ui_widgets.open_filesel), GTK_RESPONSE_ACCEPT);
158 gtk_widget_set_size_request(ui_widgets.open_filesel, -1, 460);
159 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_filesel), TRUE);
160 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_filesel), TRUE);
161 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_filesel), FALSE);
162 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_filesel), GDK_WINDOW_TYPE_HINT_DIALOG);
163 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_filesel), GTK_WINDOW(main_widgets.window));
164 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(ui_widgets.open_filesel), TRUE);
165 if (gtk_check_version(2, 14, 0) == NULL)
166 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(ui_widgets.open_filesel), FALSE);
168 /* add checkboxes and filename entry */
169 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
170 add_file_open_extra_widget());
171 filetype_combo = ui_lookup_widget(ui_widgets.open_filesel, "filetype_combo");
173 gtk_combo_box_append_text(GTK_COMBO_BOX(filetype_combo), _("Detect by file extension"));
174 /* add FileFilters(start with "All Files") */
175 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
176 filetypes_create_file_filter(filetypes[GEANY_FILETYPES_NONE]));
177 /* now create meta filter "All Source" */
178 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
179 filetypes_create_file_filter_all_source());
180 foreach_slist(node, filetypes_by_title)
182 GeanyFiletype *ft = node->data;
184 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
185 continue;
186 gtk_combo_box_append_text(GTK_COMBO_BOX(filetype_combo), ft->title);
187 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
188 filetypes_create_file_filter(ft));
190 gtk_combo_box_set_active(GTK_COMBO_BOX(filetype_combo), 0);
192 /* fill encoding combo box */
193 encoding_combo = ui_lookup_widget(ui_widgets.open_filesel, "encoding_combo");
194 for (i = 0; i < GEANY_ENCODINGS_MAX; i++)
196 encoding_string = encodings_to_string(&encodings[i]);
197 gtk_combo_box_append_text(GTK_COMBO_BOX(encoding_combo), encoding_string);
198 g_free(encoding_string);
200 gtk_combo_box_append_text(GTK_COMBO_BOX(encoding_combo), _("Detect from file"));
201 gtk_combo_box_set_active(GTK_COMBO_BOX(encoding_combo), GEANY_ENCODINGS_MAX);
203 g_signal_connect(ui_widgets.open_filesel, "notify::show-hidden",
204 G_CALLBACK(on_file_open_notify), NULL);
205 g_signal_connect(ui_widgets.open_filesel, "delete-event",
206 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
207 g_signal_connect(ui_widgets.open_filesel, "response",
208 G_CALLBACK(on_file_open_dialog_response), NULL);
212 /* This shows the file selection dialog to open a file. */
213 void dialogs_show_open_file()
215 gchar *initdir;
217 /* set dialog directory to the current file's directory, if present */
218 initdir = utils_get_current_file_dir_utf8();
220 /* use project or default startup directory (if set) if no files are open */
221 /** TODO should it only be used when initally open the dialog and not on every show? */
222 if (! initdir)
223 initdir = g_strdup(utils_get_default_dir_utf8());
225 setptr(initdir, utils_get_locale_from_utf8(initdir));
227 #ifdef G_OS_WIN32
228 if (interface_prefs.use_native_windows_dialogs)
229 win32_show_document_open_dialog(GTK_WINDOW(main_widgets.window), _("Open File"), initdir);
230 else
231 #endif
233 /* We use the same file selection widget each time, so first of all we create it
234 * if it hasn't already been created. */
235 if (ui_widgets.open_filesel == NULL)
236 create_open_file_dialog();
238 if (initdir != NULL)
240 if (g_path_is_absolute(initdir))
241 gtk_file_chooser_set_current_folder(
242 GTK_FILE_CHOOSER(ui_widgets.open_filesel), initdir);
245 if (app->project && NZV(app->project->base_path))
246 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(ui_widgets.open_filesel),
247 app->project->base_path, NULL);
249 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(ui_widgets.open_filesel));
250 gtk_window_present(GTK_WINDOW(ui_widgets.open_filesel));
252 g_free(initdir);
256 static GtkWidget *add_file_open_extra_widget()
258 GtkWidget *expander, *vbox, *table, *check_hidden;
259 GtkWidget *filetype_ebox, *filetype_label, *filetype_combo;
260 GtkWidget *encoding_ebox, *encoding_label, *encoding_combo;
262 expander = gtk_expander_new_with_mnemonic(_("_More Options"));
263 vbox = gtk_vbox_new(FALSE, 6);
264 gtk_container_add(GTK_CONTAINER(expander), vbox);
266 table = gtk_table_new(2, 4, FALSE);
268 /* line 1 with checkbox and encoding combo */
269 check_hidden = gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
270 gtk_widget_show(check_hidden);
271 gtk_table_attach(GTK_TABLE(table), check_hidden, 0, 1, 0, 1,
272 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
273 (GtkAttachOptions) (0), 0, 5);
275 /* spacing */
276 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""), 1, 2, 0, 1,
277 (GtkAttachOptions) (GTK_FILL),
278 (GtkAttachOptions) (0), 5, 5);
280 encoding_label = gtk_label_new(_("Set encoding:"));
281 gtk_misc_set_alignment(GTK_MISC(encoding_label), 1, 0);
282 gtk_table_attach(GTK_TABLE(table), encoding_label, 2, 3, 0, 1,
283 (GtkAttachOptions) (GTK_FILL),
284 (GtkAttachOptions) (0), 4, 5);
285 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
286 encoding_ebox = gtk_event_box_new();
287 encoding_combo = gtk_combo_box_new_text();
288 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(encoding_combo), 3);
289 ui_widget_set_tooltip_text(encoding_ebox,
290 _("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."));
291 gtk_container_add(GTK_CONTAINER(encoding_ebox), encoding_combo);
292 gtk_table_attach(GTK_TABLE(table), encoding_ebox, 3, 4, 0, 1,
293 (GtkAttachOptions) (GTK_FILL),
294 (GtkAttachOptions) (0), 0, 5);
296 /* line 2 with filetype combo */
297 filetype_label = gtk_label_new(_("Set filetype:"));
298 gtk_misc_set_alignment(GTK_MISC(filetype_label), 1, 0);
299 gtk_table_attach(GTK_TABLE(table), filetype_label, 2, 3, 1, 2,
300 (GtkAttachOptions) (GTK_FILL),
301 (GtkAttachOptions) (0), 4, 5);
302 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
303 filetype_ebox = gtk_event_box_new();
304 filetype_combo = gtk_combo_box_new_text();
305 gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(filetype_combo), 2);
306 ui_widget_set_tooltip_text(filetype_ebox,
307 _("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."));
308 gtk_container_add(GTK_CONTAINER(filetype_ebox), filetype_combo);
309 gtk_table_attach(GTK_TABLE(table), filetype_ebox, 3, 4, 1, 2,
310 (GtkAttachOptions) (GTK_FILL),
311 (GtkAttachOptions) (0), 0, 5);
313 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
314 gtk_widget_show_all(vbox);
316 g_signal_connect(check_hidden, "toggled",
317 G_CALLBACK(on_file_open_check_hidden_toggled), NULL);
319 g_object_set_data_full(G_OBJECT(ui_widgets.open_filesel), "check_hidden",
320 g_object_ref(check_hidden), (GDestroyNotify)g_object_unref);
321 g_object_set_data_full(G_OBJECT(ui_widgets.open_filesel), "filetype_combo",
322 g_object_ref(filetype_combo), (GDestroyNotify)g_object_unref);
323 g_object_set_data_full(G_OBJECT(ui_widgets.open_filesel), "encoding_combo",
324 g_object_ref(encoding_combo), (GDestroyNotify)g_object_unref);
326 return expander;
330 static void on_save_as_new_tab_toggled(GtkToggleButton *togglebutton, gpointer user_data)
332 gtk_widget_set_sensitive(GTK_WIDGET(user_data),
333 ! gtk_toggle_button_get_active(togglebutton));
337 static gboolean handle_save_as(const gchar *utf8_filename, gboolean open_new_tab, gboolean rename_file)
339 GeanyDocument *doc = document_get_current();
340 gboolean success = FALSE;
342 g_return_val_if_fail(NZV(utf8_filename), FALSE);
344 if (open_new_tab)
345 { /* "open" the saved file in a new tab and switch to it */
346 doc = document_clone(doc, utf8_filename);
347 success = document_save_file_as(doc, NULL);
349 else
351 if (doc->file_name != NULL)
353 if (rename_file)
355 document_rename_file(doc, utf8_filename);
357 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
358 tm_workspace_remove_object(doc->tm_file, TRUE, TRUE);
359 doc->tm_file = NULL;
361 success = document_save_file_as(doc, utf8_filename);
363 build_menu_update(doc);
365 return success;
369 static void
370 on_file_save_dialog_response (GtkDialog *dialog,
371 gint response,
372 gpointer user_data)
374 gboolean rename_file = FALSE;
375 gboolean success = FALSE;
376 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(ui_widgets.save_filesel));
378 switch (response)
380 case GEANY_RESPONSE_RENAME:
381 /* rename doesn't check for empty filename or overwriting */
382 if (! NZV(new_filename))
384 utils_beep();
385 break;
387 if (g_file_test(new_filename, G_FILE_TEST_EXISTS) &&
388 !dialogs_show_question_full(NULL, NULL, NULL,
389 _("Overwrite?"),
390 _("Filename already exists!")))
391 break;
392 rename_file = TRUE;
393 /* fall through */
394 case GTK_RESPONSE_ACCEPT:
396 gboolean open_new_tab = gtk_toggle_button_get_active(
397 GTK_TOGGLE_BUTTON(ui_lookup_widget(ui_widgets.save_filesel, "check_open_new_tab")));
398 gchar *utf8_filename;
400 utf8_filename = utils_get_utf8_from_locale(new_filename);
401 success = handle_save_as(utf8_filename, open_new_tab, rename_file);
403 g_free(utf8_filename);
404 break;
406 case GTK_RESPONSE_CANCEL:
407 success = TRUE;
408 break;
410 g_free(new_filename);
412 if (success)
413 gtk_widget_hide(ui_widgets.save_filesel);
417 static void create_save_file_dialog(void)
419 GtkWidget *vbox, *check_open_new_tab, *rename_btn;
420 const gchar *initdir;
422 ui_widgets.save_filesel = gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets.window),
423 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
424 gtk_window_set_modal(GTK_WINDOW(ui_widgets.save_filesel), TRUE);
425 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.save_filesel), TRUE);
426 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.save_filesel), FALSE);
427 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.save_filesel), GDK_WINDOW_TYPE_HINT_DIALOG);
428 gtk_widget_set_name(ui_widgets.save_filesel, "GeanyDialog");
430 rename_btn = gtk_dialog_add_button(GTK_DIALOG(ui_widgets.save_filesel), _("R_ename"),
431 GEANY_RESPONSE_RENAME);
432 ui_widget_set_tooltip_text(rename_btn, _("Save the file and rename it"));
434 gtk_dialog_add_buttons(GTK_DIALOG(ui_widgets.save_filesel),
435 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
436 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
437 gtk_dialog_set_default_response(GTK_DIALOG(ui_widgets.save_filesel), GTK_RESPONSE_ACCEPT);
439 vbox = gtk_vbox_new(FALSE, 0);
440 check_open_new_tab = gtk_check_button_new_with_mnemonic(_("_Open file in a new tab"));
441 ui_widget_set_tooltip_text(check_open_new_tab,
442 _("Keep the current unsaved document open"
443 " and open the newly saved file in a new tab"));
444 gtk_box_pack_start(GTK_BOX(vbox), check_open_new_tab, FALSE, FALSE, 0);
445 gtk_widget_show_all(vbox);
446 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(ui_widgets.save_filesel), vbox);
447 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(ui_widgets.save_filesel), TRUE);
448 if (gtk_check_version(2, 14, 0) == NULL)
449 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(ui_widgets.save_filesel), FALSE);
451 /* set the folder by default to the project base dir or the global pref for opening files */
452 initdir = utils_get_default_dir_utf8();
453 if (initdir)
455 gchar *linitdir = utils_get_locale_from_utf8(initdir);
456 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(ui_widgets.save_filesel), linitdir);
457 g_free(linitdir);
460 g_signal_connect(check_open_new_tab, "toggled",
461 G_CALLBACK(on_save_as_new_tab_toggled), rename_btn);
463 g_object_set_data_full(G_OBJECT(ui_widgets.save_filesel), "check_open_new_tab",
464 g_object_ref(check_open_new_tab), (GDestroyNotify)g_object_unref);
466 g_signal_connect(ui_widgets.save_filesel, "delete-event",
467 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
468 g_signal_connect(ui_widgets.save_filesel, "response",
469 G_CALLBACK(on_file_save_dialog_response), NULL);
471 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.save_filesel), GTK_WINDOW(main_widgets.window));
475 static gboolean gtk_show_save_as(void)
477 GeanyDocument *doc = document_get_current();
478 gint resp;
480 g_return_val_if_fail(doc != NULL, FALSE);
482 if (G_UNLIKELY(ui_widgets.save_filesel == NULL))
483 create_save_file_dialog();
485 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(ui_widgets.save_filesel));
487 if (doc->file_name != NULL)
489 if (g_path_is_absolute(doc->file_name))
491 gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name);
492 gchar *locale_basename = g_path_get_basename(locale_filename);
493 gchar *locale_dirname = g_path_get_dirname(locale_filename);
495 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(ui_widgets.save_filesel),
496 locale_dirname);
497 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(ui_widgets.save_filesel),
498 locale_basename);
500 g_free(locale_filename);
501 g_free(locale_basename);
502 g_free(locale_dirname);
504 else
505 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(ui_widgets.save_filesel),
506 doc->file_name);
508 else
510 gchar *fname = NULL;
512 if (doc->file_type != NULL && doc->file_type->extension != NULL)
513 fname = g_strconcat(GEANY_STRING_UNTITLED, ".",
514 doc->file_type->extension, NULL);
515 else
516 fname = g_strdup(GEANY_STRING_UNTITLED);
518 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(ui_widgets.save_filesel), fname);
520 g_free(fname);
523 if (app->project && NZV(app->project->base_path))
524 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(ui_widgets.save_filesel),
525 app->project->base_path, NULL);
527 /* Run the dialog synchronously, pausing this function call */
528 resp = gtk_dialog_run(GTK_DIALOG(ui_widgets.save_filesel));
530 if (app->project && NZV(app->project->base_path))
531 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(ui_widgets.save_filesel),
532 app->project->base_path, NULL);
534 return (resp == GTK_RESPONSE_ACCEPT);
539 * Shows the Save As dialog for the current notebook page.
541 * @return @c TRUE if the file was saved, otherwise @c FALSE.
543 gboolean dialogs_show_save_as()
545 gboolean result = FALSE;
547 #ifdef G_OS_WIN32
548 if (interface_prefs.use_native_windows_dialogs)
550 GeanyDocument *doc = document_get_current();
551 gchar *utf8_name = win32_show_document_save_as_dialog(GTK_WINDOW(main_widgets.window),
552 _("Save File"), DOC_FILENAME(doc));
553 if (utf8_name != NULL)
554 result = handle_save_as(utf8_name, FALSE, FALSE);
556 else
557 #endif
558 result = gtk_show_save_as();
559 return result;
563 #ifndef G_OS_WIN32
564 static void show_msgbox_dialog(GtkWidget *dialog, GtkMessageType type, GtkWindow *parent)
566 const gchar *title;
567 switch (type)
569 case GTK_MESSAGE_ERROR:
570 title = _("Error");
571 break;
572 case GTK_MESSAGE_QUESTION:
573 title = _("Question");
574 break;
575 case GTK_MESSAGE_WARNING:
576 title = _("Warning");
577 break;
578 default:
579 title = _("Information");
580 break;
582 gtk_window_set_title(GTK_WINDOW(dialog), title);
583 if (parent == NULL || GTK_IS_DIALOG(parent))
585 GdkPixbuf *pb = ui_new_pixbuf_from_inline(GEANY_IMAGE_LOGO);
586 gtk_window_set_icon(GTK_WINDOW(dialog), pb);
587 g_object_unref(pb);
589 gtk_widget_set_name(dialog, "GeanyDialog");
591 gtk_dialog_run(GTK_DIALOG(dialog));
592 gtk_widget_destroy(dialog);
594 #endif
598 * Shows a message box of the type @a type with @a text.
599 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
600 * message dialog box is shown.
602 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
603 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
604 * @param text Printf()-style format string.
605 * @param ... Arguments for the @a text format string.
607 void dialogs_show_msgbox(GtkMessageType type, const gchar *text, ...)
609 #ifndef G_OS_WIN32
610 GtkWidget *dialog;
611 #endif
612 gchar *string;
613 va_list args;
614 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
616 va_start(args, text);
617 string = g_strdup_vprintf(text, args);
618 va_end(args);
620 #ifdef G_OS_WIN32
621 win32_message_dialog(GTK_WIDGET(parent), type, string);
622 #else
623 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
624 type, GTK_BUTTONS_OK, "%s", string);
625 show_msgbox_dialog(dialog, type, parent);
626 #endif
627 g_free(string);
631 void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
633 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
634 #ifdef G_OS_WIN32
635 /* put the two strings together because Windows message boxes don't support secondary texts */
636 gchar *string = g_strconcat(text, "\n", secondary, NULL);
637 win32_message_dialog(GTK_WIDGET(parent), type, string);
638 g_free(string);
639 #else
640 GtkWidget *dialog;
641 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
642 type, GTK_BUTTONS_OK, "%s", text);
643 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
644 show_msgbox_dialog(dialog, type, parent);
645 #endif
649 #ifndef G_OS_WIN32
650 static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
652 GtkWidget *dialog, *button;
653 gint ret;
655 dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
656 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
657 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg2);
658 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
660 button = ui_button_new_with_image(GTK_STOCK_CLEAR, _("_Don't save"));
661 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
662 gtk_widget_show(button);
664 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
666 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
667 ret = gtk_dialog_run(GTK_DIALOG(dialog));
669 gtk_widget_destroy(dialog);
671 return ret;
673 #endif
676 gboolean dialogs_show_unsaved_file(GeanyDocument *doc)
678 gchar *msg, *short_fn = NULL;
679 const gchar *msg2;
680 gint ret;
681 gboolean old_quitting_state = main_status.quitting;
683 /* display the file tab to remind the user of the document */
684 main_status.quitting = FALSE;
685 gtk_notebook_set_current_page(GTK_NOTEBOOK(main_widgets.notebook),
686 document_get_notebook_page(doc));
687 main_status.quitting = old_quitting_state;
689 short_fn = document_get_basename_for_display(doc, -1);
691 msg = g_strdup_printf(_("The file '%s' is not saved."),
692 (short_fn != NULL) ? short_fn : GEANY_STRING_UNTITLED);
693 msg2 = _("Do you want to save it before closing?");
694 g_free(short_fn);
696 #ifdef G_OS_WIN32
697 setptr(msg, g_strconcat(msg, "\n", msg2, NULL));
698 ret = win32_message_dialog_unsaved(msg);
699 #else
700 ret = run_unsaved_dialog(msg, msg2);
701 #endif
702 g_free(msg);
704 switch (ret)
706 case GTK_RESPONSE_YES:
708 if (document_need_save_as(doc))
710 ret = dialogs_show_save_as();
712 else
713 /* document_save_file() returns the status if the file could be saved */
714 ret = document_save_file(doc, FALSE);
715 break;
717 case GTK_RESPONSE_NO: ret = TRUE; break;
718 case GTK_RESPONSE_CANCEL: /* fall through to default and leave the function */
719 default: ret = FALSE; break;
722 return (gboolean) ret;
726 #ifndef G_OS_WIN32
727 static void
728 on_font_apply_button_clicked (GtkButton *button,
729 gpointer user_data)
731 gchar *fontname;
733 fontname = gtk_font_selection_dialog_get_font_name(
734 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel));
735 ui_set_editor_font(fontname);
736 g_free(fontname);
740 static void
741 on_font_ok_button_clicked (GtkButton *button,
742 gpointer user_data)
744 /* We do the same thing as apply, but we close the dialog after. */
745 on_font_apply_button_clicked(button, NULL);
746 gtk_widget_hide(ui_widgets.open_fontsel);
750 static void
751 on_font_cancel_button_clicked (GtkButton *button,
752 gpointer user_data)
754 gtk_widget_hide(ui_widgets.open_fontsel);
756 #endif
759 /* This shows the font selection dialog to choose a font. */
760 void dialogs_show_open_font()
762 #ifdef G_OS_WIN32
763 win32_show_font_dialog();
764 #else
766 if (ui_widgets.open_fontsel == NULL)
768 ui_widgets.open_fontsel = gtk_font_selection_dialog_new(_("Choose font"));;
769 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets.open_fontsel), 4);
770 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
771 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
772 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
773 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_fontsel), GDK_WINDOW_TYPE_HINT_DIALOG);
774 gtk_widget_set_name(ui_widgets.open_fontsel, "GeanyDialog");
776 gtk_widget_show(GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->apply_button);
778 g_signal_connect(ui_widgets.open_fontsel,
779 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
780 g_signal_connect(GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->ok_button,
781 "clicked", G_CALLBACK(on_font_ok_button_clicked), NULL);
782 g_signal_connect(GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->cancel_button,
783 "clicked", G_CALLBACK(on_font_cancel_button_clicked), NULL);
784 g_signal_connect(GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel)->apply_button,
785 "clicked", G_CALLBACK(on_font_apply_button_clicked), NULL);
787 gtk_font_selection_dialog_set_font_name(
788 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel), interface_prefs.editor_font);
789 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_fontsel), GTK_WINDOW(main_widgets.window));
791 /* We make sure the dialog is visible. */
792 gtk_window_present(GTK_WINDOW(ui_widgets.open_fontsel));
793 #endif
797 static void
798 on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
800 gtk_widget_grab_focus(entry);
804 static void
805 on_input_entry_activate(GtkEntry *entry, GtkDialog *dialog)
807 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
811 static void
812 on_input_numeric_activate(GtkEntry *entry, GtkDialog *dialog)
814 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
818 static void
819 on_input_dialog_response(GtkDialog *dialog,
820 gint response,
821 GtkWidget *entry)
823 gboolean persistent = (gboolean) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dialog), "has_combo"));
825 if (response == GTK_RESPONSE_ACCEPT)
827 const gchar *str = gtk_entry_get_text(GTK_ENTRY(entry));
828 GeanyInputCallback input_cb =
829 (GeanyInputCallback) g_object_get_data(G_OBJECT(dialog), "input_cb");
831 if (persistent)
833 GtkWidget *combo = (GtkWidget *) g_object_get_data(G_OBJECT(dialog), "combo");
834 ui_combo_box_add_to_history(GTK_COMBO_BOX(combo), str);
836 input_cb(str);
838 gtk_widget_hide(GTK_WIDGET(dialog));
842 static void add_input_widgets(GtkWidget *dialog, GtkWidget *vbox,
843 const gchar *label_text, const gchar *default_text, gboolean persistent,
844 GCallback insert_text_cb)
846 GtkWidget *entry;
848 if (label_text)
850 GtkWidget *label = gtk_label_new(label_text);
851 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
852 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
853 gtk_container_add(GTK_CONTAINER(vbox), label);
856 if (persistent) /* remember previous entry text in a combo box */
858 GtkWidget *combo = gtk_combo_box_entry_new_text();
860 entry = GTK_BIN(combo)->child;
861 ui_entry_add_clear_icon(GTK_ENTRY(entry));
862 g_object_set_data(G_OBJECT(dialog), "combo", combo);
863 gtk_container_add(GTK_CONTAINER(vbox), combo);
865 else
867 entry = gtk_entry_new();
868 ui_entry_add_clear_icon(GTK_ENTRY(entry));
869 gtk_container_add(GTK_CONTAINER(vbox), entry);
872 if (default_text != NULL)
874 gtk_entry_set_text(GTK_ENTRY(entry), default_text);
876 gtk_entry_set_max_length(GTK_ENTRY(entry), 255);
877 gtk_entry_set_width_chars(GTK_ENTRY(entry), 30);
879 if (insert_text_cb != NULL)
880 g_signal_connect(entry, "insert-text", insert_text_cb, NULL);
881 g_signal_connect(entry, "activate", G_CALLBACK(on_input_entry_activate), dialog);
882 g_signal_connect(dialog, "show", G_CALLBACK(on_input_dialog_show), entry);
883 g_signal_connect(dialog, "response", G_CALLBACK(on_input_dialog_response), entry);
887 /* Create and display an input dialog.
888 * persistent: whether to remember previous entry text in a combo box;
889 * in this case the dialog returned is not destroyed on a response,
890 * and can be reshown.
891 * Returns: the dialog widget. */
892 static GtkWidget *
893 dialogs_show_input_full(const gchar *title, const gchar *label_text, const gchar *default_text,
894 gboolean persistent, GeanyInputCallback input_cb, GCallback insert_text_cb)
896 GtkWidget *dialog, *vbox;
898 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
899 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
900 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
901 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
902 gtk_widget_set_name(dialog, "GeanyDialog");
903 gtk_box_set_spacing(GTK_BOX(vbox), 6);
905 g_object_set_data(G_OBJECT(dialog), "has_combo", GINT_TO_POINTER(persistent));
906 g_object_set_data(G_OBJECT(dialog), "input_cb", (gpointer) input_cb);
908 add_input_widgets(dialog, vbox, label_text, default_text, persistent, insert_text_cb);
910 if (persistent)
912 /* override default handler */
913 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
914 gtk_widget_show_all(dialog);
915 return dialog;
917 gtk_widget_show_all(dialog);
918 gtk_dialog_run(GTK_DIALOG(dialog));
919 gtk_widget_destroy(dialog);
920 return NULL;
924 /* Remember previous entry text in a combo box.
925 * Returns: the dialog widget. */
926 GtkWidget *
927 dialogs_show_input_persistent(const gchar *title, const gchar *label_text, const gchar *default_text,
928 GeanyInputCallback input_cb)
930 return dialogs_show_input_full(title, label_text, default_text, TRUE, input_cb, NULL);
934 /* ugly hack - user_data not supported for callback */
935 static gchar *dialog_input = NULL;
937 static void on_dialog_input(const gchar *str)
939 dialog_input = g_strdup(str);
943 /* Returns: newly allocated string - a copy of either the entry text or default_text. */
944 gchar *dialogs_show_input(const gchar *title, const gchar *label_text,
945 const gchar *default_text)
947 dialog_input = NULL;
948 dialogs_show_input_full(title, label_text, default_text, FALSE, on_dialog_input, NULL);
949 return NVL(dialog_input, g_strdup(default_text));
953 /* Returns: newly allocated copy of the entry text or NULL on cancel.
954 * Specialised variant for Goto Line dialog. */
955 gchar *dialogs_show_input_goto_line(const gchar *title, const gchar *label_text,
956 const gchar *default_text)
958 dialog_input = NULL;
959 dialogs_show_input_full(
960 title, label_text, default_text, FALSE, on_dialog_input,
961 G_CALLBACK(ui_editable_insert_text_callback));
962 return dialog_input;
967 * Shows an input box to enter a numerical value using a GtkSpinButton.
968 * If the dialog is aborted, @a value remains untouched.
970 * @param title The dialog title.
971 * @param label_text The shown dialog label.
972 * @param value The default value for the spin button and the return location of the entered value.
973 * Must be non-NULL.
974 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
975 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
976 * @param step Increment added or subtracted by spinning the widget
977 * (see documentation for @c gtk_spin_button_new_with_range()).
979 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
981 * @since 0.16
983 gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text,
984 gdouble *value, gdouble min, gdouble max, gdouble step)
986 GtkWidget *dialog, *label, *spin, *vbox;
987 gboolean res = FALSE;
989 g_return_val_if_fail(title != NULL, FALSE);
990 g_return_val_if_fail(label_text != NULL, FALSE);
991 g_return_val_if_fail(value != NULL, FALSE);
993 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
994 GTK_DIALOG_DESTROY_WITH_PARENT,
995 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
996 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
997 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
998 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
999 gtk_widget_set_name(dialog, "GeanyDialog");
1001 label = gtk_label_new(label_text);
1002 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1004 spin = gtk_spin_button_new_with_range(min, max, step);
1005 ui_entry_add_clear_icon(GTK_ENTRY(spin));
1006 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), *value);
1007 g_signal_connect(spin, "activate", G_CALLBACK(on_input_numeric_activate), dialog);
1009 gtk_container_add(GTK_CONTAINER(vbox), label);
1010 gtk_container_add(GTK_CONTAINER(vbox), spin);
1011 gtk_widget_show_all(vbox);
1013 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1015 *value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1016 res = TRUE;
1018 gtk_widget_destroy(dialog);
1020 return res;
1024 void dialogs_show_file_properties(GeanyDocument *doc)
1026 GtkWidget *dialog, *label, *table, *hbox, *image, *perm_table, *check, *vbox;
1027 gchar *file_size, *title, *base_name, *time_changed, *time_modified, *time_accessed, *enctext;
1028 gchar *short_name;
1029 #ifdef HAVE_SYS_TYPES_H
1030 struct stat st;
1031 off_t filesize;
1032 mode_t mode;
1033 gchar *locale_filename;
1034 #else
1035 gint filesize = 0;
1036 gint mode = 0;
1037 #endif
1039 /* define this ones, to avoid later trouble */
1040 #ifndef S_IRUSR
1041 # define S_IRUSR 0
1042 # define S_IWUSR 0
1043 # define S_IXUSR 0
1044 #endif
1045 #ifndef S_IRGRP
1046 # define S_IRGRP 0
1047 # define S_IWGRP 0
1048 # define S_IXGRP 0
1049 # define S_IROTH 0
1050 # define S_IWOTH 0
1051 # define S_IXOTH 0
1052 #endif
1054 if (doc == NULL || doc->file_name == NULL)
1056 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1057 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1058 return;
1062 #ifdef HAVE_SYS_TYPES_H
1063 locale_filename = utils_get_locale_from_utf8(doc->file_name);
1064 if (g_stat(locale_filename, &st) == 0)
1066 /* first copy the returned string and the trim it, to not modify the static glibc string
1067 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1068 time_changed = g_strchomp(g_strdup(ctime(&st.st_ctime)));
1069 time_modified = g_strchomp(g_strdup(ctime(&st.st_mtime)));
1070 time_accessed = g_strchomp(g_strdup(ctime(&st.st_atime)));
1071 filesize = st.st_size;
1072 mode = st.st_mode;
1074 else
1076 time_changed = g_strdup(_("unknown"));
1077 time_modified = g_strdup(_("unknown"));
1078 time_accessed = g_strdup(_("unknown"));
1079 filesize = (off_t) 0;
1080 mode = (mode_t) 0;
1082 g_free(locale_filename);
1083 #else
1084 time_changed = g_strdup(_("unknown"));
1085 time_modified = g_strdup(_("unknown"));
1086 time_accessed = g_strdup(_("unknown"));
1087 #endif
1089 base_name = g_path_get_basename(doc->file_name);
1090 short_name = utils_str_middle_truncate(base_name, 30);
1091 title = g_strconcat(short_name, " ", _("Properties"), NULL);
1092 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
1093 GTK_DIALOG_DESTROY_WITH_PARENT,
1094 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, NULL);
1095 g_free(short_name);
1096 g_free(title);
1097 gtk_widget_set_name(dialog, "GeanyDialog");
1098 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1100 g_signal_connect(dialog, "response", G_CALLBACK(gtk_widget_destroy), NULL);
1101 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_destroy), NULL);
1103 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
1105 label = ui_label_new_bold(base_name);
1106 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1107 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1108 image = gtk_image_new_from_stock("gtk-file", GTK_ICON_SIZE_BUTTON);
1109 gtk_misc_set_alignment(GTK_MISC(image), 1.0, 0.5);
1110 hbox = gtk_hbox_new(FALSE, 6);
1111 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1112 gtk_container_add(GTK_CONTAINER(hbox), image);
1113 gtk_container_add(GTK_CONTAINER(hbox), label);
1114 gtk_container_add(GTK_CONTAINER(vbox), hbox);
1116 table = gtk_table_new(8, 2, FALSE);
1117 gtk_table_set_row_spacings(GTK_TABLE(table), 10);
1118 gtk_table_set_col_spacings(GTK_TABLE(table), 10);
1120 label = gtk_label_new(_("<b>Type:</b>"));
1121 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,
1122 (GtkAttachOptions) (GTK_FILL),
1123 (GtkAttachOptions) (0), 0, 0);
1124 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1125 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1127 label = gtk_label_new(doc->file_type->title);
1128 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1129 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1,
1130 (GtkAttachOptions) (GTK_FILL),
1131 (GtkAttachOptions) (0), 0, 0);
1132 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1134 label = gtk_label_new(_("<b>Size:</b>"));
1135 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
1136 (GtkAttachOptions) (GTK_FILL),
1137 (GtkAttachOptions) (0), 0, 0);
1138 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1139 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1141 file_size = utils_make_human_readable_str(filesize, 1, 0);
1142 label = gtk_label_new(file_size);
1143 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1144 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 1, 2,
1145 (GtkAttachOptions) (GTK_FILL),
1146 (GtkAttachOptions) (0), 0, 0);
1147 g_free(file_size);
1148 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1150 label = gtk_label_new(_("<b>Location:</b>"));
1151 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3,
1152 (GtkAttachOptions) (GTK_FILL),
1153 (GtkAttachOptions) (0), 0, 0);
1154 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1155 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1157 label = gtk_label_new(doc->file_name);
1158 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1159 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 2, 3,
1160 (GtkAttachOptions) (GTK_FILL),
1161 (GtkAttachOptions) (0), 0, 0);
1162 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0);
1164 label = gtk_label_new(_("<b>Read-only:</b>"));
1165 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4,
1166 (GtkAttachOptions) (GTK_FILL),
1167 (GtkAttachOptions) (0), 0, 0);
1168 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1169 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1171 check = gtk_check_button_new_with_label(_("(only inside Geany)"));
1172 gtk_widget_set_sensitive(check, FALSE);
1173 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1174 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), doc->readonly);
1175 gtk_table_attach(GTK_TABLE(table), check, 1, 2, 3, 4,
1176 (GtkAttachOptions) (GTK_FILL),
1177 (GtkAttachOptions) (0), 0, 0);
1178 gtk_button_set_alignment(GTK_BUTTON(check), 0.0, 0);
1180 label = gtk_label_new(_("<b>Encoding:</b>"));
1181 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5,
1182 (GtkAttachOptions) (GTK_FILL),
1183 (GtkAttachOptions) (0), 0, 0);
1184 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1185 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1187 enctext = g_strdup_printf("%s %s",
1188 doc->encoding,
1189 (encodings_is_unicode_charset(doc->encoding)) ?
1190 ((doc->has_bom) ? _("(with BOM)") : _("(without BOM)")) : "");
1192 label = gtk_label_new(enctext);
1193 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1194 g_free(enctext);
1196 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 4, 5,
1197 (GtkAttachOptions) (GTK_FILL),
1198 (GtkAttachOptions) (0), 0, 0);
1199 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0);
1201 label = gtk_label_new(_("<b>Modified:</b>"));
1202 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6,
1203 (GtkAttachOptions) (GTK_FILL),
1204 (GtkAttachOptions) (0), 0, 0);
1205 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1206 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1208 label = gtk_label_new(time_modified);
1209 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1210 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 5, 6,
1211 (GtkAttachOptions) (GTK_FILL),
1212 (GtkAttachOptions) (0), 0, 0);
1213 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1215 label = gtk_label_new(_("<b>Changed:</b>"));
1216 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 6, 7,
1217 (GtkAttachOptions) (GTK_FILL),
1218 (GtkAttachOptions) (0), 0, 0);
1219 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1220 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1222 label = gtk_label_new(time_changed);
1223 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1224 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 6, 7,
1225 (GtkAttachOptions) (GTK_FILL),
1226 (GtkAttachOptions) (0), 0, 0);
1227 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1229 label = gtk_label_new(_("<b>Accessed:</b>"));
1230 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 7, 8,
1231 (GtkAttachOptions) (GTK_FILL),
1232 (GtkAttachOptions) (0), 0, 0);
1233 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1234 gtk_misc_set_alignment(GTK_MISC(label), 1, 0);
1236 label = gtk_label_new(time_accessed);
1237 gtk_label_set_selectable(GTK_LABEL(label), TRUE);
1238 gtk_table_attach(GTK_TABLE(table), label, 1, 2, 7, 8,
1239 (GtkAttachOptions) (GTK_FILL),
1240 (GtkAttachOptions) (0), 0, 0);
1241 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
1243 /* add table */
1244 gtk_container_add(GTK_CONTAINER(vbox), table);
1246 /* create table with the permissions */
1247 perm_table = gtk_table_new(5, 4, TRUE);
1248 gtk_table_set_row_spacings(GTK_TABLE(perm_table), 5);
1249 gtk_table_set_col_spacings(GTK_TABLE(perm_table), 5);
1251 label = gtk_label_new(_("<b>Permissions:</b>"));
1252 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1253 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1254 gtk_table_attach(GTK_TABLE(perm_table), label, 0, 4, 0, 1,
1255 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1256 (GtkAttachOptions) (0), 0, 0);
1258 /* Header */
1259 label = gtk_label_new(_("Read:"));
1260 gtk_table_attach(GTK_TABLE(perm_table), label, 1, 2, 1, 2,
1261 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1262 (GtkAttachOptions) (0), 0, 0);
1263 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1264 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0);
1266 label = gtk_label_new(_("Write:"));
1267 gtk_table_attach(GTK_TABLE(perm_table), label, 2, 3, 1, 2,
1268 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1269 (GtkAttachOptions) (0), 0, 0);
1270 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1271 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0);
1273 label = gtk_label_new(_("Execute:"));
1274 gtk_table_attach(GTK_TABLE(perm_table), label, 3, 4, 1, 2,
1275 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1276 (GtkAttachOptions) (0), 0, 0);
1277 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1278 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0);
1280 /* Owner */
1281 label = gtk_label_new(_("Owner:"));
1282 gtk_table_attach(GTK_TABLE(perm_table), label, 0, 1, 2, 3,
1283 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1284 (GtkAttachOptions) (0), 0, 0);
1285 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1286 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0);
1288 check = gtk_check_button_new();
1289 gtk_widget_set_sensitive(check, FALSE);
1290 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1291 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRUSR);
1292 gtk_table_attach(GTK_TABLE(perm_table), check, 1, 2, 2, 3,
1293 (GtkAttachOptions) (GTK_EXPAND | GTK_EXPAND | GTK_FILL),
1294 (GtkAttachOptions) (0), 0, 0);
1295 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1297 check = gtk_check_button_new();
1298 gtk_widget_set_sensitive(check, FALSE);
1299 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1300 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWUSR);
1301 gtk_table_attach(GTK_TABLE(perm_table), check, 2, 3, 2, 3,
1302 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1303 (GtkAttachOptions) (0), 0, 0);
1304 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1306 check = gtk_check_button_new();
1307 gtk_widget_set_sensitive(check, FALSE);
1308 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1309 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXUSR);
1310 gtk_table_attach(GTK_TABLE(perm_table), check, 3, 4, 2, 3,
1311 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1312 (GtkAttachOptions) (0), 0, 0);
1313 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1316 /* Group */
1317 label = gtk_label_new(_("Group:"));
1318 gtk_table_attach(GTK_TABLE(perm_table), label, 0, 1, 3, 4,
1319 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1320 (GtkAttachOptions) (0), 0, 0);
1321 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1322 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0);
1324 check = gtk_check_button_new();
1325 gtk_widget_set_sensitive(check, FALSE);
1326 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1327 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRGRP);
1328 gtk_table_attach(GTK_TABLE(perm_table), check, 1, 2, 3, 4,
1329 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1330 (GtkAttachOptions) (0), 0, 0);
1331 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1333 check = gtk_check_button_new();
1334 gtk_widget_set_sensitive(check, FALSE);
1335 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1336 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWGRP);
1337 gtk_table_attach(GTK_TABLE(perm_table), check, 2, 3, 3, 4,
1338 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1339 (GtkAttachOptions) (0), 0, 0);
1340 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1342 check = gtk_check_button_new();
1343 gtk_widget_set_sensitive(check, FALSE);
1344 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1345 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXGRP);
1346 gtk_table_attach(GTK_TABLE(perm_table), check, 3, 4, 3, 4,
1347 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1348 (GtkAttachOptions) (0), 0, 0);
1349 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1352 /* Other */
1353 label = gtk_label_new(_("Other:"));
1354 gtk_table_attach(GTK_TABLE(perm_table), label, 0, 1, 4, 5,
1355 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1356 (GtkAttachOptions) (0), 0, 0);
1357 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1358 gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0);
1360 check = gtk_check_button_new();
1361 gtk_widget_set_sensitive(check, FALSE);
1362 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1363 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IROTH);
1364 gtk_table_attach(GTK_TABLE(perm_table), check, 1, 2, 4, 5,
1365 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1366 (GtkAttachOptions) (0), 0, 0);
1367 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1369 check = gtk_check_button_new();
1370 gtk_widget_set_sensitive(check, FALSE);
1371 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1372 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWOTH);
1373 gtk_table_attach(GTK_TABLE(perm_table), check, 2, 3, 4, 5,
1374 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1375 (GtkAttachOptions) (0), 0, 0);
1376 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1378 check = gtk_check_button_new();
1379 gtk_widget_set_sensitive(check, FALSE);
1380 gtk_button_set_focus_on_click(GTK_BUTTON(check), FALSE);
1381 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXOTH);
1382 gtk_table_attach(GTK_TABLE(perm_table), check, 3, 4, 4, 5,
1383 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1384 (GtkAttachOptions) (0), 0, 0);
1385 gtk_button_set_alignment(GTK_BUTTON(check), 0.5, 0);
1387 gtk_container_add(GTK_CONTAINER(vbox), perm_table);
1389 g_free(base_name);
1390 g_free(time_changed);
1391 g_free(time_modified);
1392 g_free(time_accessed);
1394 gtk_widget_show_all(dialog);
1398 /* extra_text can be NULL; otherwise it is displayed below main_text.
1399 * if parent is NULL, main_widgets.window will be used
1400 * btn_1, btn_2, btn_3 can be NULL.
1401 * returns response_1, response_2 or response_3 */
1402 static gint show_prompt(GtkWidget *parent,
1403 const gchar *btn_1, GtkResponseType response_1,
1404 const gchar *btn_2, GtkResponseType response_2,
1405 const gchar *btn_3, GtkResponseType response_3,
1406 const gchar *question_text, const gchar *extra_text)
1408 gboolean ret = FALSE;
1409 GtkWidget *dialog;
1410 GtkWidget *btn;
1412 if (btn_2 == NULL)
1414 btn_2 = GTK_STOCK_NO;
1415 response_2 = GTK_RESPONSE_NO;
1417 if (btn_3 == NULL)
1419 btn_3 = GTK_STOCK_YES;
1420 response_3 = GTK_RESPONSE_YES;
1423 #ifdef G_OS_WIN32
1424 /* our native dialog code doesn't support custom buttons */
1425 if (btn_3 == GTK_STOCK_YES && btn_2 == GTK_STOCK_NO && btn_1 == NULL)
1427 gchar *string = (extra_text == NULL) ? g_strdup(question_text) :
1428 g_strconcat(question_text, "\n\n", extra_text, NULL);
1430 ret = win32_message_dialog(parent, GTK_MESSAGE_QUESTION, string);
1431 ret = ret ? response_3 : response_2;
1432 g_free(string);
1433 return ret;
1435 #endif
1436 if (parent == NULL && main_status.main_window_realized)
1437 parent = main_widgets.window;
1439 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
1440 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1441 GTK_BUTTONS_NONE, "%s", question_text);
1442 gtk_widget_set_name(dialog, "GeanyDialog");
1443 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1444 if (parent == NULL || GTK_IS_DIALOG(parent))
1446 GdkPixbuf *pb = ui_new_pixbuf_from_inline(GEANY_IMAGE_LOGO);
1447 gtk_window_set_icon(GTK_WINDOW(dialog), pb);
1448 g_object_unref(pb);
1451 /* question_text will be in bold if optional extra_text used */
1452 if (extra_text != NULL)
1453 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1454 "%s", extra_text);
1456 if (btn_1 != NULL)
1457 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_1, response_1);
1459 /* For a cancel button, use cancel response so user can press escape to cancel */
1460 btn = gtk_dialog_add_button(GTK_DIALOG(dialog), btn_2,
1461 utils_str_equal(btn_2, GTK_STOCK_CANCEL) ? GTK_RESPONSE_CANCEL : response_2);
1462 /* we don't want a default, but we need to override the apply button as default */
1463 gtk_widget_grab_default(btn);
1464 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_3, response_3);
1466 ret = gtk_dialog_run(GTK_DIALOG(dialog));
1467 gtk_widget_destroy(dialog);
1469 if (ret == GTK_RESPONSE_CANCEL)
1470 ret = response_2;
1471 return ret;
1476 * Shows a question message box with @a text and Yes/No buttons.
1477 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1478 * message dialog box is shown.
1480 * @param text Printf()-style format string.
1481 * @param ... Arguments for the @a text format string.
1483 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1485 gboolean dialogs_show_question(const gchar *text, ...)
1487 gchar *string;
1488 va_list args;
1489 GtkWidget *parent = (main_status.main_window_realized) ? main_widgets.window : NULL;
1490 gint result;
1492 va_start(args, text);
1493 string = g_strdup_vprintf(text, args);
1494 va_end(args);
1495 result = show_prompt(parent,
1496 NULL, GTK_RESPONSE_NONE,
1497 GTK_STOCK_NO, GTK_RESPONSE_NO,
1498 GTK_STOCK_YES, GTK_RESPONSE_YES,
1499 string, NULL);
1500 g_free(string);
1501 return (result == GTK_RESPONSE_YES);
1505 /* extra_text can be NULL; otherwise it is displayed below main_text.
1506 * if parent is NULL, main_widgets.window will be used
1507 * yes_btn, no_btn can be NULL. */
1508 gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn,
1509 const gchar *extra_text, const gchar *main_text, ...)
1511 gint result;
1512 gchar *string;
1513 va_list args;
1515 va_start(args, main_text);
1516 string = g_strdup_vprintf(main_text, args);
1517 va_end(args);
1518 result = show_prompt(parent,
1519 NULL, GTK_RESPONSE_NONE,
1520 no_btn, GTK_RESPONSE_NO,
1521 yes_btn, GTK_RESPONSE_YES,
1522 string, extra_text);
1523 g_free(string);
1524 return (result == GTK_RESPONSE_YES);
1528 /* extra_text can be NULL; otherwise it is displayed below main_text.
1529 * if parent is NULL, main_widgets.window will be used
1530 * btn_1, btn_2, btn_3 can be NULL.
1531 * returns response_1, response_2 or response_3 */
1532 gint dialogs_show_prompt(GtkWidget *parent,
1533 const gchar *btn_1, GtkResponseType response_1,
1534 const gchar *btn_2, GtkResponseType response_2,
1535 const gchar *btn_3, GtkResponseType response_3,
1536 const gchar *extra_text, const gchar *main_text, ...)
1538 gchar *string;
1539 va_list args;
1540 gint result;
1542 va_start(args, main_text);
1543 string = g_strdup_vprintf(main_text, args);
1544 va_end(args);
1545 result = show_prompt(parent, btn_1, response_1, btn_2, response_2, btn_3, response_3,
1546 string, extra_text);
1547 g_free(string);
1548 return result;