2 * ui_utils.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2006-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 * User Interface general utility functions.
31 #include <gdk/gdkkeysyms.h>
35 #include "sciwrappers.h"
37 #include "documentprivate.h"
38 #include "filetypes.h"
40 #include "msgwindow.h"
42 #include "callbacks.h"
43 #include "encodings.h"
52 #include "geanymenubuttonaction.h"
55 GeanyInterfacePrefs interface_prefs
;
56 GeanyMainWidgets main_widgets
;
63 /* pointers to widgets only sensitive when there is at least one document, the pointers can
64 * also be GtkAction objects, so check each pointer before using it */
65 GPtrArray
*document_buttons
;
66 GtkWidget
*menu_insert_include_items
[2];
67 GtkWidget
*popup_goto_items
[4];
68 GtkWidget
*popup_copy_items
[3];
69 GtkWidget
*menu_copy_items
[3];
70 GtkWidget
*redo_items
[3];
71 GtkWidget
*undo_items
[3];
72 GtkWidget
*save_buttons
[4];
73 GtkWidget
*config_files_menu
;
74 GtkWidget
*commands_menu
;
75 GtkWidget
*format_menu
;
91 void (*activate_cb
)(GtkMenuItem
*, gpointer
);
95 static void update_recent_menu(GeanyRecentFiles
*grf
);
96 static void recent_file_loaded(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
);
97 static void recent_file_activate_cb(GtkMenuItem
*menuitem
, gpointer user_data
);
98 static void recent_project_activate_cb(GtkMenuItem
*menuitem
, gpointer user_data
);
99 static GtkWidget
*progress_bar_create(void);
102 /* simple wrapper for gtk_widget_set_sensitive() to allow widget being NULL */
103 void ui_widget_set_sensitive(GtkWidget
*widget
, gboolean set
)
106 gtk_widget_set_sensitive(widget
, set
);
110 /* allow_override is TRUE if text can be ignored when another message has been set
111 * that didn't use allow_override and has not timed out. */
112 static void set_statusbar(const gchar
*text
, gboolean allow_override
)
114 static glong last_time
= 0;
116 const gint GEANY_STATUS_TIMEOUT
= 1;
118 if (! interface_prefs
.statusbar_visible
)
119 return; /* just do nothing if statusbar is not visible */
121 g_get_current_time(&timeval
);
123 if (! allow_override
)
125 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets
.statusbar
), 1);
126 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets
.statusbar
), 1, text
);
127 last_time
= timeval
.tv_sec
;
130 if (timeval
.tv_sec
> last_time
+ GEANY_STATUS_TIMEOUT
)
132 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets
.statusbar
), 1);
133 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets
.statusbar
), 1, text
);
138 /** Displays text on the statusbar.
139 * @param log Whether the message should be recorded in the Status window.
140 * @param format A @c printf -style string. */
141 void ui_set_statusbar(gboolean log
, const gchar
*format
, ...)
146 va_start(args
, format
);
147 g_vsnprintf(string
, 512, format
, args
);
150 if (! prefs
.suppress_status_messages
)
151 set_statusbar(string
, FALSE
);
153 if (log
|| prefs
.suppress_status_messages
)
154 msgwin_status_add("%s", string
);
158 /* updates the status bar document statistics */
159 void ui_update_statusbar(GeanyDocument
*doc
, gint pos
)
161 if (! interface_prefs
.statusbar_visible
)
162 return; /* just do nothing if statusbar is not visible */
165 doc
= document_get_current();
169 static GString
*stats_str
= NULL
;
170 const gchar sp
[] = " ";
172 const gchar
*cur_tag
;
173 gchar
*filetype_name
= doc
->file_type
->name
;
175 if (G_UNLIKELY(stats_str
== NULL
))
176 stats_str
= g_string_sized_new(120);
179 pos
= sci_get_current_position(doc
->editor
->sci
);
180 line
= sci_get_line_from_position(doc
->editor
->sci
, pos
);
182 /* Add temporary fix for sci infinite loop in Document::GetColumn(int)
183 * when current pos is beyond document end (can occur when removing
184 * blocks of selected lines especially esp. brace sections near end of file). */
185 if (pos
<= sci_get_length(doc
->editor
->sci
))
186 col
= sci_get_col_from_position(doc
->editor
->sci
, pos
);
190 /* Status bar statistics: col = column, sel = selection. */
191 g_string_printf(stats_str
, _("line: %d / %d\t col: %d\t sel: %d\t "),
192 (line
+ 1), sci_get_line_count(doc
->editor
->sci
), col
,
193 sci_get_selected_text_length(doc
->editor
->sci
) - 1);
195 g_string_append(stats_str
,
197 (doc
->readonly
) ? _("RO ") :
198 /* OVR = overwrite/overtype, INS = insert */
199 (sci_get_overtype(doc
->editor
->sci
) ? _("OVR") : _("INS")));
200 g_string_append(stats_str
, sp
);
202 switch (editor_get_indent_prefs(doc
->editor
)->type
)
204 case GEANY_INDENT_TYPE_TABS
:
205 g_string_append(stats_str
, _("TAB"));
207 case GEANY_INDENT_TYPE_SPACES
:
208 g_string_append(stats_str
, _("SP")); /* SP = space */
210 case GEANY_INDENT_TYPE_BOTH
:
211 g_string_append(stats_str
, _("T/S")); /* T/S = tabs and spaces */
214 g_string_append(stats_str
, sp
);
215 g_string_append_printf(stats_str
, _("mode: %s"),
216 editor_get_eol_char_name(doc
->editor
));
217 g_string_append(stats_str
, sp
);
218 g_string_append_printf(stats_str
, _("encoding: %s %s"),
219 (doc
->encoding
) ? doc
->encoding
: _("unknown"),
220 (encodings_is_unicode_charset(doc
->encoding
)) ?
221 /* BOM = byte order mark */
222 ((doc
->has_bom
) ? _("(with BOM)") : "") : "");
223 g_string_append(stats_str
, sp
);
224 g_string_append_printf(stats_str
, _("filetype: %s"), filetype_name
);
225 g_string_append(stats_str
, sp
);
228 g_string_append(stats_str
, _("MOD")); /* MOD = modified */
229 g_string_append(stats_str
, sp
);
232 symbols_get_current_function(doc
, &cur_tag
);
233 g_string_append_printf(stats_str
, _("scope: %s"),
237 g_string_append(stats_str
, sp
);
238 g_string_append_printf(stats_str
, "pos: %d", pos
);
239 g_string_append(stats_str
, sp
);
240 g_string_append_printf(stats_str
, "style: %d", sci_get_style_at(doc
->editor
->sci
, pos
));
243 /* can be overridden by status messages */
244 set_statusbar(stats_str
->str
, TRUE
);
246 else /* no documents */
248 set_statusbar("", TRUE
); /* can be overridden by status messages */
253 /* This sets the window title according to the current filename. */
254 void ui_set_window_title(GeanyDocument
*doc
)
257 GeanyProject
*project
= app
->project
;
260 doc
= document_get_current();
262 str
= g_string_new(NULL
);
266 g_string_append(str
, doc
->changed
? "*" : "");
268 if (doc
->file_name
== NULL
)
269 g_string_append(str
, DOC_FILENAME(doc
));
272 gchar
*short_name
= document_get_basename_for_display(doc
, 30);
273 gchar
*dirname
= g_path_get_dirname(DOC_FILENAME(doc
));
275 g_string_append(str
, short_name
);
276 g_string_append(str
, " - ");
277 g_string_append(str
, dirname
? dirname
: "");
281 g_string_append(str
, " - ");
285 g_string_append_c(str
, '[');
286 g_string_append(str
, project
->name
);
287 g_string_append(str
, "] - ");
289 g_string_append(str
, "Geany");
290 gtk_window_set_title(GTK_WINDOW(main_widgets
.window
), str
->str
);
291 g_string_free(str
, TRUE
);
295 void ui_set_editor_font(const gchar
*font_name
)
299 g_return_if_fail(font_name
!= NULL
);
301 /* do nothing if font has not changed */
302 if (interface_prefs
.editor_font
!= NULL
)
303 if (strcmp(font_name
, interface_prefs
.editor_font
) == 0)
306 g_free(interface_prefs
.editor_font
);
307 interface_prefs
.editor_font
= g_strdup(font_name
);
309 /* We copy the current style, and update the font in all open tabs. */
310 for (i
= 0; i
< documents_array
->len
; i
++)
312 if (documents
[i
]->editor
)
314 editor_set_font(documents
[i
]->editor
, interface_prefs
.editor_font
);
318 ui_set_statusbar(TRUE
, _("Font updated (%s)."), interface_prefs
.editor_font
);
322 void ui_set_fullscreen(void)
324 if (ui_prefs
.fullscreen
)
326 gtk_window_fullscreen(GTK_WINDOW(main_widgets
.window
));
330 gtk_window_unfullscreen(GTK_WINDOW(main_widgets
.window
));
335 void ui_update_popup_reundo_items(GeanyDocument
*doc
)
337 gboolean enable_undo
;
338 gboolean enable_redo
;
348 enable_undo
= document_can_undo(doc
);
349 enable_redo
= document_can_redo(doc
);
352 /* index 0 is the popup menu, 1 is the menubar, 2 is the toolbar */
353 len
= G_N_ELEMENTS(widgets
.undo_items
);
354 for (i
= 0; i
< len
; i
++)
356 ui_widget_set_sensitive(widgets
.undo_items
[i
], enable_undo
);
358 len
= G_N_ELEMENTS(widgets
.redo_items
);
359 for (i
= 0; i
< len
; i
++)
361 ui_widget_set_sensitive(widgets
.redo_items
[i
], enable_redo
);
366 void ui_update_popup_copy_items(GeanyDocument
*doc
)
374 enable
= sci_has_selection(doc
->editor
->sci
);
376 len
= G_N_ELEMENTS(widgets
.popup_copy_items
);
377 for (i
= 0; i
< len
; i
++)
378 ui_widget_set_sensitive(widgets
.popup_copy_items
[i
], enable
);
382 void ui_update_popup_goto_items(gboolean enable
)
385 len
= G_N_ELEMENTS(widgets
.popup_goto_items
);
386 for (i
= 0; i
< len
; i
++)
387 ui_widget_set_sensitive(widgets
.popup_goto_items
[i
], enable
);
391 void ui_update_menu_copy_items(GeanyDocument
*doc
)
393 gboolean enable
= FALSE
;
395 GtkWidget
*focusw
= gtk_window_get_focus(GTK_WINDOW(main_widgets
.window
));
397 if (IS_SCINTILLA(focusw
))
398 enable
= (doc
== NULL
) ? FALSE
: sci_has_selection(doc
->editor
->sci
);
400 if (GTK_IS_EDITABLE(focusw
))
401 enable
= gtk_editable_get_selection_bounds(GTK_EDITABLE(focusw
), NULL
, NULL
);
403 if (GTK_IS_TEXT_VIEW(focusw
))
405 GtkTextBuffer
*buffer
= gtk_text_view_get_buffer(
406 GTK_TEXT_VIEW(focusw
));
407 enable
= gtk_text_buffer_get_selection_bounds(buffer
, NULL
, NULL
);
410 len
= G_N_ELEMENTS(widgets
.menu_copy_items
);
411 for (i
= 0; i
< len
; i
++)
412 ui_widget_set_sensitive(widgets
.menu_copy_items
[i
], enable
);
416 void ui_update_insert_include_item(GeanyDocument
*doc
, gint item
)
418 gboolean enable
= FALSE
;
420 if (doc
== NULL
|| doc
->file_type
== NULL
)
422 else if (doc
->file_type
->id
== GEANY_FILETYPES_C
|| doc
->file_type
->id
== GEANY_FILETYPES_CPP
)
425 ui_widget_set_sensitive(widgets
.menu_insert_include_items
[item
], enable
);
429 void ui_update_fold_items(void)
431 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "menu_fold_all1"), editor_prefs
.folding
);
432 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "menu_unfold_all1"), editor_prefs
.folding
);
433 ui_widget_show_hide(ui_lookup_widget(main_widgets
.window
, "separator22"), editor_prefs
.folding
);
437 static void insert_include_items(GtkMenu
*me
, GtkMenu
*mp
, gchar
**includes
, gchar
*label
)
441 GtkWidget
*tmp_popup
;
442 GtkWidget
*edit_menu
, *edit_menu_item
;
443 GtkWidget
*popup_menu
, *popup_menu_item
;
445 edit_menu
= gtk_menu_new();
446 popup_menu
= gtk_menu_new();
447 edit_menu_item
= gtk_menu_item_new_with_label(label
);
448 popup_menu_item
= gtk_menu_item_new_with_label(label
);
449 gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_menu_item
), edit_menu
);
450 gtk_menu_item_set_submenu(GTK_MENU_ITEM(popup_menu_item
), popup_menu
);
452 while (includes
[i
] != NULL
)
454 tmp_menu
= gtk_menu_item_new_with_label(includes
[i
]);
455 tmp_popup
= gtk_menu_item_new_with_label(includes
[i
]);
456 gtk_container_add(GTK_CONTAINER(edit_menu
), tmp_menu
);
457 gtk_container_add(GTK_CONTAINER(popup_menu
), tmp_popup
);
458 g_signal_connect(tmp_menu
, "activate",
459 G_CALLBACK(on_menu_insert_include_activate
), (gpointer
) includes
[i
]);
460 g_signal_connect(tmp_popup
, "activate",
461 G_CALLBACK(on_insert_include_activate
), (gpointer
) includes
[i
]);
464 gtk_widget_show_all(edit_menu_item
);
465 gtk_widget_show_all(popup_menu_item
);
466 gtk_container_add(GTK_CONTAINER(me
), edit_menu_item
);
467 gtk_container_add(GTK_CONTAINER(mp
), popup_menu_item
);
471 void ui_create_insert_menu_items(void)
473 GtkMenu
*menu_edit
= GTK_MENU(ui_lookup_widget(main_widgets
.window
, "insert_include2_menu"));
474 GtkMenu
*menu_popup
= GTK_MENU(ui_lookup_widget(main_widgets
.editor_menu
, "insert_include1_menu"));
476 const gchar
*c_includes_stdlib
[] = {
477 "assert.h", "ctype.h", "errno.h", "float.h", "limits.h", "locale.h", "math.h", "setjmp.h",
478 "signal.h", "stdarg.h", "stddef.h", "stdio.h", "stdlib.h", "string.h", "time.h", NULL
480 const gchar
*c_includes_c99
[] = {
481 "complex.h", "fenv.h", "inttypes.h", "iso646.h", "stdbool.h", "stdint.h",
482 "tgmath.h", "wchar.h", "wctype.h", NULL
484 const gchar
*c_includes_cpp
[] = {
485 "cstdio", "cstring", "cctype", "cmath", "ctime", "cstdlib", "cstdarg", NULL
487 const gchar
*c_includes_cppstdlib
[] = {
488 "iostream", "fstream", "iomanip", "sstream", "exception", "stdexcept",
489 "memory", "locale", NULL
491 const gchar
*c_includes_stl
[] = {
492 "bitset", "dequev", "list", "map", "set", "queue", "stack", "vector", "algorithm",
493 "iterator", "functional", "string", "complex", "valarray", NULL
496 blank
= gtk_menu_item_new_with_label("#include \"...\"");
497 gtk_container_add(GTK_CONTAINER(menu_edit
), blank
);
498 gtk_widget_show(blank
);
499 g_signal_connect(blank
, "activate", G_CALLBACK(on_menu_insert_include_activate
),
501 blank
= gtk_separator_menu_item_new ();
502 gtk_container_add(GTK_CONTAINER(menu_edit
), blank
);
503 gtk_widget_show(blank
);
505 blank
= gtk_menu_item_new_with_label("#include \"...\"");
506 gtk_container_add(GTK_CONTAINER(menu_popup
), blank
);
507 gtk_widget_show(blank
);
508 g_signal_connect(blank
, "activate", G_CALLBACK(on_insert_include_activate
),
510 blank
= gtk_separator_menu_item_new();
511 gtk_container_add(GTK_CONTAINER(menu_popup
), blank
);
512 gtk_widget_show(blank
);
514 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_stdlib
, _("C Standard Library"));
515 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_c99
, _("ISO C99"));
516 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_cpp
, _("C++ (C Standard Library)"));
517 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_cppstdlib
, _("C++ Standard Library"));
518 insert_include_items(menu_edit
, menu_popup
, (gchar
**) c_includes_stl
, _("C++ STL"));
522 static void insert_date_items(GtkMenu
*me
, GtkMenu
*mp
, gchar
*label
)
526 item
= gtk_menu_item_new_with_mnemonic(label
);
527 gtk_container_add(GTK_CONTAINER(me
), item
);
528 gtk_widget_show(item
);
529 g_signal_connect(item
, "activate", G_CALLBACK(on_menu_insert_date_activate
), label
);
531 item
= gtk_menu_item_new_with_mnemonic(label
);
532 gtk_container_add(GTK_CONTAINER(mp
), item
);
533 gtk_widget_show(item
);
534 g_signal_connect(item
, "activate", G_CALLBACK(on_insert_date_activate
), label
);
538 void ui_create_insert_date_menu_items(void)
540 GtkMenu
*menu_edit
= GTK_MENU(ui_lookup_widget(main_widgets
.window
, "insert_date1_menu"));
541 GtkMenu
*menu_popup
= GTK_MENU(ui_lookup_widget(main_widgets
.editor_menu
, "insert_date2_menu"));
545 insert_date_items(menu_edit
, menu_popup
, _("dd.mm.yyyy"));
546 insert_date_items(menu_edit
, menu_popup
, _("mm.dd.yyyy"));
547 insert_date_items(menu_edit
, menu_popup
, _("yyyy/mm/dd"));
549 item
= gtk_separator_menu_item_new();
550 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
551 gtk_widget_show(item
);
552 item
= gtk_separator_menu_item_new();
553 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
554 gtk_widget_show(item
);
556 insert_date_items(menu_edit
, menu_popup
, _("dd.mm.yyyy hh:mm:ss"));
557 insert_date_items(menu_edit
, menu_popup
, _("mm.dd.yyyy hh:mm:ss"));
558 insert_date_items(menu_edit
, menu_popup
, _("yyyy/mm/dd hh:mm:ss"));
560 item
= gtk_separator_menu_item_new();
561 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
562 gtk_widget_show(item
);
563 item
= gtk_separator_menu_item_new();
564 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
565 gtk_widget_show(item
);
567 str
= _("_Use Custom Date Format");
568 item
= gtk_menu_item_new_with_mnemonic(str
);
569 gtk_container_add(GTK_CONTAINER(menu_edit
), item
);
570 gtk_widget_show(item
);
571 g_signal_connect(item
, "activate", G_CALLBACK(on_menu_insert_date_activate
), str
);
572 g_object_set_data_full(G_OBJECT(main_widgets
.window
),
573 "insert_date_custom1", g_object_ref(item
), (GDestroyNotify
)g_object_unref
);
575 item
= gtk_menu_item_new_with_mnemonic(str
);
576 gtk_container_add(GTK_CONTAINER(menu_popup
), item
);
577 gtk_widget_show(item
);
578 g_signal_connect(item
, "activate", G_CALLBACK(on_insert_date_activate
), str
);
579 g_object_set_data_full(G_OBJECT(main_widgets
.editor_menu
),
580 "insert_date_custom2", g_object_ref(item
), (GDestroyNotify
)g_object_unref
);
582 insert_date_items(menu_edit
, menu_popup
, _("_Set Custom Date Format"));
586 void ui_save_buttons_toggle(gboolean enable
)
589 gboolean dirty_tabs
= FALSE
;
591 if (ui_prefs
.allow_always_save
)
594 ui_widget_set_sensitive(widgets
.save_buttons
[0], enable
);
595 ui_widget_set_sensitive(widgets
.save_buttons
[1], enable
);
597 /* save all menu item and tool button */
598 for (i
= 0; i
< documents_array
->len
; i
++)
600 /* check whether there are files where changes were made and if there are some,
601 * we need the save all button / item */
602 if (documents
[i
]->is_valid
&& documents
[i
]->changed
)
609 ui_widget_set_sensitive(widgets
.save_buttons
[2], dirty_tabs
);
610 ui_widget_set_sensitive(widgets
.save_buttons
[3], dirty_tabs
);
614 #define add_doc_widget(widget_name) \
615 g_ptr_array_add(widgets.document_buttons, ui_lookup_widget(main_widgets.window, widget_name))
617 #define add_doc_toolitem(widget_name) \
618 g_ptr_array_add(widgets.document_buttons, toolbar_get_action_by_name(widget_name))
620 static void init_document_widgets(void)
622 widgets
.document_buttons
= g_ptr_array_new();
624 /* Cache the document-sensitive widgets so we don't have to keep looking them up
625 * when using ui_document_buttons_update(). */
626 add_doc_widget("menu_close1");
627 add_doc_widget("close_other_documents1");
628 add_doc_widget("menu_change_font1");
629 add_doc_widget("menu_close_all1");
630 add_doc_widget("menu_save1");
631 add_doc_widget("menu_save_all1");
632 add_doc_widget("menu_save_as1");
633 add_doc_widget("menu_count_words1");
634 add_doc_widget("menu_build1");
635 add_doc_widget("add_comments1");
636 add_doc_widget("menu_paste1");
637 add_doc_widget("menu_undo2");
638 add_doc_widget("preferences2");
639 add_doc_widget("menu_reload1");
640 add_doc_widget("menu_document1");
641 add_doc_widget("menu_choose_color1");
642 add_doc_widget("menu_zoom_in1");
643 add_doc_widget("menu_zoom_out1");
644 add_doc_widget("menu_view_editor1");
645 add_doc_widget("normal_size1");
646 add_doc_widget("treeview6");
647 add_doc_widget("print1");
648 add_doc_widget("menu_reload_as1");
649 add_doc_widget("menu_select_all1");
650 add_doc_widget("insert_date1");
651 add_doc_widget("menu_format1");
652 add_doc_widget("commands2");
653 add_doc_widget("menu_open_selected_file1");
654 add_doc_widget("page_setup1");
655 add_doc_widget("find1");
656 add_doc_widget("find_next1");
657 add_doc_widget("find_previous1");
658 add_doc_widget("replace1");
659 add_doc_widget("find_nextsel1");
660 add_doc_widget("find_prevsel1");
661 add_doc_widget("go_to_line1");
662 add_doc_toolitem("Close");
663 add_doc_toolitem("CloseAll");
664 add_doc_toolitem("Search");
665 add_doc_toolitem("SearchEntry");
666 add_doc_toolitem("NavBack");
667 add_doc_toolitem("NavFor");
668 add_doc_toolitem("ZoomIn");
669 add_doc_toolitem("ZoomOut");
670 add_doc_toolitem("Indent");
671 add_doc_toolitem("UnIndent");
672 add_doc_toolitem("Cut");
673 add_doc_toolitem("Copy");
674 add_doc_toolitem("Paste");
675 add_doc_toolitem("Delete");
676 add_doc_toolitem("Save");
677 add_doc_toolitem("SaveAll");
678 add_doc_toolitem("Compile");
679 add_doc_toolitem("Run");
680 add_doc_toolitem("Reload");
681 add_doc_toolitem("Color");
682 add_doc_toolitem("Goto");
683 add_doc_toolitem("GotoEntry");
687 void ui_document_buttons_update(void)
690 gboolean enable
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
)) ? TRUE
: FALSE
;
692 for (i
= 0; i
< widgets
.document_buttons
->len
; i
++)
694 GtkWidget
*widget
= g_ptr_array_index(widgets
.document_buttons
, i
);
695 if (GTK_IS_ACTION(widget
))
696 gtk_action_set_sensitive(GTK_ACTION(widget
), enable
);
698 ui_widget_set_sensitive(widget
, enable
);
703 static void on_doc_sensitive_widget_destroy(GtkWidget
*widget
, G_GNUC_UNUSED gpointer user_data
)
705 g_ptr_array_remove_fast(widgets
.document_buttons
, widget
);
709 /** Adds a widget to the list of widgets that should be set sensitive/insensitive
710 * when some documents are present/no documents are open.
711 * It will be removed when the widget is destroyed.
712 * @param widget The widget to add.
716 void ui_add_document_sensitive(GtkWidget
*widget
)
718 gboolean enable
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
)) ? TRUE
: FALSE
;
720 ui_widget_set_sensitive(widget
, enable
);
722 g_ptr_array_add(widgets
.document_buttons
, widget
);
723 g_signal_connect(widget
, "destroy", G_CALLBACK(on_doc_sensitive_widget_destroy
), NULL
);
727 void ui_widget_show_hide(GtkWidget
*widget
, gboolean show
)
731 gtk_widget_show(widget
);
735 gtk_widget_hide(widget
);
740 void ui_sidebar_show_hide(void)
744 /* check that there are no other notebook pages before hiding the sidebar completely
745 * other pages could be e.g. the file browser plugin */
746 if (! interface_prefs
.sidebar_openfiles_visible
&& ! interface_prefs
.sidebar_symbol_visible
&&
747 gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.sidebar_notebook
)) <= 2)
749 ui_prefs
.sidebar_visible
= FALSE
;
752 widget
= ui_lookup_widget(main_widgets
.window
, "menu_show_sidebar1");
753 if (ui_prefs
.sidebar_visible
!= gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget
)))
755 ignore_callback
= TRUE
;
756 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget
), ui_prefs
.sidebar_visible
);
757 ignore_callback
= FALSE
;
760 ui_widget_show_hide(main_widgets
.sidebar_notebook
, ui_prefs
.sidebar_visible
);
762 ui_widget_show_hide(gtk_notebook_get_nth_page(
763 GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), 0), interface_prefs
.sidebar_symbol_visible
);
764 ui_widget_show_hide(gtk_notebook_get_nth_page(
765 GTK_NOTEBOOK(main_widgets
.sidebar_notebook
), 1), interface_prefs
.sidebar_openfiles_visible
);
769 void ui_document_show_hide(GeanyDocument
*doc
)
773 const GeanyIndentPrefs
*iprefs
;
776 doc
= document_get_current();
781 ignore_callback
= TRUE
;
783 gtk_check_menu_item_set_active(
784 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_line_wrapping1")),
785 doc
->editor
->line_wrapping
);
787 gtk_check_menu_item_set_active(
788 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "line_breaking1")),
789 doc
->editor
->line_breaking
);
791 iprefs
= editor_get_indent_prefs(doc
->editor
);
793 item
= ui_lookup_widget(main_widgets
.window
, "menu_use_auto_indentation1");
794 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), doc
->editor
->auto_indent
);
796 switch (iprefs
->type
)
798 case GEANY_INDENT_TYPE_SPACES
:
799 widget_name
= "spaces1"; break;
800 case GEANY_INDENT_TYPE_TABS
:
801 widget_name
= "tabs1"; break;
802 case GEANY_INDENT_TYPE_BOTH
:
804 widget_name
= "tabs_and_spaces1"; break;
806 item
= ui_lookup_widget(main_widgets
.window
, widget_name
);
807 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), TRUE
);
809 gtk_check_menu_item_set_active(
810 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "set_file_readonly1")),
813 item
= ui_lookup_widget(main_widgets
.window
, "menu_write_unicode_bom1");
814 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item
), doc
->has_bom
);
815 ui_widget_set_sensitive(item
, encodings_is_unicode_charset(doc
->encoding
));
817 switch (sci_get_eol_mode(doc
->editor
->sci
))
819 case SC_EOL_CR
: widget_name
= "cr"; break;
820 case SC_EOL_LF
: widget_name
= "lf"; break;
821 default: widget_name
= "crlf"; break;
823 gtk_check_menu_item_set_active(
824 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, widget_name
)), TRUE
);
826 encodings_select_radio_item(doc
->encoding
);
827 filetypes_select_radio_item(doc
->file_type
);
829 ignore_callback
= FALSE
;
833 void ui_set_search_entry_background(GtkWidget
*widget
, gboolean success
)
835 static const GdkColor red
= {0, 0xffff, 0x6666, 0x6666};
836 static const GdkColor white
= {0, 0xffff, 0xffff, 0xffff};
837 static gboolean old_value
= TRUE
;
839 g_return_if_fail(widget
!= NULL
);
841 /* update only if really needed */
842 if (old_value
!= success
)
844 gtk_widget_modify_base(widget
, GTK_STATE_NORMAL
, success
? NULL
: &red
);
845 gtk_widget_modify_text(widget
, GTK_STATE_NORMAL
, success
? NULL
: &white
);
852 static gboolean
have_tango_icon_theme(void)
854 static gboolean result
= FALSE
;
855 static gboolean checked
= FALSE
;
861 g_object_get(G_OBJECT(gtk_settings_get_default()), "gtk-icon-theme-name", &theme_name
, NULL
);
862 setptr(theme_name
, g_utf8_strdown(theme_name
, -1));
864 result
= (strstr(theme_name
, "tango") != NULL
);
874 /* Note: remember to unref the pixbuf once an image or window has added a reference. */
875 GdkPixbuf
*ui_new_pixbuf_from_inline(gint img
)
879 case GEANY_IMAGE_LOGO
:
880 return gdk_pixbuf_new_from_inline(-1, aladin_inline
, FALSE
, NULL
);
882 case GEANY_IMAGE_SAVE_ALL
:
884 /* check whether the icon theme looks like a Gnome icon theme, if so use the
885 * old Gnome based Save All icon, otherwise assume a Tango-like icon theme */
886 if (have_tango_icon_theme())
887 return gdk_pixbuf_new_from_inline(-1, save_all_tango_inline
, FALSE
, NULL
);
889 return gdk_pixbuf_new_from_inline(-1, save_all_gnome_inline
, FALSE
, NULL
);
892 case GEANY_IMAGE_CLOSE_ALL
:
894 return gdk_pixbuf_new_from_inline(-1, close_all_inline
, FALSE
, NULL
);
897 case GEANY_IMAGE_BUILD
:
899 return gdk_pixbuf_new_from_inline(-1, build_inline
, FALSE
, NULL
);
908 static GdkPixbuf
*ui_new_pixbuf_from_stock(const gchar
*stock_id
)
910 if (utils_str_equal(stock_id
, GEANY_STOCK_CLOSE_ALL
))
911 return ui_new_pixbuf_from_inline(GEANY_IMAGE_CLOSE_ALL
);
912 else if (utils_str_equal(stock_id
, GEANY_STOCK_BUILD
))
913 return ui_new_pixbuf_from_inline(GEANY_IMAGE_BUILD
);
914 else if (utils_str_equal(stock_id
, GEANY_STOCK_SAVE_ALL
))
915 return ui_new_pixbuf_from_inline(GEANY_IMAGE_SAVE_ALL
);
921 GtkWidget
*ui_new_image_from_inline(gint img
)
926 pb
= ui_new_pixbuf_from_inline(img
);
927 wid
= gtk_image_new_from_pixbuf(pb
);
928 g_object_unref(pb
); /* the image doesn't adopt our reference, so remove our ref. */
933 static void recent_create_menu(GeanyRecentFiles
*grf
)
939 len
= MIN(file_prefs
.mru_length
, g_queue_get_length(grf
->recent_queue
));
940 for (i
= 0; i
< len
; i
++)
942 filename
= g_queue_peek_nth(grf
->recent_queue
, i
);
943 /* create menu item for the recent files menu in the menu bar */
944 tmp
= gtk_menu_item_new_with_label(filename
);
945 gtk_widget_show(tmp
);
946 gtk_container_add(GTK_CONTAINER(grf
->menubar
), tmp
);
947 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
948 /* create menu item for the recent files menu in the toolbar */
949 if (grf
->toolbar
!= NULL
)
951 tmp
= gtk_menu_item_new_with_label(filename
);
952 gtk_widget_show(tmp
);
953 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
954 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
960 static GeanyRecentFiles
*recent_get_recent_files(void)
962 static GeanyRecentFiles grf
= { RECENT_FILE_FILE
, NULL
, NULL
, NULL
, NULL
};
964 if (G_UNLIKELY(grf
.recent_queue
== NULL
))
966 grf
.recent_queue
= ui_prefs
.recent_queue
;
967 grf
.menubar
= ui_widgets
.recent_files_menu_menubar
;
968 grf
.toolbar
= geany_menu_button_action_get_menu(GEANY_MENU_BUTTON_ACTION(
969 toolbar_get_action_by_name("Open")));
970 grf
.activate_cb
= recent_file_activate_cb
;
976 static GeanyRecentFiles
*recent_get_recent_projects(void)
978 static GeanyRecentFiles grf
= { RECENT_FILE_PROJECT
, NULL
, NULL
, NULL
, NULL
};
980 if (G_UNLIKELY(grf
.recent_queue
== NULL
))
982 grf
.recent_queue
= ui_prefs
.recent_projects_queue
;
983 grf
.menubar
= ui_widgets
.recent_projects_menu_menubar
;
985 grf
.activate_cb
= recent_project_activate_cb
;
991 void ui_create_recent_menus(void)
993 recent_create_menu(recent_get_recent_files());
994 recent_create_menu(recent_get_recent_projects());
998 static void recent_file_activate_cb(GtkMenuItem
*menuitem
, G_GNUC_UNUSED gpointer user_data
)
1000 gchar
*utf8_filename
= ui_menu_item_get_text(menuitem
);
1001 gchar
*locale_filename
= utils_get_locale_from_utf8(utf8_filename
);
1003 if (document_open_file(locale_filename
, FALSE
, NULL
, NULL
) != NULL
)
1004 recent_file_loaded(utf8_filename
, recent_get_recent_files());
1006 g_free(locale_filename
);
1007 g_free(utf8_filename
);
1011 static void recent_project_activate_cb(GtkMenuItem
*menuitem
, G_GNUC_UNUSED gpointer user_data
)
1013 gchar
*utf8_filename
= ui_menu_item_get_text(menuitem
);
1014 gchar
*locale_filename
= utils_get_locale_from_utf8(utf8_filename
);
1016 if (project_ask_close() && project_load_file_with_session(locale_filename
))
1017 recent_file_loaded(utf8_filename
, recent_get_recent_projects());
1019 g_free(locale_filename
);
1020 g_free(utf8_filename
);
1024 static void add_recent_file(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
)
1026 if (g_queue_find_custom(grf
->recent_queue
, utf8_filename
, (GCompareFunc
) strcmp
) == NULL
)
1028 #if GTK_CHECK_VERSION(2, 10, 0)
1029 if (grf
->type
== RECENT_FILE_FILE
)
1031 GtkRecentManager
*manager
= gtk_recent_manager_get_default();
1032 gchar
*uri
= g_filename_to_uri(utf8_filename
, NULL
, NULL
);
1035 gtk_recent_manager_add_item(manager
, uri
);
1040 g_queue_push_head(grf
->recent_queue
, g_strdup(utf8_filename
));
1041 if (g_queue_get_length(grf
->recent_queue
) > file_prefs
.mru_length
)
1043 g_free(g_queue_pop_tail(grf
->recent_queue
));
1045 update_recent_menu(grf
);
1047 /* filename already in recent list */
1049 recent_file_loaded(utf8_filename
, grf
);
1053 void ui_add_recent_file(const gchar
*utf8_filename
)
1055 add_recent_file(utf8_filename
, recent_get_recent_files());
1059 void ui_add_recent_project_file(const gchar
*utf8_filename
)
1061 add_recent_file(utf8_filename
, recent_get_recent_projects());
1065 /* Returns: newly allocated string with the UTF-8 menu text. */
1066 gchar
*ui_menu_item_get_text(GtkMenuItem
*menu_item
)
1068 const gchar
*text
= NULL
;
1070 if (GTK_BIN(menu_item
)->child
)
1072 GtkWidget
*child
= GTK_BIN(menu_item
)->child
;
1074 if (GTK_IS_LABEL(child
))
1075 text
= gtk_label_get_text(GTK_LABEL(child
));
1077 /* GTK owns text so it's much safer to return a copy of it in case the memory is reallocated */
1078 return g_strdup(text
);
1082 static gint
find_recent_file_item(gconstpointer list_data
, gconstpointer user_data
)
1084 gchar
*menu_text
= ui_menu_item_get_text(GTK_MENU_ITEM(list_data
));
1087 if (utils_str_equal(menu_text
, user_data
))
1097 static void recent_file_loaded(const gchar
*utf8_filename
, GeanyRecentFiles
*grf
)
1099 GList
*item
, *children
;
1103 /* first reorder the queue */
1104 item
= g_queue_find_custom(grf
->recent_queue
, utf8_filename
, (GCompareFunc
) strcmp
);
1105 g_return_if_fail(item
!= NULL
);
1108 g_queue_remove(grf
->recent_queue
, data
);
1109 g_queue_push_head(grf
->recent_queue
, data
);
1111 /* remove the old menuitem for the filename */
1112 children
= gtk_container_get_children(GTK_CONTAINER(grf
->menubar
));
1113 item
= g_list_find_custom(children
, utf8_filename
, (GCompareFunc
) find_recent_file_item
);
1115 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1116 g_list_free(children
);
1118 if (grf
->toolbar
!= NULL
)
1120 children
= gtk_container_get_children(GTK_CONTAINER(grf
->toolbar
));
1121 item
= g_list_find_custom(children
, utf8_filename
, (GCompareFunc
) find_recent_file_item
);
1123 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1124 g_list_free(children
);
1126 /* now prepend a new menuitem for the filename,
1127 * first for the recent files menu in the menu bar */
1128 tmp
= gtk_menu_item_new_with_label(utf8_filename
);
1129 gtk_widget_show(tmp
);
1130 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf
->menubar
), tmp
);
1131 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1132 /* then for the recent files menu in the tool bar */
1133 if (grf
->toolbar
!= NULL
)
1135 tmp
= gtk_menu_item_new_with_label(utf8_filename
);
1136 gtk_widget_show(tmp
);
1137 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
1138 /* this is a bit ugly, but we need to use gtk_container_add(). Using
1139 * gtk_menu_shell_prepend() doesn't emit GtkContainer's "add" signal which we need in
1140 * GeanyMenubuttonAction */
1141 gtk_menu_reorder_child(GTK_MENU(grf
->toolbar
), tmp
, 0);
1142 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1147 static void update_recent_menu(GeanyRecentFiles
*grf
)
1151 GList
*children
, *item
;
1153 filename
= g_queue_peek_head(grf
->recent_queue
);
1155 /* clean the MRU list before adding an item (menubar) */
1156 children
= gtk_container_get_children(GTK_CONTAINER(grf
->menubar
));
1157 if (g_list_length(children
) > file_prefs
.mru_length
- 1)
1159 item
= g_list_nth(children
, file_prefs
.mru_length
- 1);
1160 while (item
!= NULL
)
1162 if (GTK_IS_MENU_ITEM(item
->data
))
1163 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1164 item
= g_list_next(item
);
1167 g_list_free(children
);
1169 /* create item for the menu bar menu */
1170 tmp
= gtk_menu_item_new_with_label(filename
);
1171 gtk_widget_show(tmp
);
1172 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf
->menubar
), tmp
);
1173 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1175 /* clean the MRU list before adding an item (toolbar) */
1176 if (grf
->toolbar
!= NULL
)
1178 children
= gtk_container_get_children(GTK_CONTAINER(grf
->toolbar
));
1179 if (g_list_length(children
) > file_prefs
.mru_length
- 1)
1181 item
= g_list_nth(children
, file_prefs
.mru_length
- 1);
1182 while (item
!= NULL
)
1184 if (GTK_IS_MENU_ITEM(item
->data
))
1185 gtk_widget_destroy(GTK_WIDGET(item
->data
));
1186 item
= g_list_next(item
);
1189 g_list_free(children
);
1191 /* create item for the tool bar menu */
1192 tmp
= gtk_menu_item_new_with_label(filename
);
1193 gtk_widget_show(tmp
);
1194 gtk_container_add(GTK_CONTAINER(grf
->toolbar
), tmp
);
1195 gtk_menu_reorder_child(GTK_MENU(grf
->toolbar
), tmp
, 0);
1196 g_signal_connect(tmp
, "activate", G_CALLBACK(grf
->activate_cb
), NULL
);
1201 void ui_toggle_editor_features(GeanyUIEditorFeatures feature
)
1203 gint i
, max
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
));
1206 for (i
= 0; i
< max
; i
++)
1208 doc
= document_get_from_page(i
);
1212 case GEANY_EDITOR_SHOW_MARKERS_MARGIN
:
1213 sci_set_symbol_margin(doc
->editor
->sci
, editor_prefs
.show_markers_margin
);
1215 case GEANY_EDITOR_SHOW_LINE_NUMBERS
:
1216 sci_set_line_numbers(doc
->editor
->sci
, editor_prefs
.show_linenumber_margin
, 0);
1218 case GEANY_EDITOR_SHOW_WHITE_SPACE
:
1219 sci_set_visible_white_spaces(doc
->editor
->sci
, editor_prefs
.show_white_space
);
1221 case GEANY_EDITOR_SHOW_LINE_ENDINGS
:
1222 sci_set_visible_eols(doc
->editor
->sci
, editor_prefs
.show_line_endings
);
1224 case GEANY_EDITOR_SHOW_INDENTATION_GUIDES
:
1225 editor_set_indentation_guides(doc
->editor
);
1232 void ui_update_view_editor_menu_items(void)
1234 ignore_callback
= TRUE
;
1235 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_markers_margin1")), editor_prefs
.show_markers_margin
);
1236 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets
.window
, "menu_linenumber_margin1")), editor_prefs
.show_linenumber_margin
);
1237 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
);
1238 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
);
1239 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
);
1240 ignore_callback
= FALSE
;
1244 /** Creates a GNOME HIG-style frame (with no border and indented child alignment).
1245 * @param label_text The label text.
1246 * @param alignment An address to store the alignment widget pointer.
1247 * @return The frame widget, setting the alignment container for packing child widgets. */
1248 GtkWidget
*ui_frame_new_with_alignment(const gchar
*label_text
, GtkWidget
**alignment
)
1250 GtkWidget
*label
, *align
;
1251 GtkWidget
*frame
= gtk_frame_new(NULL
);
1253 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_NONE
);
1255 align
= gtk_alignment_new(0.5, 0.5, 1, 1);
1256 gtk_container_add(GTK_CONTAINER(frame
), align
);
1257 gtk_alignment_set_padding(GTK_ALIGNMENT(align
), 0, 0, 12, 0);
1259 label
= ui_label_new_bold(label_text
);
1260 gtk_frame_set_label_widget(GTK_FRAME(frame
), label
);
1267 /** Makes a fixed border for dialogs without increasing the button box border.
1268 * @param dialog The parent container for the @c GtkVBox.
1269 * @return The packed @c GtkVBox. */
1270 GtkWidget
*ui_dialog_vbox_new(GtkDialog
*dialog
)
1272 GtkWidget
*vbox
= gtk_vbox_new(FALSE
, 12); /* need child vbox to set a separate border. */
1274 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 6);
1275 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), vbox
);
1280 /** Creates a @c GtkButton with custom text and a stock image similar to
1281 * @c gtk_button_new_from_stock().
1282 * @param stock_id A @c GTK_STOCK_NAME string.
1283 * @param text Button label text, can include mnemonics.
1284 * @return The new @c GtkButton.
1286 GtkWidget
*ui_button_new_with_image(const gchar
*stock_id
, const gchar
*text
)
1288 GtkWidget
*image
, *button
;
1290 button
= gtk_button_new_with_mnemonic(text
);
1291 gtk_widget_show(button
);
1292 image
= gtk_image_new_from_stock(stock_id
, GTK_ICON_SIZE_BUTTON
);
1293 gtk_button_set_image(GTK_BUTTON(button
), image
);
1294 /* note: image is shown by gtk */
1299 /** Creates a @c GtkImageMenuItem with a stock image and a custom label.
1300 * @param stock_id Stock image ID, e.g. @c GTK_STOCK_OPEN.
1301 * @param label Menu item label, can include mnemonics.
1302 * @return The new @c GtkImageMenuItem.
1307 ui_image_menu_item_new(const gchar
*stock_id
, const gchar
*label
)
1309 GtkWidget
*item
= gtk_image_menu_item_new_with_mnemonic(label
);
1310 GtkWidget
*image
= gtk_image_new_from_stock(stock_id
, GTK_ICON_SIZE_MENU
);
1312 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), image
);
1313 gtk_widget_show(image
);
1318 static void entry_clear_icon_release_cb(GtkEntry
*entry
, gint icon_pos
,
1319 GdkEvent
*event
, gpointer data
)
1321 if (event
->button
.button
== 1 && icon_pos
== 1)
1323 gtk_entry_set_text(entry
, "");
1324 gtk_widget_grab_focus(GTK_WIDGET(entry
));
1329 /** Adds a small clear icon to the right end of the passed @a entry.
1330 * A callback to clear the contents of the GtkEntry is automatically added.
1332 * This feature is only available with GTK 2.16 but implemented as a runtime check,
1333 * so it is safe to just use this function, if the code is ran with older versions,
1334 * nothing happens. If ran with GTK 2.16 or newer, the icon is displayed.
1336 * @param entry The GtkEntry object to which the icon should be attached.
1340 void ui_entry_add_clear_icon(GtkEntry
*entry
)
1342 if (gtk_check_version(2, 15, 2) == NULL
)
1344 g_object_set(entry
, "secondary-icon-stock", "gtk-clear", NULL
);
1345 g_signal_connect(entry
, "icon-release", G_CALLBACK(entry_clear_icon_release_cb
), NULL
);
1350 static void add_to_size_group(GtkWidget
*widget
, gpointer size_group
)
1352 g_return_if_fail(GTK_IS_SIZE_GROUP(size_group
));
1353 gtk_size_group_add_widget(GTK_SIZE_GROUP(size_group
), widget
);
1357 /* Copies the spacing and layout of the master GtkHButtonBox and synchronises
1358 * the width of each button box's children.
1359 * Should be called after all child widgets have been packed. */
1360 void ui_hbutton_box_copy_layout(GtkButtonBox
*master
, GtkButtonBox
*copy
)
1362 GtkSizeGroup
*size_group
;
1364 gtk_box_set_spacing(GTK_BOX(copy
), 10);
1365 gtk_button_box_set_layout(copy
, gtk_button_box_get_layout(master
));
1367 /* now we need to put the widest widget from each button box in a size group,
1368 * but we don't know the width before they are drawn, and for different label
1369 * translations the widest widget can vary, so we just add all widgets. */
1370 size_group
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1371 gtk_container_foreach(GTK_CONTAINER(master
), add_to_size_group
, size_group
);
1372 gtk_container_foreach(GTK_CONTAINER(copy
), add_to_size_group
, size_group
);
1373 g_object_unref(size_group
);
1377 /* Prepends the active text to the drop down list, unless the first element in
1378 * the list is identical, ensuring there are <= history_len elements. */
1379 void ui_combo_box_add_to_history(GtkComboBox
*combo
, const gchar
*text
)
1381 const gint history_len
= 30;
1382 GtkTreeModel
*model
;
1385 gboolean equal
= FALSE
;
1388 model
= gtk_combo_box_get_model(combo
);
1389 if (gtk_tree_model_get_iter_first(model
, &iter
))
1391 gtk_tree_model_get(model
, &iter
, 0, &combo_text
, -1);
1392 equal
= utils_str_equal(combo_text
, text
);
1395 if (equal
) return; /* don't prepend duplicate */
1397 gtk_combo_box_prepend_text(combo
, text
);
1400 path
= gtk_tree_path_new_from_indices(history_len
, -1);
1401 if (gtk_tree_model_get_iter(model
, &iter
, path
))
1403 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
1405 gtk_tree_path_free(path
);
1409 /* Same as gtk_combo_box_prepend_text(), except that text is only prepended if it not already
1410 * exists in the combo's model. */
1411 void ui_combo_box_prepend_text_once(GtkComboBox
*combo
, const gchar
*text
)
1413 GtkTreeModel
*model
;
1416 gboolean found
= FALSE
;
1418 model
= gtk_combo_box_get_model(combo
);
1419 if (gtk_tree_model_get_iter_first(model
, &iter
))
1423 gtk_tree_model_get(model
, &iter
, 0, &combo_text
, -1);
1424 found
= utils_str_equal(combo_text
, text
);
1427 while (!found
&& gtk_tree_model_iter_next(model
, &iter
));
1430 return; /* don't prepend duplicate */
1432 gtk_combo_box_prepend_text(combo
, text
);
1436 /* Changes the color of the notebook tab text and open files items according to
1437 * document status. */
1438 void ui_update_tab_status(GeanyDocument
*doc
)
1440 const GdkColor
*color
= document_get_status_color(doc
);
1442 /* NULL color will reset to default */
1443 gtk_widget_modify_fg(doc
->priv
->tab_label
, GTK_STATE_NORMAL
, color
);
1444 gtk_widget_modify_fg(doc
->priv
->tab_label
, GTK_STATE_ACTIVE
, color
);
1446 sidebar_openfiles_update(doc
);
1450 static gboolean
tree_model_iter_get_next(GtkTreeModel
*model
, GtkTreeIter
*iter
,
1457 return gtk_tree_model_iter_next(model
, iter
);
1459 path
= gtk_tree_model_get_path(model
, iter
);
1460 result
= gtk_tree_path_prev(path
) && gtk_tree_model_get_iter(model
, iter
, path
);
1461 gtk_tree_path_free(path
);
1466 /* note: the while loop might be more efficient when searching upwards if it
1467 * used tree paths instead of tree iters, but in practice it probably doesn't matter much. */
1468 static gboolean
tree_view_find(GtkTreeView
*treeview
, TVMatchCallback cb
, gboolean down
)
1470 GtkTreeSelection
*treesel
;
1472 GtkTreeModel
*model
;
1474 treesel
= gtk_tree_view_get_selection(treeview
);
1475 if (gtk_tree_selection_get_selected(treesel
, &model
, &iter
))
1477 /* get the next selected item */
1478 if (! tree_model_iter_get_next(model
, &iter
, down
))
1479 return FALSE
; /* no more items */
1481 else /* no selection */
1483 if (! gtk_tree_model_get_iter_first(model
, &iter
))
1484 return TRUE
; /* no items */
1488 gtk_tree_selection_select_iter(treesel
, &iter
);
1490 break; /* found next message */
1492 if (! tree_model_iter_get_next(model
, &iter
, down
))
1493 return FALSE
; /* no more items */
1495 /* scroll item in view */
1496 if (ui_prefs
.msgwindow_visible
)
1498 GtkTreePath
*path
= gtk_tree_model_get_path(
1499 gtk_tree_view_get_model(treeview
), &iter
);
1501 gtk_tree_view_scroll_to_cell(treeview
, path
, NULL
, TRUE
, 0.5, 0.5);
1502 gtk_tree_path_free(path
);
1508 /* Returns FALSE if the treeview has items but no matching next item. */
1509 gboolean
ui_tree_view_find_next(GtkTreeView
*treeview
, TVMatchCallback cb
)
1511 return tree_view_find(treeview
, cb
, TRUE
);
1515 /* Returns FALSE if the treeview has items but no matching next item. */
1516 gboolean
ui_tree_view_find_previous(GtkTreeView
*treeview
, TVMatchCallback cb
)
1518 return tree_view_find(treeview
, cb
, FALSE
);
1523 * Modifies the font of a widget using gtk_widget_modify_font().
1525 * @param widget The widget.
1526 * @param str The font name as expected by pango_font_description_from_string().
1528 void ui_widget_modify_font_from_string(GtkWidget
*widget
, const gchar
*str
)
1530 PangoFontDescription
*pfd
;
1532 pfd
= pango_font_description_from_string(str
);
1533 gtk_widget_modify_font(widget
, pfd
);
1534 pango_font_description_free(pfd
);
1538 /** Creates a @c GtkHBox with @a entry packed into it and an open button which runs a
1539 * file chooser, replacing entry text (if successful) with the path returned from the
1540 * @c GtkFileChooser.
1541 * @note @a entry can be the child of an unparented widget, such as @c GtkComboBoxEntry.
1542 * @param title The file chooser dialog title, or @c NULL.
1543 * @param action The mode of the file chooser.
1544 * @param entry Can be an unpacked @c GtkEntry, or the child of an unpacked widget,
1545 * such as @c GtkComboBoxEntry.
1546 * @return The @c GtkHBox.
1548 /* @see ui_setup_open_button_callback(). */
1549 GtkWidget
*ui_path_box_new(const gchar
*title
, GtkFileChooserAction action
, GtkEntry
*entry
)
1551 GtkWidget
*vbox
, *dirbtn
, *openimg
, *hbox
, *path_entry
;
1553 hbox
= gtk_hbox_new(FALSE
, 6);
1554 path_entry
= GTK_WIDGET(entry
);
1556 /* prevent path_entry being vertically stretched to the height of dirbtn */
1557 vbox
= gtk_vbox_new(FALSE
, 0);
1558 if (gtk_widget_get_parent(path_entry
)) /* entry->parent may be a GtkComboBoxEntry */
1560 GtkWidget
*parent
= gtk_widget_get_parent(path_entry
);
1562 gtk_box_pack_start(GTK_BOX(vbox
), parent
, TRUE
, FALSE
, 0);
1565 gtk_box_pack_start(GTK_BOX(vbox
), path_entry
, TRUE
, FALSE
, 0);
1567 dirbtn
= gtk_button_new();
1568 openimg
= gtk_image_new_from_stock(GTK_STOCK_OPEN
, GTK_ICON_SIZE_BUTTON
);
1569 gtk_container_add(GTK_CONTAINER(dirbtn
), openimg
);
1570 ui_setup_open_button_callback(dirbtn
, title
, action
, entry
);
1572 gtk_box_pack_end(GTK_BOX(hbox
), dirbtn
, FALSE
, FALSE
, 0);
1573 gtk_box_pack_end(GTK_BOX(hbox
), vbox
, TRUE
, TRUE
, 0);
1578 static void ui_path_box_open_clicked(GtkButton
*button
, gpointer user_data
);
1581 /* Setup a GtkButton to run a GtkFileChooser, setting entry text if successful.
1582 * title can be NULL.
1583 * action is the file chooser mode to use. */
1584 void ui_setup_open_button_callback(GtkWidget
*open_btn
, const gchar
*title
,
1585 GtkFileChooserAction action
, GtkEntry
*entry
)
1587 GtkWidget
*path_entry
= GTK_WIDGET(entry
);
1590 g_object_set_data_full(G_OBJECT(open_btn
), "title", g_strdup(title
),
1591 (GDestroyNotify
) g_free
);
1592 g_object_set_data(G_OBJECT(open_btn
), "action", (gpointer
) action
);
1593 g_object_set_data_full(G_OBJECT(open_btn
), "entry",
1594 g_object_ref(path_entry
), (GDestroyNotify
) g_object_unref
);
1595 g_signal_connect(open_btn
, "clicked", G_CALLBACK(ui_path_box_open_clicked
), open_btn
);
1600 static gchar
*run_file_chooser(const gchar
*title
, GtkFileChooserAction action
,
1601 const gchar
*utf8_path
)
1603 GtkWidget
*dialog
= gtk_file_chooser_dialog_new(title
,
1604 GTK_WINDOW(main_widgets
.window
), action
,
1605 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1606 GTK_STOCK_OPEN
, GTK_RESPONSE_OK
, NULL
);
1608 gchar
*ret_path
= NULL
;
1610 gtk_widget_set_name(dialog
, "GeanyDialog");
1611 locale_path
= utils_get_locale_from_utf8(utf8_path
);
1612 if (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
)
1614 if (g_path_is_absolute(locale_path
) && g_file_test(locale_path
, G_FILE_TEST_IS_DIR
))
1615 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
), locale_path
);
1617 else if (action
== GTK_FILE_CHOOSER_ACTION_OPEN
)
1619 if (g_path_is_absolute(locale_path
))
1620 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog
), locale_path
);
1622 g_free(locale_path
);
1624 if (gtk_dialog_run(GTK_DIALOG(dialog
)) == GTK_RESPONSE_OK
)
1628 dir_locale
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
1629 ret_path
= utils_get_utf8_from_locale(dir_locale
);
1632 gtk_widget_destroy(dialog
);
1638 static void ui_path_box_open_clicked(GtkButton
*button
, gpointer user_data
)
1640 GtkWidget
*path_box
= GTK_WIDGET(user_data
);
1641 GtkFileChooserAction action
=
1642 (GtkFileChooserAction
) g_object_get_data(G_OBJECT(path_box
), "action");
1644 (GtkEntry
*) g_object_get_data(G_OBJECT(path_box
), "entry");
1645 const gchar
*title
= g_object_get_data(G_OBJECT(path_box
), "title");
1646 gchar
*utf8_path
= NULL
;
1648 /* TODO: extend for other actions */
1649 g_return_if_fail(action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
||
1650 action
== GTK_FILE_CHOOSER_ACTION_OPEN
);
1653 title
= (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
) ?
1654 _("Select Folder") : _("Select File");
1656 if (action
== GTK_FILE_CHOOSER_ACTION_OPEN
)
1659 utf8_path
= win32_show_file_dialog(GTK_WINDOW(ui_widgets
.prefs_dialog
), title
,
1660 gtk_entry_get_text(GTK_ENTRY(entry
)));
1662 utf8_path
= run_file_chooser(title
, action
, gtk_entry_get_text(GTK_ENTRY(entry
)));
1665 else if (action
== GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
)
1667 gchar
*path
= g_path_get_dirname(gtk_entry_get_text(GTK_ENTRY(entry
)));
1669 utf8_path
= win32_show_folder_dialog(ui_widgets
.prefs_dialog
, title
,
1670 gtk_entry_get_text(GTK_ENTRY(entry
)));
1672 utf8_path
= run_file_chooser(title
, action
, path
);
1677 if (utf8_path
!= NULL
)
1679 gtk_entry_set_text(GTK_ENTRY(entry
), utf8_path
);
1685 void ui_statusbar_showhide(gboolean state
)
1687 /* handle statusbar visibility */
1690 gtk_widget_show(ui_widgets
.statusbar
);
1691 ui_update_statusbar(NULL
, -1);
1694 gtk_widget_hide(ui_widgets
.statusbar
);
1698 /** Packs all @c GtkWidgets passed after the row argument into a table, using
1699 * one widget per cell. The first widget is not expanded as the table grows,
1700 * as this is usually a label.
1702 * @param row The row number of the table.
1704 void ui_table_add_row(GtkTable
*table
, gint row
, ...)
1710 va_start(args
, row
);
1711 for (i
= 0; (widget
= va_arg(args
, GtkWidget
*), widget
!= NULL
); i
++)
1713 gint options
= (i
== 0) ? GTK_FILL
: GTK_EXPAND
| GTK_FILL
;
1715 gtk_table_attach(GTK_TABLE(table
), widget
, i
, i
+ 1, row
, row
+ 1,
1722 static void on_config_file_clicked(GtkWidget
*widget
, gpointer user_data
)
1724 const gchar
*file_name
= user_data
;
1725 GeanyFiletype
*ft
= NULL
;
1727 if (strstr(file_name
, G_DIR_SEPARATOR_S
"filetypes."))
1728 ft
= filetypes
[GEANY_FILETYPES_CONF
];
1730 if (g_file_test(file_name
, G_FILE_TEST_EXISTS
))
1731 document_open_file(file_name
, FALSE
, ft
, NULL
);
1734 gchar
*utf8_filename
= utils_get_utf8_from_locale(file_name
);
1735 gchar
*base_name
= g_path_get_basename(file_name
);
1736 gchar
*global_file
= g_build_filename(app
->datadir
, base_name
, NULL
);
1737 gchar
*global_content
= NULL
;
1739 /* if the requested file doesn't exist in the user's config dir, try loading the file
1740 * from the global data directory and use its contents for the newly created file */
1741 if (g_file_test(global_file
, G_FILE_TEST_EXISTS
))
1742 g_file_get_contents(global_file
, &global_content
, NULL
, NULL
);
1744 document_new_file(utf8_filename
, ft
, global_content
);
1746 utils_free_pointers(4, utf8_filename
, base_name
, global_file
, global_content
, NULL
);
1751 /* @note You should connect to the "document-save" signal yourself to detect
1752 * if the user has just saved the config file, reloading it. */
1753 void ui_add_config_file_menu_item(const gchar
*real_path
, const gchar
*label
, GtkContainer
*parent
)
1758 parent
= GTK_CONTAINER(widgets
.config_files_menu
);
1764 base_name
= g_path_get_basename(real_path
);
1765 item
= gtk_menu_item_new_with_label(base_name
);
1769 item
= gtk_menu_item_new_with_mnemonic(label
);
1771 gtk_widget_show(item
);
1772 gtk_container_add(parent
, item
);
1773 g_signal_connect(item
, "activate", G_CALLBACK(on_config_file_clicked
),
1774 /* this memory is kept */
1775 g_strdup(real_path
));
1779 static gboolean
sort_menu(gpointer data
)
1781 ui_menu_sort_by_label(GTK_MENU(data
));
1786 static void create_config_files_menu(void)
1788 GtkWidget
*menu
, *item
;
1790 widgets
.config_files_menu
= menu
= gtk_menu_new();
1792 item
= ui_lookup_widget(main_widgets
.window
, "configuration_files1");
1793 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
1795 /* sort menu after all items added */
1796 g_idle_add(sort_menu
, widgets
.config_files_menu
);
1800 void ui_init_stock_items(void)
1802 GtkIconSet
*icon_set
;
1803 GtkIconFactory
*factory
= gtk_icon_factory_new();
1806 GtkStockItem items
[] =
1808 { GEANY_STOCK_SAVE_ALL
, _("Save All"), 0, 0, GETTEXT_PACKAGE
},
1809 { GEANY_STOCK_CLOSE_ALL
, _("Close All"), 0, 0, GETTEXT_PACKAGE
},
1810 { GEANY_STOCK_BUILD
, _("Build"), 0, 0, GETTEXT_PACKAGE
}
1813 len
= G_N_ELEMENTS(items
);
1814 for (i
= 0; i
< len
; i
++)
1816 pb
= ui_new_pixbuf_from_stock(items
[i
].stock_id
);
1817 icon_set
= gtk_icon_set_new_from_pixbuf(pb
);
1819 gtk_icon_factory_add(factory
, items
[i
].stock_id
, icon_set
);
1821 gtk_icon_set_unref(icon_set
);
1824 gtk_stock_add((GtkStockItem
*) items
, len
);
1825 gtk_icon_factory_add_default(factory
);
1826 g_object_unref(factory
);
1830 void ui_init_toolbar_widgets(void)
1832 widgets
.save_buttons
[1] = toolbar_get_widget_by_name("Save");
1833 widgets
.save_buttons
[3] = toolbar_get_widget_by_name("SaveAll");
1834 widgets
.redo_items
[2] = toolbar_get_widget_by_name("Redo");
1835 widgets
.undo_items
[2] = toolbar_get_widget_by_name("Undo");
1839 void ui_swap_sidebar_pos(void)
1841 GtkWidget
*pane
= ui_lookup_widget(main_widgets
.window
, "hpaned1");
1842 GtkWidget
*left
= gtk_paned_get_child1(GTK_PANED(pane
));
1843 GtkWidget
*right
= gtk_paned_get_child2(GTK_PANED(pane
));
1844 GtkWidget
*box
= ui_lookup_widget(main_widgets
.window
, "vbox1");
1846 /* reparenting avoids scintilla problem with middle click paste */
1847 gtk_widget_reparent(left
, box
);
1848 gtk_widget_reparent(right
, box
);
1849 gtk_widget_reparent(right
, pane
);
1850 gtk_widget_reparent(left
, pane
);
1852 gtk_paned_set_position(GTK_PANED(pane
), pane
->allocation
.width
1853 - gtk_paned_get_position(GTK_PANED(pane
)));
1857 static void init_recent_files(void)
1859 GtkWidget
*toolbar_recent_files_menu
;
1861 /* add recent files to the File menu */
1862 ui_widgets
.recent_files_menuitem
= ui_lookup_widget(main_widgets
.window
, "recent_files1");
1863 ui_widgets
.recent_files_menu_menubar
= gtk_menu_new();
1864 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_widgets
.recent_files_menuitem
),
1865 ui_widgets
.recent_files_menu_menubar
);
1867 /* add recent files to the toolbar Open button */
1868 toolbar_recent_files_menu
= gtk_menu_new();
1869 g_object_ref(toolbar_recent_files_menu
);
1870 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
1871 toolbar_get_action_by_name("Open")), toolbar_recent_files_menu
);
1875 static void ui_menu_move(GtkWidget
*menu
, GtkWidget
*old
, GtkWidget
*new)
1878 gtk_menu_item_set_submenu(GTK_MENU_ITEM(old
), NULL
);
1879 gtk_menu_item_set_submenu(GTK_MENU_ITEM(new), menu
);
1880 g_object_unref(menu
);
1884 static void on_editor_menu_show(GtkWidget
*item
)
1886 GtkWidget
*popup
= ui_lookup_widget(main_widgets
.editor_menu
, "commands1");
1887 GtkWidget
*bar
= ui_lookup_widget(main_widgets
.window
, "commands2");
1889 ui_menu_move(widgets
.commands_menu
, bar
, popup
);
1891 popup
= ui_lookup_widget(main_widgets
.editor_menu
, "menu_format2");
1892 bar
= ui_lookup_widget(main_widgets
.window
, "menu_format1");
1893 ui_menu_move(widgets
.format_menu
, bar
, popup
);
1897 static void on_editor_menu_hide(GtkWidget
*item
)
1899 GtkWidget
*popup
= ui_lookup_widget(main_widgets
.editor_menu
, "commands1");
1900 GtkWidget
*bar
= ui_lookup_widget(main_widgets
.window
, "commands2");
1902 ui_menu_move(widgets
.commands_menu
, popup
, bar
);
1904 popup
= ui_lookup_widget(main_widgets
.editor_menu
, "menu_format2");
1905 bar
= ui_lookup_widget(main_widgets
.window
, "menu_format1");
1906 ui_menu_move(widgets
.format_menu
, popup
, bar
);
1914 init_recent_files();
1916 ui_widgets
.statusbar
= ui_lookup_widget(main_widgets
.window
, "statusbar");
1917 ui_widgets
.print_page_setup
= ui_lookup_widget(main_widgets
.window
, "page_setup1");
1919 main_widgets
.progressbar
= progress_bar_create();
1921 widgets
.popup_goto_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "goto_tag_definition1");
1922 widgets
.popup_goto_items
[1] = ui_lookup_widget(main_widgets
.editor_menu
, "goto_tag_declaration1");
1923 widgets
.popup_goto_items
[2] = ui_lookup_widget(main_widgets
.editor_menu
, "find_usage1");
1924 widgets
.popup_goto_items
[3] = ui_lookup_widget(main_widgets
.editor_menu
, "find_document_usage1");
1925 widgets
.popup_copy_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "cut1");
1926 widgets
.popup_copy_items
[1] = ui_lookup_widget(main_widgets
.editor_menu
, "copy1");
1927 widgets
.popup_copy_items
[2] = ui_lookup_widget(main_widgets
.editor_menu
, "delete1");
1928 widgets
.menu_copy_items
[0] = ui_lookup_widget(main_widgets
.window
, "menu_cut1");
1929 widgets
.menu_copy_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_copy1");
1930 widgets
.menu_copy_items
[2] = ui_lookup_widget(main_widgets
.window
, "menu_delete1");
1931 widgets
.menu_insert_include_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "insert_include1");
1932 widgets
.menu_insert_include_items
[1] = ui_lookup_widget(main_widgets
.window
, "insert_include2");
1933 widgets
.save_buttons
[0] = ui_lookup_widget(main_widgets
.window
, "menu_save1");
1934 widgets
.save_buttons
[2] = ui_lookup_widget(main_widgets
.window
, "menu_save_all1");
1935 widgets
.redo_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "redo1");
1936 widgets
.redo_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_redo2");
1937 widgets
.undo_items
[0] = ui_lookup_widget(main_widgets
.editor_menu
, "undo1");
1938 widgets
.undo_items
[1] = ui_lookup_widget(main_widgets
.window
, "menu_undo2");
1940 item
= ui_lookup_widget(main_widgets
.window
, "menu_format1");
1941 widgets
.format_menu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(item
));
1942 item
= ui_lookup_widget(main_widgets
.window
, "commands2");
1943 widgets
.commands_menu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(item
));
1945 /* reparent edit submenus as needed */
1946 item
= main_widgets
.editor_menu
;
1947 g_signal_connect(item
, "show", G_CALLBACK(on_editor_menu_show
), NULL
);
1948 g_signal_connect(item
, "hide", G_CALLBACK(on_editor_menu_hide
), NULL
);
1950 ui_init_toolbar_widgets();
1951 init_document_widgets();
1952 create_config_files_menu();
1956 static void auto_separator_update(GeanyAutoSeparator
*autosep
)
1958 g_return_if_fail(autosep
->ref_count
>= 0);
1960 if (autosep
->widget
)
1961 ui_widget_show_hide(autosep
->widget
, autosep
->ref_count
> 0);
1965 static void on_auto_separator_item_show_hide(GtkWidget
*widget
, gpointer user_data
)
1967 GeanyAutoSeparator
*autosep
= user_data
;
1969 if (GTK_WIDGET_VISIBLE(widget
))
1970 autosep
->ref_count
++;
1972 autosep
->ref_count
--;
1973 auto_separator_update(autosep
);
1977 static void on_auto_separator_item_destroy(GtkWidget
*widget
, gpointer user_data
)
1979 GeanyAutoSeparator
*autosep
= user_data
;
1981 /* GTK_WIDGET_VISIBLE won't work now the widget is being destroyed,
1982 * so assume widget was visible */
1983 autosep
->ref_count
--;
1984 autosep
->ref_count
= MAX(autosep
->ref_count
, 0);
1985 auto_separator_update(autosep
);
1989 /* Show the separator widget if @a item or another is visible. */
1990 /* Note: This would be neater taking a widget argument, setting a "visible-count"
1991 * property, and using reference counting to keep the widget alive whilst its visible group
1993 void ui_auto_separator_add_ref(GeanyAutoSeparator
*autosep
, GtkWidget
*item
)
1995 /* set widget ptr NULL when widget destroyed */
1996 if (autosep
->ref_count
== 0)
1997 g_signal_connect(autosep
->widget
, "destroy",
1998 G_CALLBACK(gtk_widget_destroyed
), &autosep
->widget
);
2000 if (GTK_WIDGET_VISIBLE(item
))
2002 autosep
->ref_count
++;
2003 auto_separator_update(autosep
);
2005 g_signal_connect(item
, "show", G_CALLBACK(on_auto_separator_item_show_hide
), autosep
);
2006 g_signal_connect(item
, "hide", G_CALLBACK(on_auto_separator_item_show_hide
), autosep
);
2007 g_signal_connect(item
, "destroy", G_CALLBACK(on_auto_separator_item_destroy
), autosep
);
2012 * Sets @a text as the contents of the tooltip for @a widget.
2014 * @param widget The widget the tooltip should be set for.
2015 * @param text The text for the tooltip.
2019 void ui_widget_set_tooltip_text(GtkWidget
*widget
, const gchar
*text
)
2021 #if GTK_CHECK_VERSION(2, 12, 0)
2022 gtk_widget_set_tooltip_text(widget
, text
);
2024 static GtkTooltips
*tooltips
= NULL
;
2026 if (G_UNLIKELY(tooltips
== NULL
))
2027 tooltips
= GTK_TOOLTIPS(ui_lookup_widget(main_widgets
.window
, "tooltips"));
2029 gtk_tooltips_set_tip(tooltips
, widget
, text
, NULL
);
2034 /** Returns a widget from a name in a component, usually created by Glade.
2035 * Call it with the toplevel widget in the component (i.e. a window/dialog),
2036 * or alternatively any widget in the component, and the name of the widget
2037 * you want returned.
2038 * @param widget Widget with the @a widget_name property set.
2039 * @param widget_name Name to lookup.
2040 * @return The widget found.
2041 * @see ui_hookup_widget().
2045 GtkWidget
*ui_lookup_widget(GtkWidget
*widget
, const gchar
*widget_name
)
2047 GtkWidget
*parent
, *found_widget
;
2049 g_return_val_if_fail(widget
!= NULL
, NULL
);
2050 g_return_val_if_fail(widget_name
!= NULL
, NULL
);
2054 if (GTK_IS_MENU(widget
))
2055 parent
= gtk_menu_get_attach_widget(GTK_MENU(widget
));
2057 parent
= widget
->parent
;
2059 parent
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), "GladeParentKey");
2065 found_widget
= (GtkWidget
*) g_object_get_data(G_OBJECT(widget
), widget_name
);
2066 if (G_UNLIKELY(found_widget
== NULL
))
2067 g_warning("Widget not found: %s", widget_name
);
2068 return found_widget
;
2073 static guint progress_bar_timer_id
= (guint
) -1;
2076 static GtkWidget
*progress_bar_create(void)
2078 GtkWidget
*bar
= gtk_progress_bar_new();
2080 /* Set the progressbar's height to 1 to fit it in the statusbar */
2081 gtk_widget_set_size_request(bar
, -1, 1);
2082 gtk_box_pack_start (GTK_BOX(ui_widgets
.statusbar
), bar
, FALSE
, FALSE
, 3);
2088 static gboolean
progress_bar_pulse(gpointer data
)
2090 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(main_widgets
.progressbar
));
2097 * Starts a constantly pulsing progressbar in the right corner of the statusbar
2098 * (if the statusbar is visible). This is a convenience function which adds a timer to
2099 * pulse the progressbar constantly until ui_progress_bar_stop() is called.
2100 * You can use this function when you have time consuming asynchronous operation and want to
2101 * display some activity in the GUI and when you don't know about detailed progress steps.
2102 * The progressbar widget is hidden by default when it is not active. This function and
2103 * ui_progress_bar_stop() will show and hide it automatically for you.
2105 * You can also access the progressbar widget directly using @c geany->main_widgets->progressbar
2106 * and use the GtkProgressBar API to set discrete fractions to display better progress information.
2107 * In this case, you need to show and hide the widget yourself. You can find some example code
2108 * in @c src/printing.c.
2110 * @param text The text to be shown as the progress bar label or NULL to leave it empty.
2114 void ui_progress_bar_start(const gchar
*text
)
2116 g_return_if_fail(progress_bar_timer_id
== (guint
) -1);
2118 if (! interface_prefs
.statusbar_visible
)
2121 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets
.progressbar
), text
);
2123 progress_bar_timer_id
= g_timeout_add(200, progress_bar_pulse
, NULL
);
2125 gtk_widget_show(GTK_WIDGET(main_widgets
.progressbar
));
2129 /** Stops a running progress bar and hides the widget again.
2133 void ui_progress_bar_stop(void)
2135 gtk_widget_hide(GTK_WIDGET(main_widgets
.progressbar
));
2137 if (progress_bar_timer_id
!= (guint
) -1)
2139 g_source_remove(progress_bar_timer_id
);
2140 progress_bar_timer_id
= (guint
) -1;
2145 static gint
compare_menu_item_labels(gconstpointer a
, gconstpointer b
)
2147 GtkMenuItem
*item_a
= GTK_MENU_ITEM(a
);
2148 GtkMenuItem
*item_b
= GTK_MENU_ITEM(b
);
2152 sa
= ui_menu_item_get_text(item_a
);
2153 sb
= ui_menu_item_get_text(item_b
);
2154 result
= utils_str_casecmp(sa
, sb
);
2161 /* Currently @a menu should contain only GtkMenuItems with labels. */
2162 void ui_menu_sort_by_label(GtkMenu
*menu
)
2164 GList
*list
= gtk_container_get_children(GTK_CONTAINER(menu
));
2168 list
= g_list_sort(list
, compare_menu_item_labels
);
2170 foreach_list(node
, list
)
2172 gtk_menu_reorder_child(menu
, node
->data
, pos
);
2179 /* return value is for macros */
2180 GtkWidget
*ui_label_set_markup(GtkLabel
*label
, const gchar
*format
, ...)
2185 va_start(a
, format
);
2186 text
= g_strdup_vprintf(format
, a
);
2189 gtk_label_set_text(label
, text
);
2190 gtk_label_set_use_markup(label
, TRUE
);
2192 return GTK_WIDGET(label
);
2196 /** Adds a list of document items to @a menu.
2198 * @param active Which document to highlight, or @c NULL.
2199 * @param callback is used for each menu item's @c "activate" signal and will be passed
2200 * the corresponding document pointer as @c user_data.
2201 * @warning You should check @c doc->is_valid in the callback.
2203 void ui_menu_add_document_items(GtkMenu
*menu
, GeanyDocument
*active
, GCallback callback
)
2205 GtkWidget
*menu_item
, *menu_item_label
;
2206 const GdkColor
*color
;
2211 len
= gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets
.notebook
));
2212 for (i
= 0; i
< len
; i
++)
2214 doc
= document_get_from_page(i
);
2215 if (! DOC_VALID(doc
))
2218 base_name
= g_path_get_basename(DOC_FILENAME(doc
));
2219 menu_item
= gtk_menu_item_new_with_label(base_name
);
2220 gtk_widget_show(menu_item
);
2221 gtk_container_add(GTK_CONTAINER(menu
), menu_item
);
2222 g_signal_connect(menu_item
, "activate", callback
, doc
);
2224 color
= document_get_status_color(doc
);
2225 menu_item_label
= gtk_bin_get_child(GTK_BIN(menu_item
));
2226 gtk_widget_modify_fg(menu_item_label
, GTK_STATE_NORMAL
, color
);
2227 gtk_widget_modify_fg(menu_item_label
, GTK_STATE_ACTIVE
, color
);
2230 ui_label_set_markup(GTK_LABEL(menu_item_label
), "<b>%s</b>", base_name
);
2237 /** Checks whether the passed @a keyval is the Enter or Return key.
2238 * There are three different Enter/Return key values
2239 * (@c GDK_Return, @c GDK_ISO_Enter, @c GDK_KP_Enter).
2240 * This is just a convenience function.
2241 * @param keyval A keyval.
2242 * @return @c TRUE if @a keyval is the one of the Enter/Return key values, otherwise @c FALSE.
2244 gboolean
ui_is_keyval_enter_or_return(guint keyval
)
2246 return (keyval
== GDK_Return
|| keyval
== GDK_ISO_Enter
|| keyval
== GDK_KP_Enter
);
2250 /** Reads an integer from the GTK default settings registry
2251 * (see http://library.gnome.org/devel/gtk/stable/GtkSettings.html).
2252 * @param property_name The property to read.
2253 * @param default_value The default value in case the value could not be read.
2254 * @return The value for the property if it exists, otherwise the @a default_value.
2256 gint
ui_get_gtk_settings_integer(const gchar
*property_name
, gint default_value
)
2258 if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(
2259 gtk_settings_get_default())), property_name
))
2262 g_object_get(G_OBJECT(gtk_settings_get_default()), property_name
, &value
, NULL
);
2266 return default_value
;