2 * ui_utils.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2006-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
6 * Copyright 2011 Matthew Brush <mbrush(at)codebrainz(dot)ca>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 * User Interface general utility functions.
33 #include <gdk/gdkkeysyms.h>
38 #include "sciwrappers.h"
40 #include "documentprivate.h"
41 #include "filetypes.h"
43 #include "msgwindow.h"
45 #include "callbacks.h"
46 #include "encodings.h"
55 #include "geanymenubuttonaction.h"
61 GeanyInterfacePrefs interface_prefs
;
62 GeanyMainWidgets main_widgets
;
67 static GtkBuilder
*builder
= NULL
;
68 static GtkWidget
* window1
= NULL
;
69 static GtkWidget
* toolbar_popup_menu1
= NULL
;
70 static GtkWidget
* edit_menu1
= NULL
;
71 static GtkWidget
* prefs_dialog
= NULL
;
72 static GtkWidget
* project_dialog
= NULL
;
76 /* pointers to widgets only sensitive when there is at least one document, the pointers can
77 * also be GtkAction objects, so check each pointer before using it */
78 GPtrArray
*document_buttons
;
79 GtkWidget
*menu_insert_include_items
[2];
80 GtkWidget
*popup_goto_items
[4];
81 GtkWidget
*popup_copy_items
[3];
82 GtkWidget
*menu_copy_items
[3];
83 GtkWidget
*redo_items
[3];
84 GtkWidget
*undo_items
[3];
85 GtkWidget
*save_buttons
[4];
86 GtkWidget
*config_files_menu
;
102 void (*activate_cb
)(GtkMenuItem
*, gpointer
);
106 static void update_recent_menu(GeanyRecentFiles
*grf
);
107 static void recent_file_loaded(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
);
108 static void recent_file_activate_cb(GtkMenuItem
*menuitem
, gpointer user_data
);
109 static void recent_project_activate_cb(GtkMenuItem
*menuitem
, gpointer user_data
);
110 static GtkWidget
*progress_bar_create(void);
113 /* simple wrapper for gtk_widget_set_sensitive() to allow widget being NULL */
114 void ui_widget_set_sensitive(GtkWidget
*widget
, gboolean set
)
117 gtk_widget_set_sensitive(widget
, set
);
121 /* allow_override is TRUE if text can be ignored when another message has been set
122 * that didn't use allow_override and has not timed out. */
123 static void set_statusbar(const gchar
*text
, gboolean allow_override
)
125 static glong last_time
= 0;
127 const gint GEANY_STATUS_TIMEOUT
= 1;
129 if (! interface_prefs
.statusbar_visible
)
130 return; /* just do nothing if statusbar is not visible */
132 g_get_current_time(&timeval
);
134 if (! allow_override
)
136 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets
.statusbar
), 1);
137 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets
.statusbar
), 1, text
);
138 last_time
= timeval
.tv_sec
;
141 if (timeval
.tv_sec
> last_time
+ GEANY_STATUS_TIMEOUT
)
143 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets
.statusbar
), 1);
144 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets
.statusbar
), 1, text
);
149 /** Displays text on the statusbar.
150 * @param log Whether the message should be recorded in the Status window.
151 * @param format A @c printf -style string. */
152 void ui_set_statusbar(gboolean log
, const gchar
*format
, ...)
157 va_start(args
, format
);
158 string
= g_strdup_vprintf(format
, args
);
161 if (! prefs
.suppress_status_messages
)
162 set_statusbar(string
, FALSE
);
164 if (log
|| prefs
.suppress_status_messages
)
165 msgwin_status_add("%s", string
);
171 static gchar
*statusbar_template
= NULL
;
173 /* note: some comments below are for translators */
174 static void add_statusbar_statistics(GString
*stats_str
,
175 GeanyDocument
*doc
, guint line
, guint col
)
177 const gchar
*cur_tag
;
179 const gchar
*expos
; /* % expansion position */
180 const gchar sp
[] = " ";
182 fmt
= NZV(statusbar_template
) ? statusbar_template
:
183 /* Status bar statistics: col = column, sel = selection. */
184 _("line: %l / %L\t col: %c\t sel: %s\t %w %t %m"
185 "mode: %M encoding: %e filetype: %f scope: %S");
187 g_string_assign(stats_str
, "");
188 while ((expos
= strchr(fmt
, '%')) != NULL
)
190 /* append leading text before % char */
191 g_string_append_len(stats_str
, fmt
, expos
- fmt
);
196 g_string_append_printf(stats_str
, "%d", line
+ 1);
199 g_string_append_printf(stats_str
, "%d",
200 sci_get_line_count(doc
->editor
->sci
));
203 g_string_append_printf(stats_str
, "%d", col
);
206 g_string_append_printf(stats_str
, "%d", col
+ 1);
209 g_string_append_printf(stats_str
, "%d",
210 sci_get_selected_text_length(doc
->editor
->sci
) - 1);
214 g_string_append(stats_str
, (doc
->readonly
) ? _("RO ") :
215 /* OVR = overwrite/overtype, INS = insert */
216 (sci_get_overtype(doc
->editor
->sci
) ? _("OVR") : _("INS")));
221 g_string_append(stats_str
, _("RO ")); /* RO = read-only */
222 g_string_append(stats_str
, sp
+ 1);
227 switch (editor_get_indent_prefs(doc
->editor
)->type
)
229 case GEANY_INDENT_TYPE_TABS
:
230 g_string_append(stats_str
, _("TAB"));
232 case GEANY_INDENT_TYPE_SPACES
: /* SP = space */
233 g_string_append(stats_str
, _("SP"));
235 case GEANY_INDENT_TYPE_BOTH
: /* T/S = tabs and spaces */
236 g_string_append(stats_str
, _("T/S"));
244 g_string_append(stats_str
, _("MOD")); /* MOD = modified */
245 g_string_append(stats_str
, sp
);
249 g_string_append(stats_str
, editor_get_eol_char_name(doc
->editor
));
252 g_string_append(stats_str
,
253 doc
->encoding
? doc
->encoding
: _("unknown"));
254 if (encodings_is_unicode_charset(doc
->encoding
) && (doc
->has_bom
))
256 g_string_append_c(stats_str
, ' ');
257 g_string_append(stats_str
, _("(with BOM)")); /* BOM = byte order mark */
261 g_string_append(stats_str
, filetypes_get_display_name(doc
->file_type
));
264 symbols_get_current_function(doc
, &cur_tag
);
265 g_string_append(stats_str
, cur_tag
);
268 g_string_append_len(stats_str
, expos
, 1);
271 /* skip past %c chars */
277 /* add any remaining text */
278 g_string_append(stats_str
, fmt
);
282 /* updates the status bar document statistics */
283 void ui_update_statusbar(GeanyDocument
*doc
, gint pos
)
285 if (! interface_prefs
.statusbar_visible
)
286 return; /* just do nothing if statusbar is not visible */
289 doc
= document_get_current();
293 static GString
*stats_str
= NULL
;
296 if (G_UNLIKELY(stats_str
== NULL
))
297 stats_str
= g_string_sized_new(120);
300 pos
= sci_get_current_position(doc
->editor
->sci
);
301 line
= sci_get_line_from_position(doc
->editor
->sci
, pos
);
303 /* Add temporary fix for sci infinite loop in Document::GetColumn(int)
304 * when current pos is beyond document end (can occur when removing
305 * blocks of selected lines especially esp. brace sections near end of file). */
306 if (pos
<= sci_get_length(doc
->editor
->sci
))
307 col
= sci_get_col_from_position(doc
->editor
->sci
, pos
);
311 add_statusbar_statistics(stats_str
, doc
, line
, col
);
315 const gchar sp
[] = " ";
316 g_string_append(stats_str
, sp
);
317 g_string_append_printf(stats_str
, "pos: %d", pos
);
318 g_string_append(stats_str
, sp
);
319 g_string_append_printf(stats_str
, "style: %d", sci_get_style_at(doc
->editor
->sci
, pos
));
322 /* can be overridden by status messages */
323 set_statusbar(stats_str
->str
, TRUE
);
325 else /* no documents */
327 set_statusbar("", TRUE
); /* can be overridden by status messages */
332 /* This sets the window title according to the current filename. */
333 void ui_set_window_title(GeanyDocument
*doc
)
336 GeanyProject
*project
= app
->project
;
339 doc
= document_get_current();
341 str
= g_string_new(NULL
);
345 g_string_append(str
, doc
->changed
? "*" : "");
347 if (doc
->file_name
== NULL
)
348 g_string_append(str
, DOC_FILENAME(doc
));
351 gchar
*short_name
= document_get_basename_for_display(doc
, 30);
352 gchar
*dirname
= g_path_get_dirname(DOC_FILENAME(doc
));
354 g_string_append(str
, short_name
);
355 g_string_append(str
, " - ");
356 g_string_append(str
, dirname
? dirname
: "");
360 g_string_append(str
, " - ");
364 g_string_append_c(str
, '[');
365 g_string_append(str
, project
->name
);
366 g_string_append(str
, "] - ");
368 g_string_append(str
, "Geany");
369 if (cl_options
.new_instance
)
371 g_string_append(str
, _(" (new instance)"));
373 gtk_window_set_title(GTK_WINDOW(main_widgets
.window
), str
->str
);
374 g_string_free(str
, TRUE
);
378 void ui_set_editor_font(const gchar
*font_name
)
382 g_return_if_fail(font_name
!= NULL
);
384 /* do nothing if font has not changed */
385 if (interface_prefs
.editor_font
!= NULL
)
386 if (strcmp(font_name
, interface_prefs
.editor_font
) == 0)
389 g_free(interface_prefs
.editor_font
);
390 interface_prefs
.editor_font
= g_strdup(font_name
);
392 /* We copy the current style, and update the font in all open tabs. */
393 for (i
= 0; i
< documents_array
->len
; i
++)
395 if (documents
[i
]->editor
)
397 editor_set_font(documents
[i
]->editor
, interface_prefs
.editor_font
);
401 ui_set_statusbar(TRUE
, _("Font updated (%s)."), interface_prefs
.editor_font
);
405 void ui_set_fullscreen(void)
407 if (ui_prefs
.fullscreen
)
409 gtk_window_fullscreen(GTK_WINDOW(main_widgets
.window
));
413 gtk_window_unfullscreen(GTK_WINDOW(main_widgets
.window
));
418 void ui_update_popup_reundo_items(GeanyDocument
*doc
)
420 gboolean enable_undo
;
421 gboolean enable_redo
;
431 enable_undo
= document_can_undo(doc
);
432 enable_redo
= document_can_redo(doc
);
435 /* index 0 is the popup menu, 1 is the menubar, 2 is the toolbar */
436 len
= G_N_ELEMENTS(widgets
.undo_items
);
437 for (i
= 0; i
< len
; i
++)
439 ui_widget_set_sensitive(widgets
.undo_items
[i
], enable_undo
);
441 len
= G_N_ELEMENTS(widgets
.redo_items
);
442 for (i
= 0; i
< len
; i
++)
444 ui_widget_set_sensitive(widgets
.redo_items
[i
], enable_redo
);
449 void ui_update_popup_copy_items(GeanyDocument
*doc
)
457 enable
= sci_has_selection(doc
->editor
->sci
);
459 len
= G_N_ELEMENTS(widgets
.popup_copy_items
);
460 for (i
= 0; i
< len
; i
++)
461 ui_widget_set_sensitive(widgets
.popup_copy_items
[i
], enable
);
465 void ui_update_popup_goto_items(gboolean enable
)
468 len
= G_N_ELEMENTS(widgets
.popup_goto_items
);
469 for (i
= 0; i
< len
; i
++)
470 ui_widget_set_sensitive(widgets
.popup_goto_items
[i
], enable
);
474 void ui_update_menu_copy_items(GeanyDocument
*doc
)
476 gboolean enable
= FALSE
;
478 GtkWidget
*focusw
= gtk_window_get_focus(GTK_WINDOW(main_widgets
.window
));
480 if (IS_SCINTILLA(focusw
))
481 enable
= (doc
== NULL
) ? FALSE
: sci_has_selection(doc
->editor
->sci
);
483 if (GTK_IS_EDITABLE(focusw
))
484 enable
= gtk_editable_get_selection_bounds(GTK_EDITABLE(focusw
), NULL
, NULL
);
486 if (GTK_IS_TEXT_VIEW(focusw
))
488 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(
489 GTK_TEXT_VIEW(focusw
));
490 enable
= gtk_text_buffer_get_selection_bounds(buffer
, NULL
, NULL
);
493 len
= G_N_ELEMENTS(widgets
.menu_copy_items
);
494 for (i
= 0; i
< len
; i
++)
495 ui_widget_set_sensitive(widgets
.menu_copy_items
[i
], enable
);
499 void ui_update_insert_include_item(GeanyDocument
*doc
, gint item
)
501 gboolean enable
= FALSE
;
503 if (doc
== NULL
|| doc
->file_type
== NULL
)
505 else if (doc
->file_type
->id
== GEANY_FILETYPES_C
|| doc
->file_type
->id
== GEANY_FILETYPES_CPP
)
508 ui_widget_set_sensitive(widgets
.menu_insert_include_items
[item
], enable
);
512 void ui_update_fold_items(void)
514 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "menu_fold_all1"), editor_prefs
.folding
);
515 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "menu_unfold_all1"), editor_prefs
.folding
);
516 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "separator22"), editor_prefs
.folding
);
520 static void insert_include_items(GtkMenu
*me
, GtkMenu
*mp
, gchar
**includes
, gchar
*label
)
524 GtkWidget
*tmp_popup
;
525 GtkWidget
*edit_menu
, *edit_menu_item
;
526 GtkWidget
*popup_menu
, *popup_menu_item
;
528 edit_menu
= gtk_menu_new();
529 popup_menu
= gtk_menu_new();
530 edit_menu_item
= gtk_menu_item_new_with_label(label
);
531 popup_menu_item
= gtk_menu_item_new_with_label(label
);
532 gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_menu_item
), edit_menu
);
533 gtk_menu_item_set_submenu(GTK_MENU_ITEM(popup_menu_item
), popup_menu
);
535 while (includes
[i
] != NULL
)
537 tmp_menu
= gtk_menu_item_new_with_label(includes
[i
]);
538 tmp_popup
= gtk_menu_item_new_with_label(includes
[i
]);
539 gtk_container_add(GTK_CONTAINER(edit_menu
), tmp_menu
);
540 gtk_container_add(GTK_CONTAINER(popup_menu
), tmp_popup
);
541 g_signal_connect(tmp_menu
, "activate",
542 G_CALLBACK(on_menu_insert_include_activate
), (gpointer
) includes
[i
]);
543 g_signal_connect(tmp_popup
, "activate",
544 G_CALLBACK(on_insert_include_activate
), (gpointer
) includes
[i
]);
547 gtk_widget_show_all(edit_menu_item
);
548 gtk_widget_show_all(popup_menu_item
);
549 gtk_container_add(GTK_CONTAINER(me
), edit_menu_item
);
550 gtk_container_add(GTK_CONTAINER(mp
), popup_menu_item
);
554 void ui_create_insert_menu_items(void)
556 GtkMenu
*menu_edit
= GTK_MENU(ui_lookup_widget(main_widgets
.window
, "insert_include2_menu"));
557 GtkMenu
*menu_popup
= GTK_MENU(ui_lookup_widget(main_widgets
.editor_menu
, "insert_include1_menu"));
559 const gchar
*c_includes_stdlib
[] = {
560 "assert.h", "ctype.h", "errno.h", "float.h", "limits.h", "locale.h", "math.h", "setjmp.h",
561 "signal.h", "stdarg.h", "stddef.h", "stdio.h", "stdlib.h", "string.h", "time.h", NULL
563 const gchar
*c_includes_c99
[] = {
564 "complex.h", "fenv.h", "inttypes.h", "iso646.h", "stdbool.h", "stdint.h",
565 "tgmath.h", "wchar.h", "wctype.h", NULL
567 const gchar
*c_includes_cpp
[] = {
568 "cstdio", "cstring", "cctype", "cmath", "ctime", "cstdlib", "cstdarg", NULL
570 const gchar
*c_includes_cppstdlib
[] = {
571 "iostream", "fstream", "iomanip", "sstream", "exception", "stdexcept",
572 "memory", "locale", NULL
574 const gchar
*c_includes_stl
[] = {
575 "bitset", "dequev", "list", "map", "set", "queue", "stack", "vector", "algorithm",
576 "iterator", "functional", "string", "complex", "valarray", NULL
579 blank
= gtk_menu_item_new_with_label("#include \"...\"");
580 gtk_container_add(GTK_CONTAINER(menu_edit
), blank
);
581 gtk_widget_show(blank
);
582 g_signal_connect(blank
, "activate", G_CALLBACK(on_menu_insert_include_activate
),
584 blank
= gtk_separator_menu_item_new ();
585 gtk_container_add(GTK_CONTAINER(menu_edit
), blank
);
586 gtk_widget_show(blank
);
588 blank
= gtk_menu_item_new_with_label("#include \"...\"");
589 gtk_container_add(GTK_CONTAINER(menu_popup
), blank
);
590 gtk_widget_show(blank
);
591 g_signal_connect(blank
, "activate", G_CALLBACK(on_insert_include_activate
),
593 blank
= gtk_separator_menu_item_new();
594 gtk_container_add(GTK_CONTAINER(menu_popup
), blank
);
595 gtk_widget_show(blank
);
597 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_stdlib
, _("C Standard Library"));
598 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_c99
, _("ISO C99"));
599 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_cpp
, _("C++ (C Standard Library)"));
600 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_cppstdlib
, _("C++ Standard Library"));
601 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_stl
, _("C++ STL"));
605 static void insert_date_items(GtkMenu
*me
, GtkMenu
*mp
, gchar
*label
)
609 item
= gtk_menu_item_new_with_mnemonic(label
);
610 gtk_container_add(GTK_CONTAINER(me
), item
);
611 gtk_widget_show(item
);
612 g_signal_connect(item
, "activate", G_CALLBACK(on_menu_insert_date_activate
), label
);
614 item
= gtk_menu_item_new_with_mnemonic(label
);
615 gtk_container_add(GTK_CONTAINER(mp
), item
);
616 gtk_widget_show(item
);
617 g_signal_connect(item
, "activate", G_CALLBACK(on_insert_date_activate
), label
);
621 void ui_create_insert_date_menu_items(void)
623 GtkMenu
*menu_edit
= GTK_MENU(ui_lookup_widget(main_widgets
.window
, "insert_date1_menu"));
624 GtkMenu
*menu_popup
= GTK_MENU(ui_lookup_widget(main_widgets
.editor_menu
, "insert_date2_menu"));
628 insert_date_items(menu_edit
, menu_popup
, _("dd.mm.yyyy"));
629 insert_date_items(menu_edit
, menu_popup
, _("mm.dd.yyyy"));
630 insert_date_items(menu_edit
, menu_popup
, _("yyyy/mm/dd"));
632 item
= gtk_separator_menu_item_new();
633 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
634 gtk_widget_show(item
);
635 item
= gtk_separator_menu_item_new();
636 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
637 gtk_widget_show(item
);
639 insert_date_items(menu_edit
, menu_popup
, _("dd.mm.yyyy hh:mm:ss"));
640 insert_date_items(menu_edit
, menu_popup
, _("mm.dd.yyyy hh:mm:ss"));
641 insert_date_items(menu_edit
, menu_popup
, _("yyyy/mm/dd hh:mm:ss"));
643 item
= gtk_separator_menu_item_new();
644 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
645 gtk_widget_show(item
);
646 item
= gtk_separator_menu_item_new();
647 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
648 gtk_widget_show(item
);
650 str
= _("_Use Custom Date Format");
651 item
= gtk_menu_item_new_with_mnemonic(str
);
652 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
653 gtk_widget_show(item
);
654 g_signal_connect(item
, "activate", G_CALLBACK(on_menu_insert_date_activate
), str
);
655 ui_hookup_widget(main_widgets
.window
, item
, "insert_date_custom1");
657 item
= gtk_menu_item_new_with_mnemonic(str
);
658 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
659 gtk_widget_show(item
);
660 g_signal_connect(item
, "activate", G_CALLBACK(on_insert_date_activate
), str
);
661 ui_hookup_widget(main_widgets
.editor_menu
, item
, "insert_date_custom2");
663 insert_date_items(menu_edit
, menu_popup
, _("_Set Custom Date Format"));
667 void ui_save_buttons_toggle(gboolean enable
)
670 gboolean dirty_tabs
= FALSE
;
672 if (ui_prefs
.allow_always_save
)
673 enable
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
)) > 0;
675 ui_widget_set_sensitive(widgets
.save_buttons
[0], enable
);
676 ui_widget_set_sensitive(widgets
.save_buttons
[1], enable
);
678 /* save all menu item and tool button */
679 for (i
= 0; i
< documents_array
->len
; i
++)
681 /* check whether there are files where changes were made and if there are some,
682 * we need the save all button / item */
683 if (documents
[i
]->is_valid
&& documents
[i
]->changed
)
690 ui_widget_set_sensitive(widgets
.save_buttons
[2], dirty_tabs
);
691 ui_widget_set_sensitive(widgets
.save_buttons
[3], dirty_tabs
);
695 #define add_doc_widget(widget_name) \
696 g_ptr_array_add(widgets.document_buttons, ui_lookup_widget(main_widgets.window, widget_name))
698 #define add_doc_toolitem(widget_name) \
699 g_ptr_array_add(widgets.document_buttons, toolbar_get_action_by_name(widget_name))
701 static void init_document_widgets(void)
703 widgets
.document_buttons
= g_ptr_array_new();
705 /* Cache the document-sensitive widgets so we don't have to keep looking them up
706 * when using ui_document_buttons_update(). */
707 add_doc_widget("menu_close1");
708 add_doc_widget("close_other_documents1");
709 add_doc_widget("menu_change_font1");
710 add_doc_widget("menu_close_all1");
711 add_doc_widget("menu_save1");
712 add_doc_widget("menu_save_all1");
713 add_doc_widget("menu_save_as1");
714 add_doc_widget("menu_count_words1");
715 add_doc_widget("menu_build1");
716 add_doc_widget("add_comments1");
717 add_doc_widget("menu_paste1");
718 add_doc_widget("menu_undo2");
719 add_doc_widget("preferences2");
720 add_doc_widget("menu_reload1");
721 add_doc_widget("menu_document1");
722 add_doc_widget("menu_choose_color1");
723 add_doc_widget("menu_zoom_in1");
724 add_doc_widget("menu_zoom_out1");
725 add_doc_widget("menu_view_editor1");
726 add_doc_widget("normal_size1");
727 add_doc_widget("treeview6");
728 add_doc_widget("print1");
729 add_doc_widget("menu_reload_as1");
730 add_doc_widget("menu_select_all1");
731 add_doc_widget("insert_date1");
732 add_doc_widget("insert_alternative_white_space1");
733 add_doc_widget("menu_format1");
734 add_doc_widget("commands2");
735 add_doc_widget("menu_open_selected_file1");
736 add_doc_widget("page_setup1");
737 add_doc_widget("find1");
738 add_doc_widget("find_next1");
739 add_doc_widget("find_previous1");
740 add_doc_widget("go_to_next_marker1");
741 add_doc_widget("go_to_previous_marker1");
742 add_doc_widget("replace1");
743 add_doc_widget("find_nextsel1");
744 add_doc_widget("find_prevsel1");
745 add_doc_widget("find_usage1");
746 add_doc_widget("find_document_usage1");
747 add_doc_widget("mark_all1");
748 add_doc_widget("go_to_line1");
749 add_doc_widget("goto_tag_definition1");
750 add_doc_widget("goto_tag_declaration1");
751 add_doc_widget("reset_indentation1");
752 add_doc_toolitem("Close");
753 add_doc_toolitem("CloseAll");
754 add_doc_toolitem("Search");
755 add_doc_toolitem("SearchEntry");
756 add_doc_toolitem("ZoomIn");
757 add_doc_toolitem("ZoomOut");
758 add_doc_toolitem("Indent");
759 add_doc_toolitem("UnIndent");
760 add_doc_toolitem("Cut");
761 add_doc_toolitem("Copy");
762 add_doc_toolitem("Paste");
763 add_doc_toolitem("Delete");
764 add_doc_toolitem("Save");
765 add_doc_toolitem("SaveAs");
766 add_doc_toolitem("SaveAll");
767 add_doc_toolitem("Compile");
768 add_doc_toolitem("Run");
769 add_doc_toolitem("Reload");
770 add_doc_toolitem("Color");
771 add_doc_toolitem("Goto");
772 add_doc_toolitem("GotoEntry");
773 add_doc_toolitem("Replace");
774 add_doc_toolitem("Print");
778 void ui_document_buttons_update(void)
781 gboolean enable
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
)) > 0;
783 for (i
= 0; i
< widgets
.document_buttons
->len
; i
++)
785 GtkWidget
*widget
= g_ptr_array_index(widgets
.document_buttons
, i
);
786 if (GTK_IS_ACTION(widget
))
787 gtk_action_set_sensitive(GTK_ACTION(widget
), enable
);
789 ui_widget_set_sensitive(widget
, enable
);
794 static void on_doc_sensitive_widget_destroy(GtkWidget
*widget
, G_GNUC_UNUSED gpointer user_data
)
796 g_ptr_array_remove_fast(widgets
.document_buttons
, widget
);
800 /** Adds a widget to the list of widgets that should be set sensitive/insensitive
801 * when some documents are present/no documents are open.
802 * It will be removed when the widget is destroyed.
803 * @param widget The widget to add.
807 void ui_add_document_sensitive(GtkWidget
*widget
)
809 gboolean enable
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
)) > 0;
811 ui_widget_set_sensitive(widget
, enable
);
813 g_ptr_array_add(widgets
.document_buttons
, widget
);
814 g_signal_connect(widget
, "destroy", G_CALLBACK(on_doc_sensitive_widget_destroy
), NULL
);
818 void ui_widget_show_hide(GtkWidget
*widget
, gboolean show
)
822 gtk_widget_show(widget
);
826 gtk_widget_hide(widget
);
831 void ui_sidebar_show_hide(void)
835 /* check that there are no other notebook pages before hiding the sidebar completely
836 * other pages could be e.g. the file browser plugin */
837 if (! interface_prefs
.sidebar_openfiles_visible
&& ! interface_prefs
.sidebar_symbol_visible
&&
838 gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
)) <= 2)
840 ui_prefs
.sidebar_visible
= FALSE
;
843 widget
= ui_lookup_widget(main_widgets
.window
, "menu_show_sidebar1");
844 if (ui_prefs
.sidebar_visible
!= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
846 ignore_callback
= TRUE
;
847 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget
), ui_prefs
.sidebar_visible
);
848 ignore_callback
= FALSE
;
851 ui_widget_show_hide(main_widgets
.sidebar_notebook
, ui_prefs
.sidebar_visible
);
853 ui_widget_show_hide(gtk_notebook_get_nth_page(
854 GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), 0), interface_prefs
.sidebar_symbol_visible
);
855 ui_widget_show_hide(gtk_notebook_get_nth_page(
856 GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), 1), interface_prefs
.sidebar_openfiles_visible
);
860 void ui_document_show_hide(GeanyDocument
*doc
)
862 const gchar
*widget_name
;
864 const GeanyIndentPrefs
*iprefs
;
867 doc
= document_get_current();
872 ignore_callback
= TRUE
;
874 gtk_check_menu_item_set_active(
875 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_line_wrapping1")),
876 doc
->editor
->line_wrapping
);
878 gtk_check_menu_item_set_active(
879 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "line_breaking1")),
880 doc
->editor
->line_breaking
);
882 iprefs
= editor_get_indent_prefs(doc
->editor
);
884 item
= ui_lookup_widget(main_widgets
.window
, "menu_use_auto_indentation1");
885 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), doc
->editor
->auto_indent
);
887 switch (iprefs
->type
)
889 case GEANY_INDENT_TYPE_SPACES
:
890 widget_name
= "spaces1"; break;
891 case GEANY_INDENT_TYPE_TABS
:
892 widget_name
= "tabs1"; break;
893 case GEANY_INDENT_TYPE_BOTH
:
895 widget_name
= "tabs_and_spaces1"; break;
897 item
= ui_lookup_widget(main_widgets
.window
, widget_name
);
898 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
900 if (iprefs
->width
>= 1 && iprefs
->width
<= 8)
904 name
= g_strdup_printf("indent_width_%d", iprefs
->width
);
905 item
= ui_lookup_widget(main_widgets
.window
, name
);
906 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
910 gtk_check_menu_item_set_active(
911 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "set_file_readonly1")),
914 item
= ui_lookup_widget(main_widgets
.window
, "menu_write_unicode_bom1");
915 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), doc
->has_bom
);
916 ui_widget_set_sensitive(item
, encodings_is_unicode_charset(doc
->encoding
));
918 switch (sci_get_eol_mode(doc
->editor
->sci
))
920 case SC_EOL_CR
: widget_name
= "cr"; break;
921 case SC_EOL_LF
: widget_name
= "lf"; break;
922 default: widget_name
= "crlf"; break;
924 gtk_check_menu_item_set_active(
925 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, widget_name
)), TRUE
);
927 encodings_select_radio_item(doc
->encoding
);
928 filetypes_select_radio_item(doc
->file_type
);
930 ignore_callback
= FALSE
;
934 void ui_set_search_entry_background(GtkWidget
*widget
, gboolean success
)
936 static const GdkColor red
= {0, 0xffff, 0x6666, 0x6666};
937 static const GdkColor white
= {0, 0xffff, 0xffff, 0xffff};
938 static gboolean old_value
= TRUE
;
940 g_return_if_fail(widget
!= NULL
);
942 /* update only if really needed */
943 if (old_value
!= success
)
945 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, success
? NULL
: &red
);
946 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, success
? NULL
: &white
);
953 static gboolean
have_tango_icon_theme(void)
955 static gboolean result
= FALSE
;
956 static gboolean checked
= FALSE
;
962 g_object_get(G_OBJECT(gtk_settings_get_default()), "gtk-icon-theme-name", &theme_name
, NULL
);
963 SETPTR(theme_name
, g_utf8_strdown(theme_name
, -1));
965 result
= (strstr(theme_name
, "tango") != NULL
);
975 /* Note: remember to unref the pixbuf once an image or window has added a reference. */
976 GdkPixbuf
*ui_new_pixbuf_from_inline(gint img
)
980 case GEANY_IMAGE_LOGO
:
981 return gdk_pixbuf_new_from_inline(-1, aladin_inline
, FALSE
, NULL
);
983 case GEANY_IMAGE_SAVE_ALL
:
985 /* check whether the icon theme looks like a Gnome icon theme, if so use the
986 * old Gnome based Save All icon, otherwise assume a Tango-like icon theme */
987 if (have_tango_icon_theme())
988 return gdk_pixbuf_new_from_inline(-1, save_all_tango_inline
, FALSE
, NULL
);
990 return gdk_pixbuf_new_from_inline(-1, save_all_gnome_inline
, FALSE
, NULL
);
993 case GEANY_IMAGE_CLOSE_ALL
:
995 return gdk_pixbuf_new_from_inline(-1, close_all_inline
, FALSE
, NULL
);
998 case GEANY_IMAGE_BUILD
:
1000 return gdk_pixbuf_new_from_inline(-1, build_inline
, FALSE
, NULL
);
1009 static GdkPixbuf
*ui_new_pixbuf_from_stock(const gchar
*stock_id
)
1011 if (utils_str_equal(stock_id
, GEANY_STOCK_CLOSE_ALL
))
1012 return ui_new_pixbuf_from_inline(GEANY_IMAGE_CLOSE_ALL
);
1013 else if (utils_str_equal(stock_id
, GEANY_STOCK_BUILD
))
1014 return ui_new_pixbuf_from_inline(GEANY_IMAGE_BUILD
);
1015 else if (utils_str_equal(stock_id
, GEANY_STOCK_SAVE_ALL
))
1016 return ui_new_pixbuf_from_inline(GEANY_IMAGE_SAVE_ALL
);
1022 GtkWidget
*ui_new_image_from_inline(gint img
)
1027 pb
= ui_new_pixbuf_from_inline(img
);
1028 wid
= gtk_image_new_from_pixbuf(pb
);
1029 g_object_unref(pb
); /* the image doesn't adopt our reference, so remove our ref. */
1034 static void recent_create_menu(GeanyRecentFiles
*grf
)
1040 len
= MIN(file_prefs
.mru_length
, g_queue_get_length(grf
->recent_queue
));
1041 for (i
= 0; i
< len
; i
++)
1043 filename
= g_queue_peek_nth(grf
->recent_queue
, i
);
1044 /* create menu item for the recent files menu in the menu bar */
1045 tmp
= gtk_menu_item_new_with_label(filename
);
1046 gtk_widget_show(tmp
);
1047 gtk_container_add(GTK_CONTAINER(grf
->menubar
), tmp
);
1048 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1049 /* create menu item for the recent files menu in the toolbar */
1050 if (grf
->toolbar
!= NULL
)
1052 tmp
= gtk_menu_item_new_with_label(filename
);
1053 gtk_widget_show(tmp
);
1054 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
1055 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1061 static GeanyRecentFiles
*recent_get_recent_files(void)
1063 static GeanyRecentFiles grf
= { RECENT_FILE_FILE
, NULL
, NULL
, NULL
, NULL
};
1065 if (G_UNLIKELY(grf
.recent_queue
== NULL
))
1067 grf
.recent_queue
= ui_prefs
.recent_queue
;
1068 grf
.menubar
= ui_widgets
.recent_files_menu_menubar
;
1069 grf
.toolbar
= geany_menu_button_action_get_menu(GEANY_MENU_BUTTON_ACTION(
1070 toolbar_get_action_by_name("Open")));
1071 grf
.activate_cb
= recent_file_activate_cb
;
1077 static GeanyRecentFiles
*recent_get_recent_projects(void)
1079 static GeanyRecentFiles grf
= { RECENT_FILE_PROJECT
, NULL
, NULL
, NULL
, NULL
};
1081 if (G_UNLIKELY(grf
.recent_queue
== NULL
))
1083 grf
.recent_queue
= ui_prefs
.recent_projects_queue
;
1084 grf
.menubar
= ui_widgets
.recent_projects_menu_menubar
;
1086 grf
.activate_cb
= recent_project_activate_cb
;
1092 void ui_create_recent_menus(void)
1094 recent_create_menu(recent_get_recent_files());
1095 recent_create_menu(recent_get_recent_projects());
1099 static void recent_file_activate_cb(GtkMenuItem
*menuitem
, G_GNUC_UNUSED gpointer user_data
)
1101 gchar
*utf8_filename
= ui_menu_item_get_text(menuitem
);
1102 gchar
*locale_filename
= utils_get_locale_from_utf8(utf8_filename
);
1104 if (document_open_file(locale_filename
, FALSE
, NULL
, NULL
) != NULL
)
1105 recent_file_loaded(utf8_filename
, recent_get_recent_files());
1107 g_free(locale_filename
);
1108 g_free(utf8_filename
);
1112 static void recent_project_activate_cb(GtkMenuItem
*menuitem
, G_GNUC_UNUSED gpointer user_data
)
1114 gchar
*utf8_filename
= ui_menu_item_get_text(menuitem
);
1115 gchar
*locale_filename
= utils_get_locale_from_utf8(utf8_filename
);
1117 if (project_ask_close() && project_load_file_with_session(locale_filename
))
1118 recent_file_loaded(utf8_filename
, recent_get_recent_projects());
1120 g_free(locale_filename
);
1121 g_free(utf8_filename
);
1125 static void add_recent_file(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
,
1126 const GtkRecentData
*rdata
)
1128 if (g_queue_find_custom(grf
->recent_queue
, utf8_filename
, (GCompareFunc
) strcmp
) == NULL
)
1131 if (grf
->type
== RECENT_FILE_FILE
&& rdata
)
1133 GtkRecentManager
*manager
= gtk_recent_manager_get_default();
1134 gchar
*uri
= g_filename_to_uri(utf8_filename
, NULL
, NULL
);
1137 gtk_recent_manager_add_full(manager
, uri
, rdata
);
1142 g_queue_push_head(grf
->recent_queue
, g_strdup(utf8_filename
));
1143 if (g_queue_get_length(grf
->recent_queue
) > file_prefs
.mru_length
)
1145 g_free(g_queue_pop_tail(grf
->recent_queue
));
1147 update_recent_menu(grf
);
1149 /* filename already in recent list */
1151 recent_file_loaded(utf8_filename
, grf
);
1155 void ui_add_recent_document(GeanyDocument
*doc
)
1157 /* what are the groups for actually? */
1158 static const gchar
*groups
[2] = {
1162 GtkRecentData rdata
;
1164 /* Prepare the data for gtk_recent_manager_add_full() */
1165 rdata
.display_name
= NULL
;
1166 rdata
.description
= NULL
;
1167 rdata
.mime_type
= doc
->file_type
->mime_type
;
1168 /* if we ain't got no mime-type, fallback to plain text */
1169 if (! rdata
.mime_type
)
1170 rdata
.mime_type
= (gchar
*) "text/plain";
1171 rdata
.app_name
= (gchar
*) "geany";
1172 rdata
.app_exec
= (gchar
*) "geany %u";
1173 rdata
.groups
= (gchar
**) groups
;
1174 rdata
.is_private
= FALSE
;
1176 add_recent_file(doc
->file_name
, recent_get_recent_files(), &rdata
);
1180 void ui_add_recent_project_file(const gchar
*utf8_filename
)
1182 add_recent_file(utf8_filename
, recent_get_recent_projects(), NULL
);
1186 /* Returns: newly allocated string with the UTF-8 menu text. */
1187 gchar
*ui_menu_item_get_text(GtkMenuItem
*menu_item
)
1189 const gchar
*text
= NULL
;
1191 if (gtk_bin_get_child(GTK_BIN(menu_item
)))
1193 GtkWidget
*child
= gtk_bin_get_child(GTK_BIN(menu_item
));
1195 if (GTK_IS_LABEL(child
))
1196 text
= gtk_label_get_text(GTK_LABEL(child
));
1198 /* GTK owns text so it's much safer to return a copy of it in case the memory is reallocated */
1199 return g_strdup(text
);
1203 static gint
find_recent_file_item(gconstpointer list_data
, gconstpointer user_data
)
1205 gchar
*menu_text
= ui_menu_item_get_text(GTK_MENU_ITEM(list_data
));
1208 if (utils_str_equal(menu_text
, user_data
))
1218 static void recent_file_loaded(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
)
1220 GList
*item
, *children
;
1224 /* first reorder the queue */
1225 item
= g_queue_find_custom(grf
->recent_queue
, utf8_filename
, (GCompareFunc
) strcmp
);
1226 g_return_if_fail(item
!= NULL
);
1229 g_queue_remove(grf
->recent_queue
, data
);
1230 g_queue_push_head(grf
->recent_queue
, data
);
1232 /* remove the old menuitem for the filename */
1233 children
= gtk_container_get_children(GTK_CONTAINER(grf
->menubar
));
1234 item
= g_list_find_custom(children
, utf8_filename
, (GCompareFunc
) find_recent_file_item
);
1236 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1237 g_list_free(children
);
1239 if (grf
->toolbar
!= NULL
)
1241 children
= gtk_container_get_children(GTK_CONTAINER(grf
->toolbar
));
1242 item
= g_list_find_custom(children
, utf8_filename
, (GCompareFunc
) find_recent_file_item
);
1244 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1245 g_list_free(children
);
1247 /* now prepend a new menuitem for the filename,
1248 * first for the recent files menu in the menu bar */
1249 tmp
= gtk_menu_item_new_with_label(utf8_filename
);
1250 gtk_widget_show(tmp
);
1251 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf
->menubar
), tmp
);
1252 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1253 /* then for the recent files menu in the tool bar */
1254 if (grf
->toolbar
!= NULL
)
1256 tmp
= gtk_menu_item_new_with_label(utf8_filename
);
1257 gtk_widget_show(tmp
);
1258 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
1259 /* this is a bit ugly, but we need to use gtk_container_add(). Using
1260 * gtk_menu_shell_prepend() doesn't emit GtkContainer's "add" signal which we need in
1261 * GeanyMenubuttonAction */
1262 gtk_menu_reorder_child(GTK_MENU(grf
->toolbar
), tmp
, 0);
1263 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1268 static void update_recent_menu(GeanyRecentFiles
*grf
)
1272 GList
*children
, *item
;
1274 filename
= g_queue_peek_head(grf
->recent_queue
);
1276 /* clean the MRU list before adding an item (menubar) */
1277 children
= gtk_container_get_children(GTK_CONTAINER(grf
->menubar
));
1278 if (g_list_length(children
) > file_prefs
.mru_length
- 1)
1280 item
= g_list_nth(children
, file_prefs
.mru_length
- 1);
1281 while (item
!= NULL
)
1283 if (GTK_IS_MENU_ITEM(item
->data
))
1284 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1285 item
= g_list_next(item
);
1288 g_list_free(children
);
1290 /* create item for the menu bar menu */
1291 tmp
= gtk_menu_item_new_with_label(filename
);
1292 gtk_widget_show(tmp
);
1293 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf
->menubar
), tmp
);
1294 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1296 /* clean the MRU list before adding an item (toolbar) */
1297 if (grf
->toolbar
!= NULL
)
1299 children
= gtk_container_get_children(GTK_CONTAINER(grf
->toolbar
));
1300 if (g_list_length(children
) > file_prefs
.mru_length
- 1)
1302 item
= g_list_nth(children
, file_prefs
.mru_length
- 1);
1303 while (item
!= NULL
)
1305 if (GTK_IS_MENU_ITEM(item
->data
))
1306 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1307 item
= g_list_next(item
);
1310 g_list_free(children
);
1312 /* create item for the tool bar menu */
1313 tmp
= gtk_menu_item_new_with_label(filename
);
1314 gtk_widget_show(tmp
);
1315 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
1316 gtk_menu_reorder_child(GTK_MENU(grf
->toolbar
), tmp
, 0);
1317 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1322 void ui_toggle_editor_features(GeanyUIEditorFeatures feature
)
1326 foreach_document (i
)
1328 GeanyDocument
*doc
= documents
[i
];
1332 case GEANY_EDITOR_SHOW_MARKERS_MARGIN
:
1333 sci_set_symbol_margin(doc
->editor
->sci
, editor_prefs
.show_markers_margin
);
1335 case GEANY_EDITOR_SHOW_LINE_NUMBERS
:
1336 sci_set_line_numbers(doc
->editor
->sci
, editor_prefs
.show_linenumber_margin
, 0);
1338 case GEANY_EDITOR_SHOW_WHITE_SPACE
:
1339 sci_set_visible_white_spaces(doc
->editor
->sci
, editor_prefs
.show_white_space
);
1341 case GEANY_EDITOR_SHOW_LINE_ENDINGS
:
1342 sci_set_visible_eols(doc
->editor
->sci
, editor_prefs
.show_line_endings
);
1344 case GEANY_EDITOR_SHOW_INDENTATION_GUIDES
:
1345 editor_set_indentation_guides(doc
->editor
);
1352 void ui_update_view_editor_menu_items(void)
1354 ignore_callback
= TRUE
;
1355 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_markers_margin1")), editor_prefs
.show_markers_margin
);
1356 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_linenumber_margin1")), editor_prefs
.show_linenumber_margin
);
1357 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_show_white_space1")), editor_prefs
.show_white_space
);
1358 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_show_line_endings1")), editor_prefs
.show_line_endings
);
1359 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_show_indentation_guides1")), editor_prefs
.show_indent_guide
);
1360 ignore_callback
= FALSE
;
1364 /** Creates a GNOME HIG-style frame (with no border and indented child alignment).
1365 * @param label_text The label text.
1366 * @param alignment An address to store the alignment widget pointer.
1367 * @return The frame widget, setting the alignment container for packing child widgets. */
1368 GtkWidget
*ui_frame_new_with_alignment(const gchar
*label_text
, GtkWidget
**alignment
)
1370 GtkWidget
*label
, *align
;
1371 GtkWidget
*frame
= gtk_frame_new(NULL
);
1373 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
1375 align
= gtk_alignment_new(0.5, 0.5, 1, 1);
1376 gtk_container_add(GTK_CONTAINER(frame
), align
);
1377 gtk_alignment_set_padding(GTK_ALIGNMENT(align
), 0, 0, 12, 0);
1379 label
= ui_label_new_bold(label_text
);
1380 gtk_frame_set_label_widget(GTK_FRAME(frame
), label
);
1387 /** Makes a fixed border for dialogs without increasing the button box border.
1388 * @param dialog The parent container for the @c GtkVBox.
1389 * @return The packed @c GtkVBox. */
1390 GtkWidget
*ui_dialog_vbox_new(GtkDialog
*dialog
)
1392 GtkWidget
*vbox
= gtk_vbox_new(FALSE
, 12); /* need child vbox to set a separate border. */
1394 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 6);
1395 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), vbox
);
1400 /** Creates a @c GtkButton with custom text and a stock image similar to
1401 * @c gtk_button_new_from_stock().
1402 * @param stock_id A @c GTK_STOCK_NAME string.
1403 * @param text Button label text, can include mnemonics.
1404 * @return The new @c GtkButton.
1406 GtkWidget
*ui_button_new_with_image(const gchar
*stock_id
, const gchar
*text
)
1408 GtkWidget
*image
, *button
;
1410 button
= gtk_button_new_with_mnemonic(text
);
1411 gtk_widget_show(button
);
1412 image
= gtk_image_new_from_stock(stock_id
, GTK_ICON_SIZE_BUTTON
);
1413 gtk_button_set_image(GTK_BUTTON(button
), image
);
1414 /* note: image is shown by gtk */
1419 /** Creates a @c GtkImageMenuItem with a stock image and a custom label.
1420 * @param stock_id Stock image ID, e.g. @c GTK_STOCK_OPEN.
1421 * @param label Menu item label, can include mnemonics.
1422 * @return The new @c GtkImageMenuItem.
1427 ui_image_menu_item_new(const gchar
*stock_id
, const gchar
*label
)
1429 GtkWidget
*item
= gtk_image_menu_item_new_with_mnemonic(label
);
1430 GtkWidget
*image
= gtk_image_new_from_stock(stock_id
, GTK_ICON_SIZE_MENU
);
1432 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), image
);
1433 gtk_widget_show(image
);
1438 static void entry_clear_icon_release_cb(GtkEntry
*entry
, gint icon_pos
,
1439 GdkEvent
*event
, gpointer data
)
1441 if (event
->button
.button
== 1 && icon_pos
== 1)
1443 gtk_entry_set_text(entry
, "");
1444 gtk_widget_grab_focus(GTK_WIDGET(entry
));
1449 /** Adds a small clear icon to the right end of the passed @a entry.
1450 * A callback to clear the contents of the GtkEntry is automatically added.
1452 * @param entry The GtkEntry object to which the icon should be attached.
1456 void ui_entry_add_clear_icon(GtkEntry
*entry
)
1458 g_object_set(entry
, "secondary-icon-stock", GTK_STOCK_CLEAR
, NULL
);
1459 g_signal_connect(entry
, "icon-release", G_CALLBACK(entry_clear_icon_release_cb
), NULL
);
1463 /* Adds a :activate-backwards signal emitted by default when <Shift>Return is pressed */
1464 void ui_entry_add_activate_backward_signal(GtkEntry
*entry
)
1466 static gboolean installed
= FALSE
;
1468 g_return_if_fail(GTK_IS_ENTRY(entry
));
1470 if (G_UNLIKELY(! installed
))
1472 GtkBindingSet
*binding_set
;
1476 /* try to handle the unexpected case where GTK would already have installed the signal */
1477 if (g_signal_lookup("activate-backward", G_TYPE_FROM_INSTANCE(entry
)))
1479 g_warning("Signal GtkEntry:activate-backward is unexpectedly already installed");
1483 g_signal_new("activate-backward", G_TYPE_FROM_INSTANCE(entry
),
1484 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
, 0, NULL
, NULL
,
1485 g_cclosure_marshal_VOID__VOID
, G_TYPE_NONE
, 0);
1486 binding_set
= gtk_binding_set_by_class(GTK_ENTRY_GET_CLASS(entry
));
1487 gtk_binding_entry_add_signal(binding_set
, GDK_Return
, GDK_SHIFT_MASK
, "activate-backward", 0);
1492 static void add_to_size_group(GtkWidget
*widget
, gpointer size_group
)
1494 g_return_if_fail(GTK_IS_SIZE_GROUP(size_group
));
1495 gtk_size_group_add_widget(GTK_SIZE_GROUP(size_group
), widget
);
1499 /* Copies the spacing and layout of the master GtkHButtonBox and synchronises
1500 * the width of each button box's children.
1501 * Should be called after all child widgets have been packed. */
1502 void ui_hbutton_box_copy_layout(GtkButtonBox
*master
, GtkButtonBox
*copy
)
1504 GtkSizeGroup
*size_group
;
1506 gtk_box_set_spacing(GTK_BOX(copy
), 10);
1507 gtk_button_box_set_layout(copy
, gtk_button_box_get_layout(master
));
1509 /* now we need to put the widest widget from each button box in a size group,
1510 * but we don't know the width before they are drawn, and for different label
1511 * translations the widest widget can vary, so we just add all widgets. */
1512 size_group
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1513 gtk_container_foreach(GTK_CONTAINER(master
), add_to_size_group
, size_group
);
1514 gtk_container_foreach(GTK_CONTAINER(copy
), add_to_size_group
, size_group
);
1515 g_object_unref(size_group
);
1519 static gboolean
tree_model_find_text(GtkTreeModel
*model
,
1520 GtkTreeIter
*iter
, gint column
, const gchar
*text
)
1523 gboolean found
= FALSE
;
1525 if (gtk_tree_model_get_iter_first(model
, iter
))
1529 gtk_tree_model_get(model
, iter
, 0, &combo_text
, -1);
1530 found
= utils_str_equal(combo_text
, text
);
1536 while (gtk_tree_model_iter_next(model
, iter
));
1542 /** Prepends @a text to the drop down list, removing a duplicate element in
1543 * the list if found. Also ensures there are <= @a history_len elements.
1544 * @param combo_entry .
1545 * @param text Text to add, or @c NULL for current entry text.
1546 * @param history_len Max number of items, or @c 0 for default. */
1547 void ui_combo_box_add_to_history(GtkComboBoxEntry
*combo_entry
,
1548 const gchar
*text
, gint history_len
)
1550 GtkComboBox
*combo
= GTK_COMBO_BOX(combo_entry
);
1551 GtkTreeModel
*model
;
1555 if (history_len
<= 0)
1558 text
= gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo
))));
1560 model
= gtk_combo_box_get_model(combo
);
1562 if (tree_model_find_text(model
, &iter
, 0, text
))
1564 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
1566 gtk_combo_box_prepend_text(combo
, text
);
1569 path
= gtk_tree_path_new_from_indices(history_len
, -1);
1570 if (gtk_tree_model_get_iter(model
, &iter
, path
))
1572 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
1574 gtk_tree_path_free(path
);
1578 /* Same as gtk_combo_box_prepend_text(), except that text is only prepended if it not already
1579 * exists in the combo's model. */
1580 void ui_combo_box_prepend_text_once(GtkComboBox
*combo
, const gchar
*text
)
1582 GtkTreeModel
*model
;
1585 model
= gtk_combo_box_get_model(combo
);
1586 if (tree_model_find_text(model
, &iter
, 0, text
))
1587 return; /* don't prepend duplicate */
1589 gtk_combo_box_prepend_text(combo
, text
);
1593 /* Changes the color of the notebook tab text and open files items according to
1594 * document status. */
1595 void ui_update_tab_status(GeanyDocument
*doc
)
1597 const GdkColor
*color
= document_get_status_color(doc
);
1599 /* NULL color will reset to default */
1600 gtk_widget_modify_fg(doc
->priv
->tab_label
, GTK_STATE_NORMAL
, color
);
1601 gtk_widget_modify_fg(doc
->priv
->tab_label
, GTK_STATE_ACTIVE
, color
);
1603 sidebar_openfiles_update(doc
);
1607 static gboolean
tree_model_iter_get_next(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1614 return gtk_tree_model_iter_next(model
, iter
);
1616 path
= gtk_tree_model_get_path(model
, iter
);
1617 result
= gtk_tree_path_prev(path
) && gtk_tree_model_get_iter(model
, iter
, path
);
1618 gtk_tree_path_free(path
);
1623 /* note: the while loop might be more efficient when searching upwards if it
1624 * used tree paths instead of tree iters, but in practice it probably doesn't matter much. */
1625 static gboolean
tree_view_find(GtkTreeView
*treeview
, TVMatchCallback cb
, gboolean down
)
1627 GtkTreeSelection
*treesel
;
1629 GtkTreeModel
*model
;
1631 treesel
= gtk_tree_view_get_selection(treeview
);
1632 if (gtk_tree_selection_get_selected(treesel
, &model
, &iter
))
1634 /* get the next selected item */
1635 if (! tree_model_iter_get_next(model
, &iter
, down
))
1636 return FALSE
; /* no more items */
1638 else /* no selection */
1640 if (! gtk_tree_model_get_iter_first(model
, &iter
))
1641 return TRUE
; /* no items */
1645 gtk_tree_selection_select_iter(treesel
, &iter
);
1647 break; /* found next message */
1649 if (! tree_model_iter_get_next(model
, &iter
, down
))
1650 return FALSE
; /* no more items */
1652 /* scroll item in view */
1653 if (ui_prefs
.msgwindow_visible
)
1655 GtkTreePath
*path
= gtk_tree_model_get_path(
1656 gtk_tree_view_get_model(treeview
), &iter
);
1658 gtk_tree_view_scroll_to_cell(treeview
, path
, NULL
, TRUE
, 0.5, 0.5);
1659 gtk_tree_path_free(path
);
1665 /* Returns FALSE if the treeview has items but no matching next item. */
1666 gboolean
ui_tree_view_find_next(GtkTreeView
*treeview
, TVMatchCallback cb
)
1668 return tree_view_find(treeview
, cb
, TRUE
);
1672 /* Returns FALSE if the treeview has items but no matching next item. */
1673 gboolean
ui_tree_view_find_previous(GtkTreeView
*treeview
, TVMatchCallback cb
)
1675 return tree_view_find(treeview
, cb
, FALSE
);
1680 * Modifies the font of a widget using gtk_widget_modify_font().
1682 * @param widget The widget.
1683 * @param str The font name as expected by pango_font_description_from_string().
1685 void ui_widget_modify_font_from_string(GtkWidget
*widget
, const gchar
*str
)
1687 PangoFontDescription
*pfd
;
1689 pfd
= pango_font_description_from_string(str
);
1690 gtk_widget_modify_font(widget
, pfd
);
1691 pango_font_description_free(pfd
);
1695 /** Creates a @c GtkHBox with @a entry packed into it and an open button which runs a
1696 * file chooser, replacing entry text (if successful) with the path returned from the
1697 * @c GtkFileChooser.
1698 * @note @a entry can be the child of an unparented widget, such as @c GtkComboBoxEntry.
1699 * @param title The file chooser dialog title, or @c NULL.
1700 * @param action The mode of the file chooser.
1701 * @param entry Can be an unpacked @c GtkEntry, or the child of an unpacked widget,
1702 * such as @c GtkComboBoxEntry.
1703 * @return The @c GtkHBox.
1705 /* @see ui_setup_open_button_callback(). */
1706 GtkWidget
*ui_path_box_new(const gchar
*title
, GtkFileChooserAction action
, GtkEntry
*entry
)
1708 GtkWidget
*vbox
, *dirbtn
, *openimg
, *hbox
, *path_entry
;
1710 hbox
= gtk_hbox_new(FALSE
, 6);
1711 path_entry
= GTK_WIDGET(entry
);
1713 /* prevent path_entry being vertically stretched to the height of dirbtn */
1714 vbox
= gtk_vbox_new(FALSE
, 0);
1715 if (gtk_widget_get_parent(path_entry
)) /* entry->parent may be a GtkComboBoxEntry */
1717 GtkWidget
*parent
= gtk_widget_get_parent(path_entry
);
1719 gtk_box_pack_start(GTK_BOX(vbox
), parent
, TRUE
, FALSE
, 0);
1722 gtk_box_pack_start(GTK_BOX(vbox
), path_entry
, TRUE
, FALSE
, 0);
1724 dirbtn
= gtk_button_new();
1725 openimg
= gtk_image_new_from_stock(GTK_STOCK_OPEN
, GTK_ICON_SIZE_BUTTON
);
1726 gtk_container_add(GTK_CONTAINER(dirbtn
), openimg
);
1727 ui_setup_open_button_callback(dirbtn
, title
, action
, entry
);
1729 gtk_box_pack_end(GTK_BOX(hbox
), dirbtn
, FALSE
, FALSE
, 0);
1730 gtk_box_pack_end(GTK_BOX(hbox
), vbox
, TRUE
, TRUE
, 0);
1735 static void ui_path_box_open_clicked(GtkButton
*button
, gpointer user_data
);
1738 /* Setup a GtkButton to run a GtkFileChooser, setting entry text if successful.
1739 * title can be NULL.
1740 * action is the file chooser mode to use. */
1741 void ui_setup_open_button_callback(GtkWidget
*open_btn
, const gchar
*title
,
1742 GtkFileChooserAction action
, GtkEntry
*entry
)
1744 GtkWidget
*path_entry
= GTK_WIDGET(entry
);
1747 g_object_set_data_full(G_OBJECT(open_btn
), "title", g_strdup(title
),
1748 (GDestroyNotify
) g_free
);
1749 g_object_set_data(G_OBJECT(open_btn
), "action", GINT_TO_POINTER(action
));
1750 ui_hookup_widget(open_btn
, path_entry
, "entry");
1751 g_signal_connect(open_btn
, "clicked", G_CALLBACK(ui_path_box_open_clicked
), open_btn
);
1756 static gchar
*run_file_chooser(const gchar
*title
, GtkFileChooserAction action
,
1757 const gchar
*utf8_path
)
1759 GtkWidget
*dialog
= gtk_file_chooser_dialog_new(title
,
1760 GTK_WINDOW(main_widgets
.window
), action
,
1761 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1762 GTK_STOCK_OPEN
, GTK_RESPONSE_OK
, NULL
);
1764 gchar
*ret_path
= NULL
;
1766 gtk_widget_set_name(dialog
, "GeanyDialog");
1767 locale_path
= utils_get_locale_from_utf8(utf8_path
);
1768 if (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
)
1770 if (g_path_is_absolute(locale_path
) && g_file_test(locale_path
, G_FILE_TEST_IS_DIR
))
1771 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), locale_path
);
1773 else if (action
== GTK_FILE_CHOOSER_ACTION_OPEN
)
1775 if (g_path_is_absolute(locale_path
))
1776 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog
), locale_path
);
1778 g_free(locale_path
);
1780 if (gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_OK
)
1784 dir_locale
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
1785 ret_path
= utils_get_utf8_from_locale(dir_locale
);
1788 gtk_widget_destroy(dialog
);
1794 static void ui_path_box_open_clicked(GtkButton
*button
, gpointer user_data
)
1796 GtkWidget
*path_box
= GTK_WIDGET(user_data
);
1797 GtkFileChooserAction action
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(path_box
), "action"));
1798 GtkEntry
*entry
= g_object_get_data(G_OBJECT(path_box
), "entry");
1799 const gchar
*title
= g_object_get_data(G_OBJECT(path_box
), "title");
1800 gchar
*utf8_path
= NULL
;
1802 /* TODO: extend for other actions */
1803 g_return_if_fail(action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
||
1804 action
== GTK_FILE_CHOOSER_ACTION_OPEN
);
1807 title
= (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
) ?
1808 _("Select Folder") : _("Select File");
1810 if (action
== GTK_FILE_CHOOSER_ACTION_OPEN
)
1813 utf8_path
= win32_show_file_dialog(GTK_WINDOW(ui_widgets
.prefs_dialog
), title
,
1814 gtk_entry_get_text(GTK_ENTRY(entry
)));
1816 utf8_path
= run_file_chooser(title
, action
, gtk_entry_get_text(GTK_ENTRY(entry
)));
1819 else if (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
)
1821 gchar
*path
= g_path_get_dirname(gtk_entry_get_text(GTK_ENTRY(entry
)));
1823 utf8_path
= win32_show_folder_dialog(ui_widgets
.prefs_dialog
, title
,
1824 gtk_entry_get_text(GTK_ENTRY(entry
)));
1826 utf8_path
= run_file_chooser(title
, action
, path
);
1831 if (utf8_path
!= NULL
)
1833 gtk_entry_set_text(GTK_ENTRY(entry
), utf8_path
);
1839 void ui_statusbar_showhide(gboolean state
)
1841 /* handle statusbar visibility */
1844 gtk_widget_show(ui_widgets
.statusbar
);
1845 ui_update_statusbar(NULL
, -1);
1848 gtk_widget_hide(ui_widgets
.statusbar
);
1852 /** Packs all @c GtkWidgets passed after the row argument into a table, using
1853 * one widget per cell. The first widget is not expanded as the table grows,
1854 * as this is usually a label.
1856 * @param row The row number of the table.
1858 void ui_table_add_row(GtkTable
*table
, gint row
, ...)
1864 va_start(args
, row
);
1865 for (i
= 0; (widget
= va_arg(args
, GtkWidget
*), widget
!= NULL
); i
++)
1867 gint options
= (i
== 0) ? GTK_FILL
: GTK_EXPAND
| GTK_FILL
;
1869 gtk_table_attach(GTK_TABLE(table
), widget
, i
, i
+ 1, row
, row
+ 1,
1876 static void on_config_file_clicked(GtkWidget
*widget
, gpointer user_data
)
1878 const gchar
*file_name
= user_data
;
1879 GeanyFiletype
*ft
= NULL
;
1881 if (strstr(file_name
, G_DIR_SEPARATOR_S
"filetypes."))
1882 ft
= filetypes
[GEANY_FILETYPES_CONF
];
1884 if (g_file_test(file_name
, G_FILE_TEST_EXISTS
))
1885 document_open_file(file_name
, FALSE
, ft
, NULL
);
1888 gchar
*utf8_filename
= utils_get_utf8_from_locale(file_name
);
1889 gchar
*base_name
= g_path_get_basename(file_name
);
1890 gchar
*global_file
= g_build_filename(app
->datadir
, base_name
, NULL
);
1891 gchar
*global_content
= NULL
;
1893 /* if the requested file doesn't exist in the user's config dir, try loading the file
1894 * from the global data directory and use its contents for the newly created file */
1895 if (g_file_test(global_file
, G_FILE_TEST_EXISTS
))
1896 g_file_get_contents(global_file
, &global_content
, NULL
, NULL
);
1898 document_new_file(utf8_filename
, ft
, global_content
);
1900 utils_free_pointers(4, utf8_filename
, base_name
, global_file
, global_content
, NULL
);
1905 static void free_on_closure_notify(gpointer data
, GClosure
*closure
)
1911 /* @note You should connect to the "document-save" signal yourself to detect
1912 * if the user has just saved the config file, reloading it. */
1913 void ui_add_config_file_menu_item(const gchar
*real_path
, const gchar
*label
, GtkContainer
*parent
)
1918 parent
= GTK_CONTAINER(widgets
.config_files_menu
);
1924 base_name
= g_path_get_basename(real_path
);
1925 item
= gtk_menu_item_new_with_label(base_name
);
1929 item
= gtk_menu_item_new_with_mnemonic(label
);
1931 gtk_widget_show(item
);
1932 gtk_container_add(parent
, item
);
1933 g_signal_connect_data(item
, "activate", G_CALLBACK(on_config_file_clicked
),
1934 g_strdup(real_path
), free_on_closure_notify
, 0);
1938 static gboolean
sort_menu(gpointer data
)
1940 ui_menu_sort_by_label(GTK_MENU(data
));
1945 static void create_config_files_menu(void)
1947 GtkWidget
*menu
, *item
;
1949 widgets
.config_files_menu
= menu
= gtk_menu_new();
1951 item
= ui_lookup_widget(main_widgets
.window
, "configuration_files1");
1952 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
1954 /* sort menu after all items added */
1955 g_idle_add(sort_menu
, widgets
.config_files_menu
);
1959 void ui_init_stock_items(void)
1961 GtkIconSet
*icon_set
;
1962 GtkIconFactory
*factory
= gtk_icon_factory_new();
1965 GtkStockItem items
[] =
1967 { GEANY_STOCK_SAVE_ALL
, N_("Save All"), 0, 0, GETTEXT_PACKAGE
},
1968 { GEANY_STOCK_CLOSE_ALL
, N_("Close All"), 0, 0, GETTEXT_PACKAGE
},
1969 { GEANY_STOCK_BUILD
, N_("Build"), 0, 0, GETTEXT_PACKAGE
}
1972 len
= G_N_ELEMENTS(items
);
1973 for (i
= 0; i
< len
; i
++)
1975 pb
= ui_new_pixbuf_from_stock(items
[i
].stock_id
);
1976 icon_set
= gtk_icon_set_new_from_pixbuf(pb
);
1978 gtk_icon_factory_add(factory
, items
[i
].stock_id
, icon_set
);
1980 gtk_icon_set_unref(icon_set
);
1983 gtk_stock_add((GtkStockItem
*) items
, len
);
1984 gtk_icon_factory_add_default(factory
);
1985 g_object_unref(factory
);
1989 void ui_init_toolbar_widgets(void)
1991 widgets
.save_buttons
[1] = toolbar_get_widget_by_name("Save");
1992 widgets
.save_buttons
[3] = toolbar_get_widget_by_name("SaveAll");
1993 widgets
.redo_items
[2] = toolbar_get_widget_by_name("Redo");
1994 widgets
.undo_items
[2] = toolbar_get_widget_by_name("Undo");
1998 void ui_swap_sidebar_pos(void)
2000 GtkWidget
*pane
= ui_lookup_widget(main_widgets
.window
, "hpaned1");
2001 GtkWidget
*left
= gtk_paned_get_child1(GTK_PANED(pane
));
2002 GtkWidget
*right
= gtk_paned_get_child2(GTK_PANED(pane
));
2005 g_object_ref(right
);
2006 gtk_container_remove (GTK_CONTAINER (pane
), left
);
2007 gtk_container_remove (GTK_CONTAINER (pane
), right
);
2008 /* only scintilla notebook should expand */
2009 gtk_paned_pack1(GTK_PANED(pane
), right
, right
== main_widgets
.notebook
, TRUE
);
2010 gtk_paned_pack2(GTK_PANED(pane
), left
, left
== main_widgets
.notebook
, TRUE
);
2011 g_object_unref(left
);
2012 g_object_unref(right
);
2014 gtk_paned_set_position(GTK_PANED(pane
), pane
->allocation
.width
2015 - gtk_paned_get_position(GTK_PANED(pane
)));
2019 static void init_recent_files(void)
2021 GtkWidget
*toolbar_recent_files_menu
;
2023 /* add recent files to the File menu */
2024 ui_widgets
.recent_files_menuitem
= ui_lookup_widget(main_widgets
.window
, "recent_files1");
2025 ui_widgets
.recent_files_menu_menubar
= gtk_menu_new();
2026 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_widgets
.recent_files_menuitem
),
2027 ui_widgets
.recent_files_menu_menubar
);
2029 /* add recent files to the toolbar Open button */
2030 toolbar_recent_files_menu
= gtk_menu_new();
2031 g_object_ref(toolbar_recent_files_menu
);
2032 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
2033 toolbar_get_action_by_name("Open")), toolbar_recent_files_menu
);
2037 static void ui_menu_move(GtkWidget
*menu
, GtkWidget
*old
, GtkWidget
*new)
2040 gtk_menu_item_set_submenu(GTK_MENU_ITEM(old
), NULL
);
2041 gtk_menu_item_set_submenu(GTK_MENU_ITEM(new), menu
);
2042 g_object_unref(menu
);
2046 typedef struct GeanySharedMenu
2049 const gchar
*menubar_item
;
2050 const gchar
*popup_item
;
2054 #define foreach_menu(item, array) \
2055 for (item = array; item->menu; item++)
2057 static void on_editor_menu_show(GtkWidget
*widget
, GeanySharedMenu
*items
)
2059 GeanySharedMenu
*item
;
2061 foreach_menu(item
, items
)
2063 GtkWidget
*popup
= ui_lookup_widget(main_widgets
.editor_menu
, item
->popup_item
);
2064 GtkWidget
*bar
= ui_lookup_widget(main_widgets
.window
, item
->menubar_item
);
2065 GtkWidget
*menu
= ui_lookup_widget(main_widgets
.window
, item
->menu
);
2067 ui_menu_move(menu
, bar
, popup
);
2072 static void on_editor_menu_hide(GtkWidget
*widget
, GeanySharedMenu
*items
)
2074 GeanySharedMenu
*item
;
2076 foreach_menu(item
, items
)
2078 GtkWidget
*popup
= ui_lookup_widget(main_widgets
.editor_menu
, item
->popup_item
);
2079 GtkWidget
*bar
= ui_lookup_widget(main_widgets
.window
, item
->menubar_item
);
2080 GtkWidget
*menu
= ui_lookup_widget(main_widgets
.window
, item
->menu
);
2082 ui_menu_move(menu
, popup
, bar
);
2087 /* Currently ui_init() is called before keyfile.c stash group code is initialized,
2088 * so this is called after that's done. */
2089 void ui_init_prefs(void)
2091 StashGroup
*group
= stash_group_new(PACKAGE
);
2094 configuration_add_various_pref_group(group
);
2096 stash_group_add_boolean(group
, &interface_prefs
.show_symbol_list_expanders
,
2097 "show_symbol_list_expanders", TRUE
);
2098 stash_group_add_boolean(group
, &interface_prefs
.compiler_tab_autoscroll
,
2099 "compiler_tab_autoscroll", TRUE
);
2100 stash_group_add_boolean(group
, &ui_prefs
.allow_always_save
,
2101 "allow_always_save", FALSE
);
2102 stash_group_add_string(group
, &statusbar_template
,
2103 "statusbar_template", "");
2104 stash_group_add_boolean(group
, &ui_prefs
.new_document_after_close
,
2105 "new_document_after_close", FALSE
);
2106 stash_group_add_boolean(group
, &interface_prefs
.msgwin_status_visible
,
2107 "msgwin_status_visible", TRUE
);
2108 stash_group_add_boolean(group
, &interface_prefs
.msgwin_compiler_visible
,
2109 "msgwin_compiler_visible", TRUE
);
2110 stash_group_add_boolean(group
, &interface_prefs
.msgwin_messages_visible
,
2111 "msgwin_messages_visible", TRUE
);
2112 stash_group_add_boolean(group
, &interface_prefs
.msgwin_scribble_visible
,
2113 "msgwin_scribble_visible", TRUE
);
2117 /* Used to find out the name of the GtkBuilder retrieved object since
2118 * some objects will be GTK_IS_BUILDABLE() and use the GtkBuildable
2119 * 'name' property for that and those that don't implement GtkBuildable
2120 * will have a "gtk-builder-name" stored in the GObject's data list. */
2121 static const gchar
*ui_guess_object_name(GObject
*obj
)
2123 const gchar
*name
= NULL
;
2125 g_return_val_if_fail(G_IS_OBJECT(obj
), NULL
);
2127 if (GTK_IS_BUILDABLE(obj
))
2128 name
= gtk_buildable_get_name(GTK_BUILDABLE(obj
));
2130 name
= g_object_get_data(obj
, "gtk-builder-name");
2138 /* Compatibility functions */
2139 GtkWidget
*create_edit_menu1(void)
2145 GtkWidget
*create_prefs_dialog(void)
2147 return prefs_dialog
;
2151 GtkWidget
*create_project_dialog(void)
2153 return project_dialog
;
2157 GtkWidget
*create_toolbar_popup_menu1(void)
2159 return toolbar_popup_menu1
;
2163 GtkWidget
*create_window1(void)
2169 static GtkWidget
*ui_get_top_parent(GtkWidget
*widget
)
2173 g_return_val_if_fail(GTK_IS_WIDGET(widget
), NULL
);
2177 if (GTK_IS_MENU(widget
))
2178 parent
= gtk_menu_get_attach_widget(GTK_MENU(widget
));
2180 parent
= gtk_widget_get_parent(widget
);
2182 parent
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), "GladeParentKey");
2192 void ui_init_builder(void)
2194 gchar
*interface_file
;
2197 GSList
*iter
, *all_objects
;
2198 GtkWidget
*widget
, *toplevel
;
2200 /* prevent function from being called twice */
2201 if (GTK_IS_BUILDER(builder
))
2204 builder
= gtk_builder_new();
2207 g_error("Failed to initialize the user-interface");
2211 gtk_builder_set_translation_domain(builder
, GETTEXT_PACKAGE
);
2214 interface_file
= g_build_filename(app
->datadir
, "geany.glade", NULL
);
2215 if (! gtk_builder_add_from_file(builder
, interface_file
, &error
))
2217 /* Show the user this message so they know WTF happened */
2218 dialogs_show_msgbox_with_secondary(GTK_MESSAGE_ERROR
,
2219 _("Geany cannot start!"), error
->message
);
2221 g_error("Cannot create user-interface: %s", error
->message
);
2222 g_error_free(error
);
2223 g_free(interface_file
);
2224 g_object_unref(builder
);
2227 g_free(interface_file
);
2229 gtk_builder_connect_signals(builder
, NULL
);
2231 edit_menu1
= GTK_WIDGET(gtk_builder_get_object(builder
, "edit_menu1"));
2232 prefs_dialog
= GTK_WIDGET(gtk_builder_get_object(builder
, "prefs_dialog"));
2233 project_dialog
= GTK_WIDGET(gtk_builder_get_object(builder
, "project_dialog"));
2234 toolbar_popup_menu1
= GTK_WIDGET(gtk_builder_get_object(builder
, "toolbar_popup_menu1"));
2235 window1
= GTK_WIDGET(gtk_builder_get_object(builder
, "window1"));
2237 g_object_set_data(G_OBJECT(edit_menu1
), "edit_menu1", edit_menu1
);
2238 g_object_set_data(G_OBJECT(prefs_dialog
), "prefs_dialog", prefs_dialog
);
2239 g_object_set_data(G_OBJECT(project_dialog
), "project_dialog", project_dialog
);
2240 g_object_set_data(G_OBJECT(toolbar_popup_menu1
), "toolbar_popup_menu1", toolbar_popup_menu1
);
2241 g_object_set_data(G_OBJECT(window1
), "window1", window1
);
2243 all_objects
= gtk_builder_get_objects(builder
);
2244 for (iter
= all_objects
; iter
!= NULL
; iter
= g_slist_next(iter
))
2246 if (! GTK_IS_WIDGET(iter
->data
))
2249 widget
= GTK_WIDGET(iter
->data
);
2251 name
= ui_guess_object_name(G_OBJECT(widget
));
2254 g_warning("Unable to get name from GtkBuilder object");
2258 toplevel
= ui_get_top_parent(widget
);
2260 ui_hookup_widget(toplevel
, widget
, name
);
2262 g_slist_free(all_objects
);
2268 init_recent_files();
2270 ui_widgets
.statusbar
= ui_lookup_widget(main_widgets
.window
, "statusbar");
2271 ui_widgets
.print_page_setup
= ui_lookup_widget(main_widgets
.window
, "page_setup1");
2273 main_widgets
.progressbar
= progress_bar_create();
2275 /* current word sensitive items */
2276 widgets
.popup_goto_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "goto_tag_definition2");
2277 widgets
.popup_goto_items
[1] = ui_lookup_widget(main_widgets
.editor_menu
, "context_action1");
2278 widgets
.popup_goto_items
[2] = ui_lookup_widget(main_widgets
.editor_menu
, "find_usage2");
2279 widgets
.popup_goto_items
[3] = ui_lookup_widget(main_widgets
.editor_menu
, "find_document_usage2");
2281 widgets
.popup_copy_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "cut1");
2282 widgets
.popup_copy_items
[1] = ui_lookup_widget(main_widgets
.editor_menu
, "copy1");
2283 widgets
.popup_copy_items
[2] = ui_lookup_widget(main_widgets
.editor_menu
, "delete1");
2284 widgets
.menu_copy_items
[0] = ui_lookup_widget(main_widgets
.window
, "menu_cut1");
2285 widgets
.menu_copy_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_copy1");
2286 widgets
.menu_copy_items
[2] = ui_lookup_widget(main_widgets
.window
, "menu_delete1");
2287 widgets
.menu_insert_include_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "insert_include1");
2288 widgets
.menu_insert_include_items
[1] = ui_lookup_widget(main_widgets
.window
, "insert_include2");
2289 widgets
.save_buttons
[0] = ui_lookup_widget(main_widgets
.window
, "menu_save1");
2290 widgets
.save_buttons
[2] = ui_lookup_widget(main_widgets
.window
, "menu_save_all1");
2291 widgets
.redo_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "redo1");
2292 widgets
.redo_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_redo2");
2293 widgets
.undo_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "undo1");
2294 widgets
.undo_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_undo2");
2296 /* reparent context submenus as needed */
2298 GeanySharedMenu arr
[] = {
2299 {"commands2_menu", "commands2", "commands1"},
2300 {"menu_format1_menu", "menu_format1", "menu_format2"},
2301 {"more1_menu", "more1", "search2"},
2304 static GeanySharedMenu items
[G_N_ELEMENTS(arr
)];
2306 memcpy(items
, arr
, sizeof(arr
));
2307 g_signal_connect(main_widgets
.editor_menu
, "show", G_CALLBACK(on_editor_menu_show
), items
);
2308 g_signal_connect(main_widgets
.editor_menu
, "hide", G_CALLBACK(on_editor_menu_hide
), items
);
2311 ui_init_toolbar_widgets();
2312 init_document_widgets();
2313 create_config_files_menu();
2317 void ui_finalize_builder(void)
2319 if (GTK_IS_BUILDER(builder
))
2320 g_object_unref(builder
);
2322 /* cleanup refs lingering even after GtkBuilder is destroyed */
2323 if (GTK_IS_WIDGET(edit_menu1
))
2324 gtk_widget_destroy(edit_menu1
);
2325 if (GTK_IS_WIDGET(prefs_dialog
))
2326 gtk_widget_destroy(prefs_dialog
);
2327 if (GTK_IS_WIDGET(project_dialog
))
2328 gtk_widget_destroy(project_dialog
);
2329 if (GTK_IS_WIDGET(toolbar_popup_menu1
))
2330 gtk_widget_destroy(toolbar_popup_menu1
);
2331 if (GTK_IS_WIDGET(window1
))
2332 gtk_widget_destroy(window1
);
2336 void ui_finalize(void)
2338 g_free(statusbar_template
);
2342 static void auto_separator_update(GeanyAutoSeparator
*autosep
)
2344 g_return_if_fail(autosep
->ref_count
>= 0);
2346 if (autosep
->widget
)
2347 ui_widget_show_hide(autosep
->widget
, autosep
->ref_count
> 0);
2351 static void on_auto_separator_item_show_hide(GtkWidget
*widget
, gpointer user_data
)
2353 GeanyAutoSeparator
*autosep
= user_data
;
2355 if (GTK_WIDGET_VISIBLE(widget
))
2356 autosep
->ref_count
++;
2358 autosep
->ref_count
--;
2359 auto_separator_update(autosep
);
2363 static void on_auto_separator_item_destroy(GtkWidget
*widget
, gpointer user_data
)
2365 GeanyAutoSeparator
*autosep
= user_data
;
2367 /* GTK_WIDGET_VISIBLE won't work now the widget is being destroyed,
2368 * so assume widget was visible */
2369 autosep
->ref_count
--;
2370 autosep
->ref_count
= MAX(autosep
->ref_count
, 0);
2371 auto_separator_update(autosep
);
2375 /* Show the separator widget if @a item or another is visible. */
2376 /* Note: This would be neater taking a widget argument, setting a "visible-count"
2377 * property, and using reference counting to keep the widget alive whilst its visible group
2379 void ui_auto_separator_add_ref(GeanyAutoSeparator
*autosep
, GtkWidget
*item
)
2381 /* set widget ptr NULL when widget destroyed */
2382 if (autosep
->ref_count
== 0)
2383 g_signal_connect(autosep
->widget
, "destroy",
2384 G_CALLBACK(gtk_widget_destroyed
), &autosep
->widget
);
2386 if (GTK_WIDGET_VISIBLE(item
))
2388 autosep
->ref_count
++;
2389 auto_separator_update(autosep
);
2391 g_signal_connect(item
, "show", G_CALLBACK(on_auto_separator_item_show_hide
), autosep
);
2392 g_signal_connect(item
, "hide", G_CALLBACK(on_auto_separator_item_show_hide
), autosep
);
2393 g_signal_connect(item
, "destroy", G_CALLBACK(on_auto_separator_item_destroy
), autosep
);
2398 * Sets @a text as the contents of the tooltip for @a widget.
2400 * @param widget The widget the tooltip should be set for.
2401 * @param text The text for the tooltip.
2404 * @deprecated 0.21 use gtk_widget_set_tooltip_text() instead
2406 void ui_widget_set_tooltip_text(GtkWidget
*widget
, const gchar
*text
)
2408 gtk_widget_set_tooltip_text(widget
, text
);
2412 /** Returns a widget from a name in a component, usually created by Glade.
2413 * Call it with the toplevel widget in the component (i.e. a window/dialog),
2414 * or alternatively any widget in the component, and the name of the widget
2415 * you want returned.
2416 * @param widget Widget with the @a widget_name property set.
2417 * @param widget_name Name to lookup.
2418 * @return The widget found.
2419 * @see ui_hookup_widget().
2423 GtkWidget
*ui_lookup_widget(GtkWidget
*widget
, const gchar
*widget_name
)
2425 GtkWidget
*parent
, *found_widget
;
2427 g_return_val_if_fail(widget
!= NULL
, NULL
);
2428 g_return_val_if_fail(widget_name
!= NULL
, NULL
);
2432 if (GTK_IS_MENU(widget
))
2433 parent
= gtk_menu_get_attach_widget(GTK_MENU(widget
));
2435 parent
= widget
->parent
;
2437 parent
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), "GladeParentKey");
2443 found_widget
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), widget_name
);
2444 if (G_UNLIKELY(found_widget
== NULL
))
2445 g_warning("Widget not found: %s", widget_name
);
2446 return found_widget
;
2450 /* wraps gtk_builder_get_object()
2451 * unlike ui_lookup_widget(), it does only support getting object created from the main
2452 * UI file, but it can fetch any object, not only widgets */
2453 gpointer
ui_builder_get_object (const gchar
*name
)
2455 return gtk_builder_get_object (builder
, name
);
2460 static guint progress_bar_timer_id
= 0;
2463 static GtkWidget
*progress_bar_create(void)
2465 GtkWidget
*bar
= gtk_progress_bar_new();
2467 /* Set the progressbar's height to 1 to fit it in the statusbar */
2468 gtk_widget_set_size_request(bar
, -1, 1);
2469 gtk_box_pack_start (GTK_BOX(ui_widgets
.statusbar
), bar
, FALSE
, FALSE
, 3);
2475 static gboolean
progress_bar_pulse(gpointer data
)
2477 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(main_widgets
.progressbar
));
2484 * Starts a constantly pulsing progressbar in the right corner of the statusbar
2485 * (if the statusbar is visible). This is a convenience function which adds a timer to
2486 * pulse the progressbar constantly until ui_progress_bar_stop() is called.
2487 * You can use this function when you have time consuming asynchronous operation and want to
2488 * display some activity in the GUI and when you don't know about detailed progress steps.
2489 * The progressbar widget is hidden by default when it is not active. This function and
2490 * ui_progress_bar_stop() will show and hide it automatically for you.
2492 * You can also access the progressbar widget directly using @c geany->main_widgets->progressbar
2493 * and use the GtkProgressBar API to set discrete fractions to display better progress information.
2494 * In this case, you need to show and hide the widget yourself. You can find some example code
2495 * in @c src/printing.c.
2497 * @param text The text to be shown as the progress bar label or NULL to leave it empty.
2501 void ui_progress_bar_start(const gchar
*text
)
2503 g_return_if_fail(progress_bar_timer_id
== 0);
2505 if (! interface_prefs
.statusbar_visible
)
2508 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets
.progressbar
), text
);
2510 progress_bar_timer_id
= g_timeout_add(200, progress_bar_pulse
, NULL
);
2512 gtk_widget_show(GTK_WIDGET(main_widgets
.progressbar
));
2516 /** Stops a running progress bar and hides the widget again.
2520 void ui_progress_bar_stop(void)
2522 gtk_widget_hide(GTK_WIDGET(main_widgets
.progressbar
));
2524 if (progress_bar_timer_id
!= 0)
2526 g_source_remove(progress_bar_timer_id
);
2527 progress_bar_timer_id
= 0;
2532 static gint
compare_menu_item_labels(gconstpointer a
, gconstpointer b
)
2534 GtkMenuItem
*item_a
= GTK_MENU_ITEM(a
);
2535 GtkMenuItem
*item_b
= GTK_MENU_ITEM(b
);
2539 sa
= ui_menu_item_get_text(item_a
);
2540 sb
= ui_menu_item_get_text(item_b
);
2541 result
= utils_str_casecmp(sa
, sb
);
2548 /* Currently @a menu should contain only GtkMenuItems with labels. */
2549 void ui_menu_sort_by_label(GtkMenu
*menu
)
2551 GList
*list
= gtk_container_get_children(GTK_CONTAINER(menu
));
2555 list
= g_list_sort(list
, compare_menu_item_labels
);
2557 foreach_list(node
, list
)
2559 gtk_menu_reorder_child(menu
, node
->data
, pos
);
2566 void ui_label_set_markup(GtkLabel
*label
, const gchar
*format
, ...)
2571 va_start(a
, format
);
2572 text
= g_strdup_vprintf(format
, a
);
2575 gtk_label_set_text(label
, text
);
2576 gtk_label_set_use_markup(label
, TRUE
);
2581 GtkWidget
*ui_label_new_bold(const gchar
*text
)
2586 label_text
= g_markup_escape_text(text
, -1);
2587 label
= gtk_label_new(NULL
);
2588 ui_label_set_markup(GTK_LABEL(label
), "<b>%s</b>", label_text
);
2594 /** Adds a list of document items to @a menu.
2596 * @param active Which document to highlight, or @c NULL.
2597 * @param callback is used for each menu item's @c "activate" signal and will be passed
2598 * the corresponding document pointer as @c user_data.
2599 * @warning You should check @c doc->is_valid in the callback.
2601 void ui_menu_add_document_items(GtkMenu
*menu
, GeanyDocument
*active
, GCallback callback
)
2603 ui_menu_add_document_items_sorted(menu
, active
, callback
, NULL
);
2607 /** Adds a list of document items to @a menu.
2609 * @a compare_func might be NULL to not sort the documents in the menu. In this case,
2610 * the order of the document tabs is used.
2612 * See document_sort_by_display_name() for an example sort function.
2615 * @param active Which document to highlight, or @c NULL.
2616 * @param callback is used for each menu item's @c "activate" signal and will be passed
2617 * the corresponding document pointer as @c user_data.
2618 * @param compare_func is used to sort the list. Might be @c NULL to not sort the list.
2619 * @warning You should check @c doc->is_valid in the callback.
2621 void ui_menu_add_document_items_sorted(GtkMenu
*menu
, GeanyDocument
*active
,
2622 GCallback callback
, GCompareFunc compare_func
)
2624 GtkWidget
*menu_item
, *menu_item_label
, *image
;
2625 const GdkColor
*color
;
2628 gchar
*base_name
, *label
;
2629 GPtrArray
*sorted_documents
;
2631 len
= (guint
) gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
));
2633 sorted_documents
= g_ptr_array_sized_new(len
);
2634 /* copy the documents_array into the new one */
2637 g_ptr_array_add(sorted_documents
, documents
[i
]);
2639 if (compare_func
== NULL
)
2640 compare_func
= document_compare_by_tab_order
;
2642 /* and now sort it */
2643 g_ptr_array_sort(sorted_documents
, compare_func
);
2645 for (i
= 0; i
< sorted_documents
->len
; i
++)
2647 doc
= g_ptr_array_index(sorted_documents
, i
);
2649 base_name
= g_path_get_basename(DOC_FILENAME(doc
));
2650 menu_item
= gtk_image_menu_item_new_with_label(base_name
);
2651 image
= gtk_image_new_from_pixbuf(doc
->file_type
->icon
);
2652 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
), image
);
2654 gtk_widget_show(menu_item
);
2655 gtk_container_add(GTK_CONTAINER(menu
), menu_item
);
2656 g_signal_connect(menu_item
, "activate", callback
, doc
);
2658 color
= document_get_status_color(doc
);
2659 menu_item_label
= gtk_bin_get_child(GTK_BIN(menu_item
));
2660 gtk_widget_modify_fg(menu_item_label
, GTK_STATE_NORMAL
, color
);
2661 gtk_widget_modify_fg(menu_item_label
, GTK_STATE_ACTIVE
, color
);
2665 label
= g_markup_escape_text(base_name
, -1);
2666 ui_label_set_markup(GTK_LABEL(menu_item_label
), "<b>%s</b>", label
);
2672 g_ptr_array_free(sorted_documents
, TRUE
);
2676 /** Checks whether the passed @a keyval is the Enter or Return key.
2677 * There are three different Enter/Return key values
2678 * (@c GDK_Return, @c GDK_ISO_Enter, @c GDK_KP_Enter).
2679 * This is just a convenience function.
2680 * @param keyval A keyval.
2681 * @return @c TRUE if @a keyval is the one of the Enter/Return key values, otherwise @c FALSE.
2683 gboolean
ui_is_keyval_enter_or_return(guint keyval
)
2685 return (keyval
== GDK_Return
|| keyval
== GDK_ISO_Enter
|| keyval
== GDK_KP_Enter
);
2689 /** Reads an integer from the GTK default settings registry
2690 * (see http://library.gnome.org/devel/gtk/stable/GtkSettings.html).
2691 * @param property_name The property to read.
2692 * @param default_value The default value in case the value could not be read.
2693 * @return The value for the property if it exists, otherwise the @a default_value.
2695 gint
ui_get_gtk_settings_integer(const gchar
*property_name
, gint default_value
)
2697 if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(
2698 gtk_settings_get_default())), property_name
))
2701 g_object_get(G_OBJECT(gtk_settings_get_default()), property_name
, &value
, NULL
);
2705 return default_value
;
2709 void ui_editable_insert_text_callback(GtkEditable
*editable
, gchar
*new_text
,
2710 gint new_text_len
, gint
*position
, gpointer data
)
2712 gboolean first
= position
!= NULL
&& *position
== 0;
2715 if (new_text_len
== -1)
2716 new_text_len
= (gint
) strlen(new_text
);
2718 for (i
= 0; i
< new_text_len
; i
++, new_text
++)
2720 if ((!first
|| !strchr("+-", *new_text
)) && !isdigit(*new_text
))
2722 g_signal_stop_emission_by_name(editable
, "insert-text");
2730 /* gets the icon that applies to a particular MIME type */
2731 GdkPixbuf
*ui_get_mime_icon(const gchar
*mime_type
, GtkIconSize size
)
2733 GdkPixbuf
*icon
= NULL
;
2737 GtkIconTheme
*theme
;
2740 if (!gtk_icon_size_lookup(size
, &real_size
, NULL
))
2742 g_return_val_if_reached(NULL
);
2744 ctype
= g_content_type_from_mime_type(mime_type
);
2747 gicon
= g_content_type_get_icon(ctype
);
2748 theme
= gtk_icon_theme_get_default();
2749 info
= gtk_icon_theme_lookup_by_gicon(theme
, gicon
, real_size
, 0);
2750 g_object_unref(gicon
);
2755 icon
= gtk_icon_info_load_icon(info
, NULL
);
2756 gtk_icon_info_free(info
);
2760 /* fallback for builds with GIO < 2.18 or if icon lookup failed, like it might happen on Windows */
2763 const gchar
*stock_id
= GTK_STOCK_FILE
;
2764 GtkIconSet
*icon_set
;
2766 if (strstr(mime_type
, "directory"))
2767 stock_id
= GTK_STOCK_DIRECTORY
;
2769 icon_set
= gtk_icon_factory_lookup_default(stock_id
);
2772 icon
= gtk_icon_set_render_icon(icon_set
, gtk_widget_get_default_style(),
2773 gtk_widget_get_default_direction(),
2774 GTK_STATE_NORMAL
, size
, NULL
, NULL
);
2781 void ui_focus_current_document(void)
2783 GeanyDocument
*doc
= document_get_current();
2786 document_grab_focus(doc
);
2790 /** Finds the label text associated with stock_id
2791 * @param stock_id stock_id to lookup e.g. @c GTK_STOCK_OPEN.
2792 * @return The label text for stock
2793 * @since Geany 1.22 */
2794 const gchar
*ui_lookup_stock_label(const gchar
*stock_id
)
2798 if (gtk_stock_lookup(stock_id
, &item
))
2801 g_warning("No stock id '%s'!", stock_id
);