geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

dialogs.c
Go to the documentation of this file.
1/*
2 * dialogs.c - this file is part of Geany, a fast and lightweight IDE
3 *
4 * Copyright 2005 The Geany contributors
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21/*
22 * File related dialogs, miscellaneous dialogs, font dialog.
23 */
24
25#ifdef HAVE_CONFIG_H
26# include "config.h"
27#endif
28
29#include "dialogs.h"
30
31#include "app.h"
32#include "build.h"
33#include "document.h"
34#include "encodings.h"
35#include "encodingsprivate.h"
36#include "filetypes.h"
37#include "main.h"
38#include "support.h"
39#include "utils.h"
40#include "ui_utils.h"
41#include "win32.h"
42
43#include <gtk/gtk.h>
44#include <gdk/gdkkeysyms.h>
45#include <string.h>
46
47#ifdef HAVE_SYS_TIME_H
48# include <sys/time.h>
49#endif
50#include <time.h>
51
52#ifdef HAVE_SYS_TYPES_H
53# include <sys/types.h>
54#endif
55
56/* gstdio.h also includes sys/stat.h */
57#include <glib/gstdio.h>
58
59
60enum
61{
64};
65
66
67static struct FileSelState
68{
69 struct
70 {
74 gboolean show_hidden;
77}
79 {
80 0,
81 GEANY_ENCODINGS_MAX, /* default encoding is detect from file */
82 -1, /* default filetype is detect from extension */
83 FALSE,
84 FALSE
85 }
86};
87
88
89static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo);
90
91
92/* gets the ID of the current file filter */
93static guint file_chooser_get_filter_idx(GtkFileChooser *chooser)
94{
95 guint idx = 0;
96 GtkFileFilter *current;
97 GSList *filters, *item;
98
99 current = gtk_file_chooser_get_filter(chooser);
100 filters = gtk_file_chooser_list_filters(chooser);
101 foreach_slist(item, filters)
102 {
103 if (item->data == current)
104 break;
105 idx ++;
106 }
107 g_slist_free(filters);
108 return idx;
109}
110
111
112/* sets the current file filter from its ID */
113static void file_chooser_set_filter_idx(GtkFileChooser *chooser, guint idx)
114{
115 GtkFileFilter *current;
116 GSList *filters;
117
118 filters = gtk_file_chooser_list_filters(chooser);
119 current = g_slist_nth_data(filters, idx);
120 g_slist_free(filters);
121 gtk_file_chooser_set_filter(chooser, current);
122}
123
124
125static gboolean open_file_dialog_handle_response(GtkWidget *dialog, gint response)
126{
127 gboolean ret = TRUE;
128
129 if (response == GTK_RESPONSE_ACCEPT || response == GEANY_RESPONSE_VIEW)
130 {
131 GSList *filelist;
132 GeanyFiletype *ft = NULL;
133 const gchar *charset = NULL;
134 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
135 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
136 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
137 gboolean ro = (response == GEANY_RESPONSE_VIEW); /* View clicked */
138
139 filesel_state.open.more_options_visible = gtk_expander_get_expanded(GTK_EXPANDER(expander));
142
143 /* ignore detect from file item */
146
150
151 filelist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
152 if (filelist != NULL)
153 {
154 const gchar *first = filelist->data;
155
156 // When there's only one filename it may have been typed manually
157 if (!filelist->next && !g_file_test(first, G_FILE_TEST_EXISTS))
158 {
159 dialogs_show_msgbox(GTK_MESSAGE_ERROR, _("\"%s\" was not found."), first);
160 ret = FALSE;
161 }
162 else
163 {
164 document_open_files(filelist, ro, ft, charset);
165 }
166 g_slist_foreach(filelist, (GFunc) g_free, NULL); /* free filenames */
167 }
168 g_slist_free(filelist);
169 }
170 if (app->project && !EMPTY(app->project->base_path))
171 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
173 return ret;
174}
175
176
177static void on_file_open_show_hidden_notify(GObject *filechooser,
178 GParamSpec *pspec, gpointer data)
179{
180 GtkWidget *toggle_button;
181
182 toggle_button = ui_lookup_widget(GTK_WIDGET(filechooser), "check_hidden");
183
184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_button),
185 gtk_file_chooser_get_show_hidden(GTK_FILE_CHOOSER(filechooser)));
186}
187
188
189static void
190on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, GtkWidget *dialog)
191{
192 filesel_state.open.show_hidden = gtk_toggle_button_get_active(togglebutton);
193 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(dialog), filesel_state.open.show_hidden);
194}
195
196
197static void filetype_combo_cell_data_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell,
198 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
199{
200 gboolean sensitive = !gtk_tree_model_iter_has_child(tree_model, iter);
201 gchar *text;
202
203 gtk_tree_model_get(tree_model, iter, 1, &text, -1);
204 g_object_set(cell, "sensitive", sensitive, "text", text, NULL);
205 g_free(text);
206}
207
208
209static GtkWidget *create_filetype_combo_box(void)
210{
211 GtkTreeStore *store;
212 GtkTreeIter iter_compiled, iter_script, iter_markup, iter_misc, iter_detect;
213 GtkTreeIter *iter_parent;
214 GtkWidget *combo;
215 GtkCellRenderer *renderer;
216 GSList *node;
217
218 store = gtk_tree_store_new(2, G_TYPE_INT, G_TYPE_STRING);
219
220 gtk_tree_store_insert_with_values(store, &iter_detect, NULL, -1,
221 0, -1 /* auto-detect */, 1, _("Detect from file"), -1);
222
223 gtk_tree_store_insert_with_values(store, &iter_compiled, NULL, -1,
224 0, -1, 1, _("Programming Languages"), -1);
225 gtk_tree_store_insert_with_values(store, &iter_script, NULL, -1,
226 0, -1, 1, _("Scripting Languages"), -1);
227 gtk_tree_store_insert_with_values(store, &iter_markup, NULL, -1,
228 0, -1, 1, _("Markup Languages"), -1);
229 gtk_tree_store_insert_with_values(store, &iter_misc, NULL, -1,
230 0, -1, 1, _("Miscellaneous"), -1);
231
233 {
234 GeanyFiletype *ft = node->data;
235
236 switch (ft->group)
237 {
238 case GEANY_FILETYPE_GROUP_COMPILED: iter_parent = &iter_compiled; break;
239 case GEANY_FILETYPE_GROUP_SCRIPT: iter_parent = &iter_script; break;
240 case GEANY_FILETYPE_GROUP_MARKUP: iter_parent = &iter_markup; break;
241 case GEANY_FILETYPE_GROUP_MISC: iter_parent = &iter_misc; break;
243 default: iter_parent = NULL;
244 }
245 gtk_tree_store_insert_with_values(store, NULL, iter_parent, -1,
246 0, (gint) ft->id, 1, ft->title, -1);
247 }
248
249 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
250 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter_detect);
251 renderer = gtk_cell_renderer_text_new();
252 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE);
253 gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo), renderer,
255
256 g_object_unref(store);
257
258 return combo;
259}
260
261
262/* the filetype, or -1 for auto-detect */
263static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo)
264{
265 gint id = -1;
266 GtkTreeIter iter;
267
268 if (gtk_combo_box_get_active_iter(combo, &iter))
269 {
270 GtkTreeModel *model = gtk_combo_box_get_model(combo);
271 gtk_tree_model_get(model, &iter, 0, &id, -1);
272 }
273 return id;
274}
275
276
277static gboolean filetype_combo_box_set_active_filetype(GtkComboBox *combo, const gint id)
278{
279 GtkTreeModel *model = gtk_combo_box_get_model(combo);
280 GtkTreeIter iter;
281
282 if (gtk_tree_model_get_iter_first(model, &iter))
283 {
284 do
285 {
286 gint row_id;
287 gtk_tree_model_get(model, &iter, 0, &row_id, -1);
288 if (id == row_id)
289 {
290 gtk_combo_box_set_active_iter(combo, &iter);
291 return TRUE;
292 }
293 }
294 while (ui_tree_model_iter_any_next(model, &iter, TRUE));
295 }
296 return FALSE;
297}
298
299
300static GtkWidget *add_file_open_extra_widget(GtkWidget *dialog)
301{
302 GtkWidget *expander, *vbox, *table, *check_hidden;
303 GtkWidget *filetype_ebox, *filetype_label, *filetype_combo;
304 GtkWidget *encoding_ebox, *encoding_label, *encoding_combo;
305
306 expander = gtk_expander_new_with_mnemonic(_("_More Options"));
307 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
308 gtk_container_add(GTK_CONTAINER(expander), vbox);
309
310 table = gtk_table_new(2, 4, FALSE);
311
312 /* line 1 with checkbox and encoding combo */
313 check_hidden = gtk_check_button_new_with_mnemonic(_("Show _hidden files"));
314 gtk_widget_show(check_hidden);
315 gtk_table_attach(GTK_TABLE(table), check_hidden, 0, 1, 0, 1,
316 (GtkAttachOptions) (GTK_FILL | GTK_EXPAND),
317 (GtkAttachOptions) (0), 0, 5);
318
319 /* spacing */
320 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""), 1, 2, 0, 1,
321 (GtkAttachOptions) (GTK_FILL),
322 (GtkAttachOptions) (0), 5, 5);
323
324 encoding_label = gtk_label_new(_("Set encoding:"));
325 gtk_misc_set_alignment(GTK_MISC(encoding_label), 1, 0);
326 gtk_table_attach(GTK_TABLE(table), encoding_label, 2, 3, 0, 1,
327 (GtkAttachOptions) (GTK_FILL),
328 (GtkAttachOptions) (0), 4, 5);
329 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
330 encoding_ebox = gtk_event_box_new();
332 gtk_widget_set_tooltip_text(encoding_ebox,
333 _("Explicitly defines an encoding for the file, if it would not be detected. This is useful when you know that the encoding of a file cannot be detected correctly by Geany.\nNote if you choose multiple files, they will all be opened with the chosen encoding."));
334 gtk_container_add(GTK_CONTAINER(encoding_ebox), encoding_combo);
335 gtk_table_attach(GTK_TABLE(table), encoding_ebox, 3, 4, 0, 1,
336 (GtkAttachOptions) (GTK_FILL),
337 (GtkAttachOptions) (0), 0, 5);
338
339 /* line 2 with filetype combo */
340 filetype_label = gtk_label_new(_("Set filetype:"));
341 gtk_misc_set_alignment(GTK_MISC(filetype_label), 1, 0);
342 gtk_table_attach(GTK_TABLE(table), filetype_label, 2, 3, 1, 2,
343 (GtkAttachOptions) (GTK_FILL),
344 (GtkAttachOptions) (0), 4, 5);
345 /* the ebox is for the tooltip, because gtk_combo_box can't show tooltips */
346 filetype_ebox = gtk_event_box_new();
347 filetype_combo = create_filetype_combo_box();
348 gtk_widget_set_tooltip_text(filetype_ebox,
349 _("Explicitly defines a filetype for the file, if it would not be detected by filename extension.\nNote if you choose multiple files, they will all be opened with the chosen filetype."));
350 gtk_container_add(GTK_CONTAINER(filetype_ebox), filetype_combo);
351 gtk_table_attach(GTK_TABLE(table), filetype_ebox, 3, 4, 1, 2,
352 (GtkAttachOptions) (GTK_FILL),
353 (GtkAttachOptions) (0), 0, 5);
354
355 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
357
358 g_signal_connect(check_hidden, "toggled", G_CALLBACK(on_file_open_check_hidden_toggled), dialog);
359
360 ui_hookup_widget(dialog, expander, "more_options_expander");
361 ui_hookup_widget(dialog, check_hidden, "check_hidden");
362 ui_hookup_widget(dialog, filetype_combo, "filetype_combo");
363 ui_hookup_widget(dialog, encoding_combo, "encoding_combo");
364
365 return expander;
366}
367
368
369static GtkWidget *create_open_file_dialog(void)
370{
371 GtkWidget *dialog;
372 GtkWidget *viewbtn;
373 GSList *node;
374
375 dialog = gtk_file_chooser_dialog_new(_("Open File"), GTK_WINDOW(main_widgets.window),
376 GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
377 gtk_widget_set_name(dialog, "GeanyDialog");
378
379 viewbtn = gtk_dialog_add_button(GTK_DIALOG(dialog), C_("Open dialog action", "_View"), GEANY_RESPONSE_VIEW);
380 gtk_widget_set_tooltip_text(viewbtn,
381 _("Opens the file in read-only mode. If you choose more than one file to open, all files will be opened read-only."));
382
383 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
384 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
385 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
386 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
387
388 gtk_widget_set_size_request(dialog, -1, 460);
389 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
390 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
391 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
392 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
393 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
394 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
395 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
396
397 /* add checkboxes and filename entry */
398 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), add_file_open_extra_widget(dialog));
399
400 /* add FileFilters(start with "All Files") */
401 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
403 /* now create meta filter "All Source" */
404 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog),
407 {
408 GeanyFiletype *ft = node->data;
409
410 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
411 continue;
412 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filetypes_create_file_filter(ft));
413 }
414
415 g_signal_connect(dialog, "notify::show-hidden",
417
418 return dialog;
419}
420
421
423{
424 static gboolean initialized = FALSE;
425 GtkWidget *check_hidden = ui_lookup_widget(dialog, "check_hidden");
426 GtkWidget *filetype_combo = ui_lookup_widget(dialog, "filetype_combo");
427 GtkWidget *encoding_combo = ui_lookup_widget(dialog, "encoding_combo");
428 GtkWidget *expander = ui_lookup_widget(dialog, "more_options_expander");
429
430 /* we can't know the initial position of combo boxes, so retrieve it the first time */
431 if (! initialized)
432 {
434
435 initialized = TRUE;
436 }
437 else
438 {
440 }
441 gtk_expander_set_expanded(GTK_EXPANDER(expander), filesel_state.open.more_options_visible);
442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_hidden), filesel_state.open.show_hidden);
445}
446
447
448/* This shows the file selection dialog to open a file. */
450{
451 gchar *initdir;
452
453 /* set dialog directory to the current file's directory, if present */
455
456 /* use project or default startup directory (if set) if no files are open */
457 /** TODO should it only be used when initially open the dialog and not on every show? */
458 if (! initdir)
459 initdir = g_strdup(utils_get_default_dir_utf8());
460
461 SETPTR(initdir, utils_get_locale_from_utf8(initdir));
462
463#ifdef G_OS_WIN32
465 win32_show_document_open_dialog(GTK_WINDOW(main_widgets.window), _("Open File"), initdir);
466 else
467#endif
468 {
469 GtkWidget *dialog = create_open_file_dialog();
470
472
473 if (initdir != NULL && g_path_is_absolute(initdir))
474 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), initdir);
475
476 if (app->project && !EMPTY(app->project->base_path))
477 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
479
481 gtk_dialog_run(GTK_DIALOG(dialog))));
482 gtk_widget_destroy(dialog);
483 }
484 g_free(initdir);
485}
486
487
488static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file)
489{
491 gboolean success = FALSE;
492
493 g_return_val_if_fail(doc != NULL, FALSE);
494 g_return_val_if_fail(!EMPTY(utf8_filename), FALSE);
495
496 if (doc->file_name != NULL)
497 {
498 if (rename_file)
499 {
500 document_rename_file(doc, utf8_filename);
501 }
502 if (doc->tm_file)
503 {
504 /* create a new tm_source_file object otherwise tagmanager won't work correctly */
507 doc->tm_file = NULL;
508 }
509 }
510 success = document_save_file_as(doc, utf8_filename);
511
513 return success;
514}
515
516
517static gboolean save_as_dialog_handle_response(GtkWidget *dialog, gint response)
518{
519 gboolean rename_file = FALSE;
520 gboolean success = FALSE;
521 gchar *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
522
523 switch (response)
524 {
526 /* rename doesn't check for empty filename or overwriting */
527 if (G_UNLIKELY(EMPTY(new_filename)))
528 {
529 utils_beep();
530 break;
531 }
532 if (g_file_test(new_filename, G_FILE_TEST_EXISTS) &&
534 _("Overwrite?"),
535 _("Filename already exists!")))
536 break;
537 rename_file = TRUE;
538 /* fall through */
539 case GTK_RESPONSE_ACCEPT:
540 {
541 gchar *utf8_filename;
542
543 utf8_filename = utils_get_utf8_from_locale(new_filename);
544 success = handle_save_as(utf8_filename, rename_file);
545 g_free(utf8_filename);
546 break;
547 }
548 case GTK_RESPONSE_DELETE_EVENT:
549 case GTK_RESPONSE_CANCEL:
550 success = TRUE;
551 break;
552 }
553 g_free(new_filename);
554
555 return success;
556}
557
558
560{
561 GtkWidget *dialog, *rename_btn;
562 const gchar *initdir;
563
564 dialog = gtk_file_chooser_dialog_new(_("Save File"), GTK_WINDOW(main_widgets.window),
565 GTK_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
566 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
567 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
568 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
569 gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG);
570 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(main_widgets.window));
571 gtk_widget_set_name(dialog, "GeanyDialog");
572
573 rename_btn = gtk_dialog_add_button(GTK_DIALOG(dialog), _("R_ename"), GEANY_RESPONSE_RENAME);
574 gtk_widget_set_tooltip_text(rename_btn, _("Save the file and rename it"));
575 /* disable rename unless file exists on disk */
576 gtk_widget_set_sensitive(rename_btn, doc->real_path != NULL);
577
578 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
579 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
580 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
581 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
582
583 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
584 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
585
586 /* set the folder by default to the project base dir or the global pref for opening files */
587 initdir = utils_get_default_dir_utf8();
588 if (initdir)
589 {
590 gchar *linitdir = utils_get_locale_from_utf8(initdir);
591 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), linitdir);
592 g_free(linitdir);
593 }
594 return dialog;
595}
596
597
598static gboolean show_save_as_gtk(GeanyDocument *doc)
599{
600 GtkWidget *dialog;
601 gint resp;
602
603 g_return_val_if_fail(DOC_VALID(doc), FALSE);
604
606
607 if (doc->file_name != NULL)
608 {
609 if (g_path_is_absolute(doc->file_name))
610 {
611 gchar *locale_filename = utils_get_locale_from_utf8(doc->file_name);
612 gchar *locale_basename = g_path_get_basename(locale_filename);
613 gchar *locale_dirname = g_path_get_dirname(locale_filename);
614
615 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_dirname);
616 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), locale_basename);
617
618 g_free(locale_filename);
619 g_free(locale_basename);
620 g_free(locale_dirname);
621 }
622 else
623 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), doc->file_name);
624 }
625 else
626 {
627 gchar *fname = NULL;
628
629 if (doc->file_type != NULL && doc->file_type->extension != NULL)
630 fname = g_strconcat(GEANY_STRING_UNTITLED, ".",
631 doc->file_type->extension, NULL);
632 else
633 fname = g_strdup(GEANY_STRING_UNTITLED);
634
635 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname);
636
637 g_free(fname);
638 }
639
640 if (app->project && !EMPTY(app->project->base_path))
641 gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
643
644 /* Run the dialog synchronously, pausing this function call */
645 do
646 {
647 resp = gtk_dialog_run(GTK_DIALOG(dialog));
648 }
649 while (! save_as_dialog_handle_response(dialog, resp));
650
651 if (app->project && !EMPTY(app->project->base_path))
652 gtk_file_chooser_remove_shortcut_folder(GTK_FILE_CHOOSER(dialog),
654
655 gtk_widget_destroy(dialog);
656
657 return (resp == GTK_RESPONSE_ACCEPT);
658}
659
660
661/**
662 * Shows the Save As dialog for the current notebook page.
663 *
664 * @return @c TRUE if the file was saved, otherwise @c FALSE.
665 **/
666GEANY_API_SYMBOL
668{
670 gboolean result = FALSE;
671
672 g_return_val_if_fail(doc, FALSE);
673
674#ifdef G_OS_WIN32
676 {
677 gchar *utf8_name = win32_show_document_save_as_dialog(GTK_WINDOW(main_widgets.window),
678 _("Save File"), doc);
679 if (utf8_name != NULL)
680 result = handle_save_as(utf8_name, FALSE);
681 }
682 else
683#endif
684 result = show_save_as_gtk(doc);
685 return result;
686}
687
688
689#ifndef G_OS_WIN32
690static void show_msgbox_dialog(GtkWidget *dialog, GtkMessageType type, GtkWindow *parent)
691{
692 const gchar *title;
693 switch (type)
694 {
695 case GTK_MESSAGE_ERROR:
696 title = _("Error");
697 break;
698 case GTK_MESSAGE_QUESTION:
699 title = _("Question");
700 break;
701 case GTK_MESSAGE_WARNING:
702 title = _("Warning");
703 break;
704 default:
705 title = _("Information");
706 break;
707 }
708 gtk_window_set_title(GTK_WINDOW(dialog), title);
709 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
710 gtk_widget_set_name(dialog, "GeanyDialog");
711
712 gtk_dialog_run(GTK_DIALOG(dialog));
713 gtk_widget_destroy(dialog);
714}
715#endif
716
717
718/**
719 * Shows a message box of the type @a type with @a text.
720 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
721 * message dialog box is shown.
722 *
723 * @param type A @c GtkMessageType, e.g. @c GTK_MESSAGE_INFO, @c GTK_MESSAGE_WARNING,
724 * @c GTK_MESSAGE_QUESTION, @c GTK_MESSAGE_ERROR.
725 * @param text Printf()-style format string.
726 * @param ... Arguments for the @a text format string.
727 **/
728GEANY_API_SYMBOL
729void dialogs_show_msgbox(GtkMessageType type, const gchar *text, ...)
730{
731#ifndef G_OS_WIN32
732 GtkWidget *dialog;
733#endif
734 gchar *string;
735 va_list args;
736 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
737
738 va_start(args, text);
739 string = g_strdup_vprintf(text, args);
740 va_end(args);
741
742#ifdef G_OS_WIN32
743 win32_message_dialog(GTK_WIDGET(parent), type, string);
744#else
745 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
746 type, GTK_BUTTONS_OK, "%s", string);
747 show_msgbox_dialog(dialog, type, parent);
748#endif
749 g_free(string);
750}
751
752
753void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
754{
755 GtkWindow *parent = (main_status.main_window_realized) ? GTK_WINDOW(main_widgets.window) : NULL;
756#ifdef G_OS_WIN32
757 /* put the two strings together because Windows message boxes don't support secondary texts */
758 gchar *string = g_strconcat(text, "\n", secondary, NULL);
759 win32_message_dialog(GTK_WIDGET(parent), type, string);
760 g_free(string);
761#else
762 GtkWidget *dialog;
763 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT,
764 type, GTK_BUTTONS_OK, "%s", text);
765 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
766 show_msgbox_dialog(dialog, type, parent);
767#endif
768}
769
770
771static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
772{
773 GtkWidget *dialog, *button;
774 gint ret;
775
776 dialog = gtk_message_dialog_new(GTK_WINDOW(main_widgets.window), GTK_DIALOG_DESTROY_WITH_PARENT,
777 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s", msg);
778 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
779 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", msg2);
780 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
781
782 button = ui_button_new_with_image(GTK_STOCK_CLEAR, _("_Don't save"));
783 gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_NO);
784 gtk_widget_show(button);
785
786 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
787
788 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
789 ret = gtk_dialog_run(GTK_DIALOG(dialog));
790
791 gtk_widget_destroy(dialog);
792
793 return ret;
794}
795
796
798{
799 gchar *msg, *short_fn = NULL;
800 const gchar *msg2;
801 gint response;
802 gboolean old_quitting_state = main_status.quitting;
803
804 /* display the file tab to remind the user of the document */
805 main_status.quitting = FALSE;
807 main_status.quitting = old_quitting_state;
808
809 short_fn = document_get_basename_for_display(doc, -1);
810
811 msg = g_strdup_printf(_("The file '%s' is not saved."), short_fn);
812 msg2 = _("Do you want to save it before closing?");
813 g_free(short_fn);
814
815 response = run_unsaved_dialog(msg, msg2);
816 g_free(msg);
817
818 switch (response)
819 {
820 case GTK_RESPONSE_YES:
821 /* document_save_file() returns the status if the file could be saved */
822 return document_save_file(doc, FALSE);
823
824 case GTK_RESPONSE_NO:
825 return TRUE;
826
827 case GTK_RESPONSE_CANCEL: /* fall through to default and leave the function */
828 default:
829 return FALSE;
830 }
831}
832
833
834/* Use GtkFontChooserDialog on GTK3.2 for consistency, and because
835 * GtkFontSelectionDialog is somewhat broken on 3.4 */
836#if GTK_CHECK_VERSION(3, 2, 0)
837# undef GTK_FONT_SELECTION_DIALOG
838# define GTK_FONT_SELECTION_DIALOG GTK_FONT_CHOOSER_DIALOG
839
840# define gtk_font_selection_dialog_new(title) \
841 gtk_font_chooser_dialog_new((title), NULL)
842# define gtk_font_selection_dialog_get_font_name(dlg) \
843 gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dlg))
844# define gtk_font_selection_dialog_set_font_name(dlg, font) \
845 gtk_font_chooser_set_font(GTK_FONT_CHOOSER(dlg), (font))
846#endif
847
848static void
849on_font_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
850{
851 gboolean close = TRUE;
852
853 switch (response)
854 {
855 case GTK_RESPONSE_APPLY:
856 case GTK_RESPONSE_OK:
857 {
858 gchar *fontname;
859
860 fontname = gtk_font_selection_dialog_get_font_name(
861 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel));
862 ui_set_editor_font(fontname);
863 g_free(fontname);
864
865 close = (response == GTK_RESPONSE_OK);
866 break;
867 }
868 }
869
870 if (close)
871 gtk_widget_hide(ui_widgets.open_fontsel);
872}
873
874
875/* This shows the font selection dialog to choose a font. */
877{
878#ifdef G_OS_WIN32
880 {
881 win32_show_font_dialog();
882 return;
883 }
884#endif
885
886 if (ui_widgets.open_fontsel == NULL)
887 {
888 GtkWidget *apply_button;
889
890 ui_widgets.open_fontsel = gtk_font_selection_dialog_new(_("Choose font"));;
891 gtk_container_set_border_width(GTK_CONTAINER(ui_widgets.open_fontsel), 4);
892 gtk_window_set_modal(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
893 gtk_window_set_destroy_with_parent(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
894 gtk_window_set_skip_taskbar_hint(GTK_WINDOW(ui_widgets.open_fontsel), TRUE);
895 gtk_window_set_type_hint(GTK_WINDOW(ui_widgets.open_fontsel), GDK_WINDOW_TYPE_HINT_DIALOG);
896 gtk_widget_set_name(ui_widgets.open_fontsel, "GeanyDialog");
897
898 apply_button = gtk_dialog_get_widget_for_response(GTK_DIALOG(ui_widgets.open_fontsel), GTK_RESPONSE_APPLY);
899
900 if (apply_button)
901 gtk_widget_show(apply_button);
902
903 g_signal_connect(ui_widgets.open_fontsel,
904 "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
905 g_signal_connect(ui_widgets.open_fontsel,
906 "response", G_CALLBACK(on_font_dialog_response), NULL);
907
908 gtk_window_set_transient_for(GTK_WINDOW(ui_widgets.open_fontsel), GTK_WINDOW(main_widgets.window));
909 }
910 gtk_font_selection_dialog_set_font_name(
911 GTK_FONT_SELECTION_DIALOG(ui_widgets.open_fontsel), interface_prefs.editor_font);
912 /* We make sure the dialog is visible. */
913 gtk_window_present(GTK_WINDOW(ui_widgets.open_fontsel));
914}
915
916
917static void
918on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
919{
920 gtk_widget_grab_focus(entry);
921}
922
923
924static void
926{
927 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
928}
929
930
931static void
933{
934 gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT);
935}
936
937
938typedef struct
939{
940 GtkWidget *entry;
941 GtkWidget *combo;
942
943 GeanyInputCallback callback;
944 gpointer data;
945}
947
948
949static void
950on_input_dialog_response(GtkDialog *dialog, gint response, InputDialogData *data)
951{
952 if (response == GTK_RESPONSE_ACCEPT)
953 {
954 const gchar *str = gtk_entry_get_text(GTK_ENTRY(data->entry));
955
956 if (data->combo != NULL)
957 ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(data->combo), str, 0);
958
959 data->callback(str, data->data);
960 }
961 gtk_widget_hide(GTK_WIDGET(dialog));
962}
963
964
965/* Create and display an input dialog.
966 * persistent: whether to remember previous entry text in a combo box;
967 * in this case the dialog returned is not destroyed on a response,
968 * and can be reshown.
969 * Returns: the dialog widget. */
970static GtkWidget *
971dialogs_show_input_full(const gchar *title, GtkWindow *parent,
972 const gchar *label_text, const gchar *default_text,
973 gboolean persistent, GeanyInputCallback input_cb, gpointer input_cb_data,
974 GCallback insert_text_cb, gpointer insert_text_cb_data)
975{
976 GtkWidget *dialog, *vbox;
977 InputDialogData *data = g_malloc(sizeof *data);
978
979 dialog = gtk_dialog_new_with_buttons(title, parent,
980 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
981 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
982 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
983 gtk_widget_set_name(dialog, "GeanyDialog");
984 gtk_box_set_spacing(GTK_BOX(vbox), 6);
985
986 data->combo = NULL;
987 data->entry = NULL;
988 data->callback = input_cb;
989 data->data = input_cb_data;
990
991 if (label_text)
992 {
993 GtkWidget *label = gtk_label_new(label_text);
994 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
995 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
996 gtk_container_add(GTK_CONTAINER(vbox), label);
997 }
998
999 if (persistent) /* remember previous entry text in a combo box */
1000 {
1001 data->combo = gtk_combo_box_text_new_with_entry();
1002 data->entry = gtk_bin_get_child(GTK_BIN(data->combo));
1003 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
1004 gtk_container_add(GTK_CONTAINER(vbox), data->combo);
1005 }
1006 else
1007 {
1008 data->entry = gtk_entry_new();
1009 ui_entry_add_clear_icon(GTK_ENTRY(data->entry));
1010 gtk_container_add(GTK_CONTAINER(vbox), data->entry);
1011 }
1012
1013 if (default_text != NULL)
1014 {
1015 gtk_entry_set_text(GTK_ENTRY(data->entry), default_text);
1016 }
1017 gtk_entry_set_max_length(GTK_ENTRY(data->entry), 255);
1018 gtk_entry_set_width_chars(GTK_ENTRY(data->entry), 30);
1019
1020 if (insert_text_cb != NULL)
1021 g_signal_connect(data->entry, "insert-text", insert_text_cb, insert_text_cb_data);
1022 g_signal_connect(data->entry, "activate", G_CALLBACK(on_input_entry_activate), dialog);
1023 g_signal_connect(dialog, "show", G_CALLBACK(on_input_dialog_show), data->entry);
1024 g_signal_connect_data(dialog, "response", G_CALLBACK(on_input_dialog_response), data, (GClosureNotify)g_free, 0);
1025
1026 if (persistent)
1027 {
1028 /* override default handler */
1029 g_signal_connect(dialog, "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
1031 return dialog;
1032 }
1034 gtk_dialog_run(GTK_DIALOG(dialog));
1035 gtk_widget_destroy(dialog);
1036 return NULL;
1037}
1038
1039
1040/* Remember previous entry text in a combo box.
1041 * Returns: the dialog widget. */
1042GtkWidget *
1043dialogs_show_input_persistent(const gchar *title, GtkWindow *parent,
1044 const gchar *label_text, const gchar *default_text,
1045 GeanyInputCallback input_cb, gpointer input_cb_data)
1046{
1047 return dialogs_show_input_full(title, parent, label_text, default_text, TRUE, input_cb, input_cb_data, NULL, NULL);
1048}
1049
1050
1051static void on_dialog_input(const gchar *str, gpointer data)
1052{
1053 gchar **dialog_input = data;
1054 *dialog_input = g_strdup(str);
1055}
1056
1057
1058/** Asks the user for text input.
1059 * @param title Dialog title.
1060 * @param parent @nullable The currently focused window, usually @c geany->main_widgets->window.
1061 * @c NULL can be used but is discouraged due to window manager effects.
1062 * @param label_text @nullable Label text, or @c NULL.
1063 * @param default_text @nullable Text to display in the input field, or @c NULL.
1064 * @return @nullable New copy of user input or @c NULL if cancelled.
1065 * @since 0.20. */
1066GEANY_API_SYMBOL
1067gchar *dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text,
1068 const gchar *default_text)
1069{
1070 gchar *dialog_input = NULL;
1071 dialogs_show_input_full(title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input, NULL, NULL);
1072 return dialog_input;
1073}
1074
1075
1076/* Note: could be changed to dialogs_show_validated_input with argument for callback. */
1077/* Returns: newly allocated copy of the entry text or NULL on cancel.
1078 * Specialised variant for Goto Line dialog. */
1079gchar *dialogs_show_input_goto_line(const gchar *title, GtkWindow *parent, const gchar *label_text,
1080 const gchar *default_text)
1081{
1082 gchar *dialog_input = NULL;
1084 title, parent, label_text, default_text, FALSE, on_dialog_input, &dialog_input,
1086 return dialog_input;
1087}
1088
1089
1090/**
1091 * Shows an input box to enter a numerical value using a GtkSpinButton.
1092 * If the dialog is aborted, @a value remains untouched.
1093 *
1094 * @param title The dialog title.
1095 * @param label_text The shown dialog label.
1096 * @param value The default value for the spin button and the return location of the entered value.
1097 * Must be non-NULL.
1098 * @param min Minimum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1099 * @param max Maximum allowable value (see documentation for @c gtk_spin_button_new_with_range()).
1100 * @param step Increment added or subtracted by spinning the widget
1101 * (see documentation for @c gtk_spin_button_new_with_range()).
1102 *
1103 * @return @c TRUE if a value was entered and the dialog closed with 'OK'. @c FALSE otherwise.
1104 *
1105 * @since 0.16
1106 **/
1107GEANY_API_SYMBOL
1108gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text,
1109 gdouble *value, gdouble min, gdouble max, gdouble step)
1110{
1111 GtkWidget *dialog, *label, *spin, *vbox;
1112 gboolean res = FALSE;
1113
1114 g_return_val_if_fail(title != NULL, FALSE);
1115 g_return_val_if_fail(label_text != NULL, FALSE);
1116 g_return_val_if_fail(value != NULL, FALSE);
1117
1118 dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
1119 GTK_DIALOG_DESTROY_WITH_PARENT,
1120 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1121 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
1122 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1123 vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
1124 gtk_widget_set_name(dialog, "GeanyDialog");
1125
1126 label = gtk_label_new(label_text);
1127 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1128
1129 spin = gtk_spin_button_new_with_range(min, max, step);
1130 ui_entry_add_clear_icon(GTK_ENTRY(spin));
1131 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), *value);
1132 g_signal_connect(spin, "activate", G_CALLBACK(on_input_numeric_activate), dialog);
1133
1134 gtk_container_add(GTK_CONTAINER(vbox), label);
1135 gtk_container_add(GTK_CONTAINER(vbox), spin);
1136 gtk_widget_show_all(vbox);
1137
1138 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1139 {
1140 *value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin));
1141 res = TRUE;
1142 }
1143 gtk_widget_destroy(dialog);
1144
1145 return res;
1146}
1147
1148
1150{
1151 GtkWidget *dialog, *label, *image, *check;
1152 gchar *file_size, *title, *base_name, *time_changed, *time_modified, *time_accessed, *enctext;
1153 gchar *short_name;
1154#ifdef HAVE_SYS_TYPES_H
1155 GStatBuf st;
1156 off_t filesize;
1157 mode_t mode;
1158 gchar *locale_filename;
1159#else
1160 gint filesize = 0;
1161 gint mode = 0;
1162#endif
1163
1164/* define this ones, to avoid later trouble */
1165#ifndef S_IRUSR
1166# define S_IRUSR 0
1167# define S_IWUSR 0
1168# define S_IXUSR 0
1169#endif
1170#ifndef S_IRGRP
1171# define S_IRGRP 0
1172# define S_IWGRP 0
1173# define S_IXGRP 0
1174# define S_IROTH 0
1175# define S_IWOTH 0
1176# define S_IXOTH 0
1177#endif
1178
1179 g_return_if_fail(doc == NULL || doc->is_valid);
1180
1181 if (doc == NULL || doc->file_name == NULL)
1182 {
1183 dialogs_show_msgbox(GTK_MESSAGE_ERROR,
1184 _("An error occurred or file information could not be retrieved (e.g. from a new file)."));
1185 return;
1186 }
1187
1188
1189#ifdef HAVE_SYS_TYPES_H
1190 locale_filename = utils_get_locale_from_utf8(doc->file_name);
1191 if (g_stat(locale_filename, &st) == 0)
1192 {
1193 /* first copy the returned string and the trim it, to not modify the static glibc string
1194 * g_strchomp() is used to remove trailing EOL chars, which are there for whatever reason */
1195 time_changed = g_strchomp(g_strdup(ctime(&st.st_ctime)));
1196 time_modified = g_strchomp(g_strdup(ctime(&st.st_mtime)));
1197 time_accessed = g_strchomp(g_strdup(ctime(&st.st_atime)));
1198 filesize = st.st_size;
1199 mode = st.st_mode;
1200 }
1201 else
1202 {
1203 time_changed = g_strdup(_("unknown"));
1204 time_modified = g_strdup(_("unknown"));
1205 time_accessed = g_strdup(_("unknown"));
1206 filesize = (off_t) 0;
1207 mode = (mode_t) 0;
1208 }
1209 g_free(locale_filename);
1210#else
1211 time_changed = g_strdup(_("unknown"));
1212 time_modified = g_strdup(_("unknown"));
1213 time_accessed = g_strdup(_("unknown"));
1214#endif
1215
1216 base_name = g_path_get_basename(doc->file_name);
1217 short_name = utils_str_middle_truncate(base_name, 30);
1218 title = g_strdup_printf(_("%s Properties"), short_name);
1219 dialog = ui_builder_get_object("properties_dialog");
1220 gtk_window_set_title(GTK_WINDOW(dialog), title);
1221 g_free(short_name);
1222 g_free(title);
1223 gtk_widget_set_name(dialog, "GeanyDialog");
1224
1225 label = ui_lookup_widget(dialog, "file_name_label");
1226 gtk_label_set_text(GTK_LABEL(label), base_name);
1227
1228 image = ui_lookup_widget(dialog, "file_type_image");
1229 gtk_image_set_from_gicon(GTK_IMAGE(image), doc->file_type->icon,
1230 GTK_ICON_SIZE_BUTTON);
1231
1232 label = ui_lookup_widget(dialog, "file_type_label");
1233 gtk_label_set_text(GTK_LABEL(label), doc->file_type->title);
1234
1235 label = ui_lookup_widget(dialog, "file_size_label");
1236 file_size = utils_make_human_readable_str(filesize, 1, 0);
1237 gtk_label_set_text(GTK_LABEL(label), file_size);
1238 g_free(file_size);
1239
1240 label = ui_lookup_widget(dialog, "file_location_label");
1241 gtk_label_set_text(GTK_LABEL(label), doc->file_name);
1242
1243 check = ui_lookup_widget(dialog, "file_read_only_check");
1244 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), doc->readonly);
1245
1246 label = ui_lookup_widget(dialog, "file_encoding_label");
1247 enctext = g_strdup_printf("%s %s",
1248 doc->encoding,
1250 ((doc->has_bom) ? _("(with BOM)") : _("(without BOM)")) : "");
1251 gtk_label_set_text(GTK_LABEL(label), enctext);
1252 g_free(enctext);
1253
1254 label = ui_lookup_widget(dialog, "file_modified_label");
1255 gtk_label_set_text(GTK_LABEL(label), time_modified);
1256 label = ui_lookup_widget(dialog, "file_changed_label");
1257 gtk_label_set_text(GTK_LABEL(label), time_changed);
1258 label = ui_lookup_widget(dialog, "file_accessed_label");
1259 gtk_label_set_text(GTK_LABEL(label), time_accessed);
1260
1261 /* permissions */
1262 check = ui_lookup_widget(dialog, "file_perm_owner_r_check");
1263 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRUSR);
1264 check = ui_lookup_widget(dialog, "file_perm_owner_w_check");
1265 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWUSR);
1266 check = ui_lookup_widget(dialog, "file_perm_owner_x_check");
1267 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXUSR);
1268 check = ui_lookup_widget(dialog, "file_perm_group_r_check");
1269 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IRGRP);
1270 check = ui_lookup_widget(dialog, "file_perm_group_w_check");
1271 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWGRP);
1272 check = ui_lookup_widget(dialog, "file_perm_group_x_check");
1273 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXGRP);
1274 check = ui_lookup_widget(dialog, "file_perm_other_r_check");
1275 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IROTH);
1276 check = ui_lookup_widget(dialog, "file_perm_other_w_check");
1277 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IWOTH);
1278 check = ui_lookup_widget(dialog, "file_perm_other_x_check");
1279 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), mode & S_IXOTH);
1280
1281 g_free(base_name);
1282 g_free(time_changed);
1283 g_free(time_modified);
1284 g_free(time_accessed);
1285
1286 gtk_widget_show(dialog);
1287}
1288
1289
1290/* extra_text can be NULL; otherwise it is displayed below main_text.
1291 * if parent is NULL, main_widgets.window will be used
1292 * btn_1, btn_2, btn_3 can be NULL.
1293 * returns response_1, response_2, response_3, or GTK_RESPONSE_DELETE_EVENT if the dialog was discarded */
1294static gint show_prompt(GtkWidget *parent,
1295 const gchar *btn_1, GtkResponseType response_1,
1296 const gchar *btn_2, GtkResponseType response_2,
1297 const gchar *btn_3, GtkResponseType response_3,
1298 const gchar *question_text, const gchar *extra_text)
1299{
1300 gboolean ret = FALSE;
1301 GtkWidget *dialog;
1302 GtkWidget *btn;
1303
1304 if (btn_2 == NULL)
1305 {
1306 btn_2 = GTK_STOCK_NO;
1307 response_2 = GTK_RESPONSE_NO;
1308 }
1309 if (btn_3 == NULL)
1310 {
1311 btn_3 = GTK_STOCK_YES;
1312 response_3 = GTK_RESPONSE_YES;
1313 }
1314
1315#ifdef G_OS_WIN32
1316 /* our native dialog code doesn't support custom buttons */
1317 if (utils_str_equal(btn_3, GTK_STOCK_YES) &&
1318 utils_str_equal(btn_2, GTK_STOCK_NO) && btn_1 == NULL)
1319 {
1320 gchar *string = (extra_text == NULL) ? g_strdup(question_text) :
1321 g_strconcat(question_text, "\n\n", extra_text, NULL);
1322
1323 ret = win32_message_dialog(parent, GTK_MESSAGE_QUESTION, string);
1324 ret = ret ? response_3 : response_2;
1325 g_free(string);
1326 return ret;
1327 }
1328#endif
1329 if (parent == NULL && main_status.main_window_realized)
1330 parent = main_widgets.window;
1331
1332 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
1333 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1334 GTK_BUTTONS_NONE, "%s", question_text);
1335 gtk_widget_set_name(dialog, "GeanyDialog");
1336 gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
1337 gtk_window_set_icon_name(GTK_WINDOW(dialog), "geany");
1338
1339 /* question_text will be in bold if optional extra_text used */
1340 if (extra_text != NULL)
1341 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1342 "%s", extra_text);
1343
1344 if (btn_1 != NULL)
1345 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_1, response_1);
1346
1347 btn = gtk_dialog_add_button(GTK_DIALOG(dialog), btn_2, response_2);
1348 /* we don't want a default, but we need to override the apply button as default */
1349 gtk_widget_grab_default(btn);
1350 gtk_dialog_add_button(GTK_DIALOG(dialog), btn_3, response_3);
1351
1352 ret = gtk_dialog_run(GTK_DIALOG(dialog));
1353 gtk_widget_destroy(dialog);
1354
1355 return ret;
1356}
1357
1358
1359/**
1360 * Shows a question message box with @a text and Yes/No buttons.
1361 * On Unix-like systems a GTK message dialog box is shown, on Win32 systems a native Windows
1362 * message dialog box is shown.
1363 *
1364 * @param text Printf()-style format string.
1365 * @param ... Arguments for the @a text format string.
1366 *
1367 * @return @c TRUE if the user answered with Yes, otherwise @c FALSE.
1368 **/
1369GEANY_API_SYMBOL
1370gboolean dialogs_show_question(const gchar *text, ...)
1371{
1372 gchar *string;
1373 va_list args;
1374 GtkWidget *parent = (main_status.main_window_realized) ? main_widgets.window : NULL;
1375 gint result;
1376
1377 va_start(args, text);
1378 string = g_strdup_vprintf(text, args);
1379 va_end(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,
1384 string, NULL);
1385 g_free(string);
1386 return (result == GTK_RESPONSE_YES);
1387}
1388
1389
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. */
1393gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn,
1394 const gchar *extra_text, const gchar *main_text, ...)
1395{
1396 gint result;
1397 gchar *string;
1398 va_list args;
1399
1400 va_start(args, main_text);
1401 string = g_strdup_vprintf(main_text, args);
1402 va_end(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);
1408 g_free(string);
1409 return (result == GTK_RESPONSE_YES);
1410}
1411
1412
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 */
1417gint 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, ...)
1422{
1423 gchar *string;
1424 va_list args;
1425 gint result;
1426
1427 va_start(args, main_text);
1428 string = g_strdup_vprintf(main_text, args);
1429 va_end(args);
1430 result = show_prompt(parent, btn_1, response_1, btn_2, response_2, btn_3, response_3,
1431 string, extra_text);
1432 g_free(string);
1433 return result;
1434}
Contains the GeanyApp.
void build_menu_update(GeanyDocument *doc)
Definition: build.c:1457
const gchar * label
Definition: build.c:2676
Interface to the Build menu functionality.
#define S_IXGRP
#define S_IROTH
static void show_msgbox_dialog(GtkWidget *dialog, GtkMessageType type, GtkWindow *parent)
Definition: dialogs.c:690
static guint file_chooser_get_filter_idx(GtkFileChooser *chooser)
Definition: dialogs.c:93
static gboolean show_save_as_gtk(GeanyDocument *doc)
Definition: dialogs.c:598
gboolean dialogs_show_question_full(GtkWidget *parent, const gchar *yes_btn, const gchar *no_btn, const gchar *extra_text, const gchar *main_text,...)
Definition: dialogs.c:1393
static gint show_prompt(GtkWidget *parent, const gchar *btn_1, GtkResponseType response_1, const gchar *btn_2, GtkResponseType response_2, const gchar *btn_3, GtkResponseType response_3, const gchar *question_text, const gchar *extra_text)
Definition: dialogs.c:1294
gboolean dialogs_show_save_as(void)
Shows the Save As dialog for the current notebook page.
Definition: dialogs.c:667
@ GEANY_RESPONSE_RENAME
Definition: dialogs.c:62
@ GEANY_RESPONSE_VIEW
Definition: dialogs.c:63
#define S_IXOTH
static gint filetype_combo_box_get_active_filetype(GtkComboBox *combo)
Definition: dialogs.c:263
static void file_chooser_set_filter_idx(GtkFileChooser *chooser, guint idx)
Definition: dialogs.c:113
#define S_IRGRP
static GtkWidget * create_filetype_combo_box(void)
Definition: dialogs.c:209
static GtkWidget * dialogs_show_input_full(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text, gboolean persistent, GeanyInputCallback input_cb, gpointer input_cb_data, GCallback insert_text_cb, gpointer insert_text_cb_data)
Definition: dialogs.c:971
#define S_IWOTH
void dialogs_show_file_properties(GeanyDocument *doc)
Definition: dialogs.c:1149
static GtkWidget * create_save_file_dialog(GeanyDocument *doc)
Definition: dialogs.c:559
static GtkWidget * add_file_open_extra_widget(GtkWidget *dialog)
Definition: dialogs.c:300
GtkWidget * dialogs_show_input_persistent(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text, GeanyInputCallback input_cb, gpointer input_cb_data)
Definition: dialogs.c:1043
static void open_file_dialog_apply_settings(GtkWidget *dialog)
Definition: dialogs.c:422
gboolean dialogs_show_question(const gchar *text,...)
Shows a question message box with text and Yes/No buttons.
Definition: dialogs.c:1370
static struct FileSelState filesel_state
gint dialogs_show_prompt(GtkWidget *parent, const gchar *btn_1, GtkResponseType response_1, const gchar *btn_2, GtkResponseType response_2, const gchar *btn_3, GtkResponseType response_3, const gchar *extra_text, const gchar *main_text,...)
Definition: dialogs.c:1417
static gint run_unsaved_dialog(const gchar *msg, const gchar *msg2)
Definition: dialogs.c:771
static void on_file_open_show_hidden_notify(GObject *filechooser, GParamSpec *pspec, gpointer data)
Definition: dialogs.c:177
void dialogs_show_open_file(void)
Definition: dialogs.c:449
#define S_IRUSR
static void filetype_combo_cell_data_func(GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
Definition: dialogs.c:197
static void on_input_dialog_response(GtkDialog *dialog, gint response, InputDialogData *data)
Definition: dialogs.c:950
static void on_dialog_input(const gchar *str, gpointer data)
Definition: dialogs.c:1051
static gboolean handle_save_as(const gchar *utf8_filename, gboolean rename_file)
Definition: dialogs.c:488
static void on_input_entry_activate(GtkEntry *entry, GtkDialog *dialog)
Definition: dialogs.c:925
static void on_input_dialog_show(GtkDialog *dialog, GtkWidget *entry)
Definition: dialogs.c:918
gboolean dialogs_show_input_numeric(const gchar *title, const gchar *label_text, gdouble *value, gdouble min, gdouble max, gdouble step)
Shows an input box to enter a numerical value using a GtkSpinButton.
Definition: dialogs.c:1108
gchar * dialogs_show_input_goto_line(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text)
Definition: dialogs.c:1079
gchar * dialogs_show_input(const gchar *title, GtkWindow *parent, const gchar *label_text, const gchar *default_text)
Asks the user for text input.
Definition: dialogs.c:1067
static void on_file_open_check_hidden_toggled(GtkToggleButton *togglebutton, GtkWidget *dialog)
Definition: dialogs.c:190
gboolean dialogs_show_unsaved_file(GeanyDocument *doc)
Definition: dialogs.c:797
static gboolean filetype_combo_box_set_active_filetype(GtkComboBox *combo, const gint id)
Definition: dialogs.c:277
static gboolean save_as_dialog_handle_response(GtkWidget *dialog, gint response)
Definition: dialogs.c:517
void dialogs_show_open_font(void)
Definition: dialogs.c:876
static void on_font_dialog_response(GtkDialog *dialog, gint response, gpointer user_data)
Definition: dialogs.c:849
static GtkWidget * create_open_file_dialog(void)
Definition: dialogs.c:369
static void on_input_numeric_activate(GtkEntry *entry, GtkDialog *dialog)
Definition: dialogs.c:932
static gboolean open_file_dialog_handle_response(GtkWidget *dialog, gint response)
Definition: dialogs.c:125
#define S_IWUSR
#define S_IWGRP
#define S_IXUSR
void dialogs_show_msgbox(GtkMessageType type, const gchar *text,...)
Shows a message box of the type type with text.
Definition: dialogs.c:729
void dialogs_show_msgbox_with_secondary(GtkMessageType type, const gchar *text, const gchar *secondary)
Definition: dialogs.c:753
File related dialogs, miscellaneous dialogs, font dialog.
GeanyDocument * document_get_current(void)
Finds the current document.
Definition: document.c:371
gboolean document_save_file_as(GeanyDocument *doc, const gchar *utf8_fname)
Saves the document, detecting the filetype.
Definition: document.c:1815
void document_show_tab(GeanyDocument *doc)
Definition: document.c:1273
gboolean document_save_file(GeanyDocument *doc, gboolean force)
Saves the document.
Definition: document.c:2103
void document_rename_file(GeanyDocument *doc, const gchar *new_filename)
Renames the file in doc to new_filename.
Definition: document.c:1749
void document_open_files(const GSList *filenames, gboolean readonly, GeanyFiletype *ft, const gchar *forced_enc)
Opens each file in the list filenames.
Definition: document.c:1555
gchar * document_get_basename_for_display(GeanyDocument *doc, gint length)
Returns the last part of the filename of the given GeanyDocument.
Definition: document.c:412
Document related actions: new, save, open, etc.
#define DOC_VALID(doc_ptr)
Null-safe way to check GeanyDocument::is_valid.
Definition: document.h:162
gchar * text
Definition: editor.c:83
GeanyEncoding encodings[GEANY_ENCODINGS_MAX]
Definition: encodings.c:59
gboolean encodings_is_unicode_charset(const gchar *string)
Definition: encodings.c:855
Encoding conversion and Byte Order Mark (BOM) handling.
@ GEANY_ENCODINGS_MAX
Definition: encodings.h:124
static gboolean check_hidden(const gchar *filename, const gchar *base_name)
Definition: filebrowser.c:128
Filetype detection, file extensions and filetype menu items.
@ GEANY_FILETYPES_NONE
Definition: filetypes.h:46
#define filetypes
Wraps GeanyData::filetypes_array so it can be used with C array syntax.
Definition: filetypes.h:178
@ GEANY_FILETYPE_GROUP_MISC
Definition: filetypes.h:131
@ GEANY_FILETYPE_GROUP_MARKUP
Definition: filetypes.h:130
@ GEANY_FILETYPE_GROUP_SCRIPT
Definition: filetypes.h:129
@ GEANY_FILETYPE_GROUP_NONE
Definition: filetypes.h:127
@ GEANY_FILETYPE_GROUP_COMPILED
Definition: filetypes.h:128
#define GEANY_STRING_UNTITLED
Definition: geany.h:49
unsigned int max
GeanyApp * app
Definition: libmain.c:86
GeanyStatus main_status
Definition: libmain.c:89
Main program-related commands.
#define NULL
Definition: rbtree.h:150
GtkWidget * entry
Definition: search.c:118
GtkWidget * encoding_combo
Definition: search.c:142
GtkWidget * close
Definition: sidebar.c:56
GeanyFiletype * filetypes_index(gint idx)
Accessor function for GeanyData::filetypes_array items.
Definition: filetypes.c:1489
GSList * filetypes_by_title
Definition: filetypes.c:59
GtkFileFilter * filetypes_create_file_filter(const GeanyFiletype *ft)
Definition: filetypes.c:1204
GtkFileFilter * filetypes_create_file_filter_all_source(void)
Definition: filetypes.c:1182
GtkWidget * dialog
gtk_container_add(GTK_CONTAINER(dialog->vbox), check_button)
ui_hookup_widget(dialog, check_button, "check_handle")
gtk_widget_show_all(dialog)
gint filetype_idx
Definition: dialogs.c:73
guint filter_idx
Definition: dialogs.c:71
struct FileSelState::@83 open
gint encoding_idx
Definition: dialogs.c:72
gboolean show_hidden
Definition: dialogs.c:74
gboolean more_options_visible
Definition: dialogs.c:75
struct GeanyProject * project
Currently active project or NULL if none is open.
Definition: app.h:49
Structure for representing an open tab with all its properties.
Definition: document.h:81
gchar * file_name
The UTF-8 encoded file name.
Definition: document.h:92
GeanyFiletype * file_type
The filetype for this document, it's only a reference to one of the elements of the global filetypes ...
Definition: document.h:101
gboolean has_bom
Internally used flag to indicate whether the file of this document has a byte-order-mark.
Definition: document.h:97
gchar * real_path
The link-dereferenced, locale-encoded file name.
Definition: document.h:115
gchar * encoding
The encoding of the document, must be a valid string representation of an encoding,...
Definition: document.h:95
gboolean readonly
Whether this document is read-only.
Definition: document.h:105
gboolean is_valid
Flag used to check if this document is valid when iterating GeanyData::documents_array.
Definition: document.h:83
TMSourceFile * tm_file
TMSourceFile object for this document, or NULL.
Definition: document.h:103
const gchar * charset
Represents a filetype.
Definition: filetypes.h:144
GIcon * icon
Definition: filetypes.h:165
GeanyFiletypeID id
Index in filetypes.
Definition: filetypes.h:145
GeanyFiletypeGroupID group
Definition: filetypes.h:161
gchar * title
Shown in the file open dialog, such as "C source file".
Definition: filetypes.h:154
gchar * extension
Default file extension for new files, or NULL.
Definition: filetypes.h:155
gboolean use_native_windows_dialogs
whether to use native Windows' dialogs (only used on Windows)
Definition: ui_utils.h:67
gchar * editor_font
editor font
Definition: ui_utils.h:49
GtkWidget * window
Main window.
Definition: ui_utils.h:80
gchar * base_path
Base path of the project directory (in UTF-8, maybe relative).
Definition: project.h:39
GtkWidget * combo
Definition: dialogs.c:941
GtkWidget * entry
Definition: dialogs.c:940
gpointer data
Definition: dialogs.c:944
GeanyInputCallback callback
Definition: dialogs.c:943
Defines internationalization macros.
#define _(String)
Definition: support.h:42
#define C_(Context, String)
Definition: support.h:44
void tm_source_file_free(TMSourceFile *source_file)
Decrements the reference count of source_file.
void tm_workspace_remove_source_file(TMSourceFile *source_file)
Removes a source file from the workspace if it exists.
Definition: tm_workspace.c:223
GtkWidget * ui_dialog_vbox_new(GtkDialog *dialog)
Makes a fixed border for dialogs without increasing the button box border.
Definition: ui_utils.c:1499
gpointer ui_builder_get_object(const gchar *name)
Definition: ui_utils.c:2773
void ui_set_editor_font(const gchar *font_name)
Definition: ui_utils.c:413
gboolean ui_encodings_combo_box_set_active_encoding(GtkComboBox *combo, gint enc)
Definition: ui_utils.c:3203
gboolean ui_tree_model_iter_any_next(GtkTreeModel *model, GtkTreeIter *iter, gboolean down)
Definition: ui_utils.c:3130
GtkWidget * ui_create_encodings_combo_box(gboolean has_detect, gint default_enc)
Definition: ui_utils.c:3165
GeanyMainWidgets main_widgets
Definition: ui_utils.c:72
void ui_entry_add_clear_icon(GtkEntry *entry)
Adds a small clear icon to the right end of the passed entry.
Definition: ui_utils.c:1604
gint ui_encodings_combo_box_get_active_encoding(GtkComboBox *combo)
Definition: ui_utils.c:3187
void ui_combo_box_add_to_history(GtkComboBoxText *combo_entry, const gchar *text, gint history_len)
Prepends text to the drop down list, removing a duplicate element in the list if found.
Definition: ui_utils.c:1697
GtkWidget * ui_button_new_with_image(const gchar *stock_id, const gchar *text)
Creates a GtkButton with custom text and a stock image similar to gtk_button_new_from_stock().
Definition: ui_utils.c:1552
GtkWidget * ui_lookup_widget(GtkWidget *widget, const gchar *widget_name)
Returns a widget from a name in a component, usually created by Glade.
Definition: ui_utils.c:2743
void ui_editable_insert_text_callback(GtkEditable *editable, gchar *new_text, gint new_text_len, gint *position, gpointer data)
Definition: ui_utils.c:3036
UIWidgets ui_widgets
Definition: ui_utils.c:75
GeanyInterfacePrefs interface_prefs
Definition: ui_utils.c:71
User Interface general utility functions.
gchar * utils_make_human_readable_str(guint64 size, gulong block_size, gulong display_unit)
Definition: utils.c:926
gchar * utils_str_middle_truncate(const gchar *string, guint truncate_length)
Truncates the input string to a given length.
Definition: utils.c:549
gchar * utils_get_utf8_from_locale(const gchar *locale_text)
Converts the given string (in locale encoding) into UTF-8 encoding.
Definition: utils.c:1272
gchar * utils_get_current_file_dir_utf8(void)
Definition: utils.c:897
void utils_beep(void)
Definition: utils.c:918
gboolean utils_str_equal(const gchar *a, const gchar *b)
NULL-safe string comparison.
Definition: utils.c:599
const gchar * utils_get_default_dir_utf8(void)
Definition: utils.c:1615
gchar * utils_get_locale_from_utf8(const gchar *utf8_text)
Converts the given UTF-8 encoded string into locale encoding.
Definition: utils.c:1243
General utility functions, non-GTK related.
#define foreach_slist(node, list)
Iterates all the nodes in list.
Definition: utils.h:121
#define SETPTR(ptr, result)
Assigns result to ptr, then frees the old value.
Definition: utils.h:50
#define EMPTY(ptr)
Returns TRUE if ptr is NULL or *ptr is FALSE.
Definition: utils.h:38