Add note to ui_hookup_widget() doc comments.
[geany-mirror.git] / src / ui_utils.c
blob9e1f8bb7b978838322940776c1f351bf6fa4798d
1 /*
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.
23 /** @file ui_utils.h
24 * User Interface general utility functions.
27 #include "geany.h"
29 #include "support.h"
31 #include <string.h>
32 #include <ctype.h>
33 #include <gdk/gdkkeysyms.h>
35 #include "ui_utils.h"
36 #include "prefs.h"
37 #include "sciwrappers.h"
38 #include "document.h"
39 #include "documentprivate.h"
40 #include "filetypes.h"
41 #include "support.h"
42 #include "msgwindow.h"
43 #include "utils.h"
44 #include "callbacks.h"
45 #include "encodings.h"
46 #include "images.c"
47 #include "sidebar.h"
48 #include "win32.h"
49 #include "project.h"
50 #include "editor.h"
51 #include "plugins.h"
52 #include "symbols.h"
53 #include "toolbar.h"
54 #include "geanymenubuttonaction.h"
55 #include "main.h"
56 #include "stash.h"
57 #include "keyfile.h"
60 GHashTable* objects_table = NULL;
62 GeanyInterfacePrefs interface_prefs;
63 GeanyMainWidgets main_widgets;
65 UIPrefs ui_prefs;
66 UIWidgets ui_widgets;
68 static struct
70 /* pointers to widgets only sensitive when there is at least one document, the pointers can
71 * also be GtkAction objects, so check each pointer before using it */
72 GPtrArray *document_buttons;
73 GtkWidget *menu_insert_include_items[2];
74 GtkWidget *popup_goto_items[4];
75 GtkWidget *popup_copy_items[3];
76 GtkWidget *menu_copy_items[3];
77 GtkWidget *redo_items[3];
78 GtkWidget *undo_items[3];
79 GtkWidget *save_buttons[4];
80 GtkWidget *config_files_menu;
82 widgets;
84 enum
86 RECENT_FILE_FILE,
87 RECENT_FILE_PROJECT
90 typedef struct
92 gint type;
93 GQueue *recent_queue;
94 GtkWidget *menubar;
95 GtkWidget *toolbar;
96 void (*activate_cb)(GtkMenuItem *, gpointer);
97 } GeanyRecentFiles;
100 static void update_recent_menu(GeanyRecentFiles *grf);
101 static void recent_file_loaded(const gchar *utf8_filename, GeanyRecentFiles *grf);
102 static void recent_file_activate_cb(GtkMenuItem *menuitem, gpointer user_data);
103 static void recent_project_activate_cb(GtkMenuItem *menuitem, gpointer user_data);
104 static GtkWidget *progress_bar_create(void);
107 /* simple wrapper for gtk_widget_set_sensitive() to allow widget being NULL */
108 void ui_widget_set_sensitive(GtkWidget *widget, gboolean set)
110 if (widget != NULL)
111 gtk_widget_set_sensitive(widget, set);
115 /* allow_override is TRUE if text can be ignored when another message has been set
116 * that didn't use allow_override and has not timed out. */
117 static void set_statusbar(const gchar *text, gboolean allow_override)
119 static glong last_time = 0;
120 GTimeVal timeval;
121 const gint GEANY_STATUS_TIMEOUT = 1;
123 if (! interface_prefs.statusbar_visible)
124 return; /* just do nothing if statusbar is not visible */
126 g_get_current_time(&timeval);
128 if (! allow_override)
130 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets.statusbar), 1);
131 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets.statusbar), 1, text);
132 last_time = timeval.tv_sec;
134 else
135 if (timeval.tv_sec > last_time + GEANY_STATUS_TIMEOUT)
137 gtk_statusbar_pop(GTK_STATUSBAR(ui_widgets.statusbar), 1);
138 gtk_statusbar_push(GTK_STATUSBAR(ui_widgets.statusbar), 1, text);
143 /** Displays text on the statusbar.
144 * @param log Whether the message should be recorded in the Status window.
145 * @param format A @c printf -style string. */
146 void ui_set_statusbar(gboolean log, const gchar *format, ...)
148 gchar *string;
149 va_list args;
151 va_start(args, format);
152 string = g_strdup_vprintf(format, args);
153 va_end(args);
155 if (! prefs.suppress_status_messages)
156 set_statusbar(string, FALSE);
158 if (log || prefs.suppress_status_messages)
159 msgwin_status_add("%s", string);
161 g_free(string);
165 static gchar *statusbar_template = NULL;
167 /* note: some comments below are for translators */
168 static void add_statusbar_statistics(GString *stats_str,
169 GeanyDocument *doc, guint line, guint col)
171 const gchar *cur_tag;
172 const gchar *fmt;
173 const gchar *expos; /* % expansion position */
174 const gchar sp[] = " ";
176 fmt = NZV(statusbar_template) ? statusbar_template :
177 /* Status bar statistics: col = column, sel = selection. */
178 _("line: %l / %L\t col: %c\t sel: %s\t %w %t %m"
179 "mode: %M encoding: %e filetype: %f scope: %S");
181 g_string_assign(stats_str, "");
182 while ((expos = strchr(fmt, '%')) != NULL)
184 /* append leading text before % char */
185 g_string_append_len(stats_str, fmt, expos - fmt);
187 switch (*++expos)
189 case 'l':
190 g_string_append_printf(stats_str, "%d", line + 1);
191 break;
192 case 'L':
193 g_string_append_printf(stats_str, "%d",
194 sci_get_line_count(doc->editor->sci));
195 break;
196 case 'c':
197 g_string_append_printf(stats_str, "%d", col);
198 break;
199 case 'C':
200 g_string_append_printf(stats_str, "%d", col + 1);
201 break;
202 case 's':
203 g_string_append_printf(stats_str, "%d",
204 sci_get_selected_text_length(doc->editor->sci) - 1);
205 break;
206 case 'w':
207 /* RO = read-only */
208 g_string_append(stats_str, (doc->readonly) ? _("RO ") :
209 /* OVR = overwrite/overtype, INS = insert */
210 (sci_get_overtype(doc->editor->sci) ? _("OVR") : _("INS")));
211 break;
212 case 'r':
213 if (doc->readonly)
215 g_string_append(stats_str, _("RO ")); /* RO = read-only */
216 g_string_append(stats_str, sp + 1);
218 break;
219 case 't':
221 switch (editor_get_indent_prefs(doc->editor)->type)
223 case GEANY_INDENT_TYPE_TABS:
224 g_string_append(stats_str, _("TAB"));
225 break;
226 case GEANY_INDENT_TYPE_SPACES: /* SP = space */
227 g_string_append(stats_str, _("SP"));
228 break;
229 case GEANY_INDENT_TYPE_BOTH: /* T/S = tabs and spaces */
230 g_string_append(stats_str, _("T/S"));
231 break;
233 break;
235 case 'm':
236 if (doc->changed)
238 g_string_append(stats_str, _("MOD")); /* MOD = modified */
239 g_string_append(stats_str, sp);
241 break;
242 case 'M':
243 g_string_append(stats_str, editor_get_eol_char_name(doc->editor));
244 break;
245 case 'e':
246 g_string_append(stats_str,
247 doc->encoding ? doc->encoding : _("unknown"));
248 if (encodings_is_unicode_charset(doc->encoding) && (doc->has_bom))
250 g_string_append_c(stats_str, ' ');
251 g_string_append(stats_str, _("(with BOM)")); /* BOM = byte order mark */
253 break;
254 case 'f':
255 g_string_append(stats_str, filetypes_get_display_name(doc->file_type));
256 break;
257 case 'S':
258 symbols_get_current_function(doc, &cur_tag);
259 g_string_append(stats_str, cur_tag);
260 break;
261 default:
262 g_string_append_len(stats_str, expos, 1);
265 /* skip past %c chars */
266 if (*expos)
267 fmt = expos + 1;
268 else
269 break;
271 /* add any remaining text */
272 g_string_append(stats_str, fmt);
276 /* updates the status bar document statistics */
277 void ui_update_statusbar(GeanyDocument *doc, gint pos)
279 if (! interface_prefs.statusbar_visible)
280 return; /* just do nothing if statusbar is not visible */
282 if (doc == NULL)
283 doc = document_get_current();
285 if (doc != NULL)
287 static GString *stats_str = NULL;
288 guint line, col;
290 if (G_UNLIKELY(stats_str == NULL))
291 stats_str = g_string_sized_new(120);
293 if (pos == -1)
294 pos = sci_get_current_position(doc->editor->sci);
295 line = sci_get_line_from_position(doc->editor->sci, pos);
297 /* Add temporary fix for sci infinite loop in Document::GetColumn(int)
298 * when current pos is beyond document end (can occur when removing
299 * blocks of selected lines especially esp. brace sections near end of file). */
300 if (pos <= sci_get_length(doc->editor->sci))
301 col = sci_get_col_from_position(doc->editor->sci, pos);
302 else
303 col = 0;
305 add_statusbar_statistics(stats_str, doc, line, col);
307 #ifdef GEANY_DEBUG
309 const gchar sp[] = " ";
310 g_string_append(stats_str, sp);
311 g_string_append_printf(stats_str, "pos: %d", pos);
312 g_string_append(stats_str, sp);
313 g_string_append_printf(stats_str, "style: %d", sci_get_style_at(doc->editor->sci, pos));
315 #endif
316 /* can be overridden by status messages */
317 set_statusbar(stats_str->str, TRUE);
319 else /* no documents */
321 set_statusbar("", TRUE); /* can be overridden by status messages */
326 /* This sets the window title according to the current filename. */
327 void ui_set_window_title(GeanyDocument *doc)
329 GString *str;
330 GeanyProject *project = app->project;
332 if (doc == NULL)
333 doc = document_get_current();
335 str = g_string_new(NULL);
337 if (doc != NULL)
339 g_string_append(str, doc->changed ? "*" : "");
341 if (doc->file_name == NULL)
342 g_string_append(str, DOC_FILENAME(doc));
343 else
345 gchar *short_name = document_get_basename_for_display(doc, 30);
346 gchar *dirname = g_path_get_dirname(DOC_FILENAME(doc));
348 g_string_append(str, short_name);
349 g_string_append(str, " - ");
350 g_string_append(str, dirname ? dirname : "");
351 g_free(short_name);
352 g_free(dirname);
354 g_string_append(str, " - ");
356 if (project)
358 g_string_append_c(str, '[');
359 g_string_append(str, project->name);
360 g_string_append(str, "] - ");
362 g_string_append(str, "Geany");
363 if (cl_options.new_instance)
365 g_string_append(str, _(" (new instance)"));
367 gtk_window_set_title(GTK_WINDOW(main_widgets.window), str->str);
368 g_string_free(str, TRUE);
372 void ui_set_editor_font(const gchar *font_name)
374 guint i;
376 g_return_if_fail(font_name != NULL);
378 /* do nothing if font has not changed */
379 if (interface_prefs.editor_font != NULL)
380 if (strcmp(font_name, interface_prefs.editor_font) == 0)
381 return;
383 g_free(interface_prefs.editor_font);
384 interface_prefs.editor_font = g_strdup(font_name);
386 /* We copy the current style, and update the font in all open tabs. */
387 for (i = 0; i < documents_array->len; i++)
389 if (documents[i]->editor)
391 editor_set_font(documents[i]->editor, interface_prefs.editor_font);
395 ui_set_statusbar(TRUE, _("Font updated (%s)."), interface_prefs.editor_font);
399 void ui_set_fullscreen(void)
401 if (ui_prefs.fullscreen)
403 gtk_window_fullscreen(GTK_WINDOW(main_widgets.window));
405 else
407 gtk_window_unfullscreen(GTK_WINDOW(main_widgets.window));
412 void ui_update_popup_reundo_items(GeanyDocument *doc)
414 gboolean enable_undo;
415 gboolean enable_redo;
416 guint i, len;
418 if (doc == NULL)
420 enable_undo = FALSE;
421 enable_redo = FALSE;
423 else
425 enable_undo = document_can_undo(doc);
426 enable_redo = document_can_redo(doc);
429 /* index 0 is the popup menu, 1 is the menubar, 2 is the toolbar */
430 len = G_N_ELEMENTS(widgets.undo_items);
431 for (i = 0; i < len; i++)
433 ui_widget_set_sensitive(widgets.undo_items[i], enable_undo);
435 len = G_N_ELEMENTS(widgets.redo_items);
436 for (i = 0; i < len; i++)
438 ui_widget_set_sensitive(widgets.redo_items[i], enable_redo);
443 void ui_update_popup_copy_items(GeanyDocument *doc)
445 gboolean enable;
446 guint i, len;
448 if (doc == NULL)
449 enable = FALSE;
450 else
451 enable = sci_has_selection(doc->editor->sci);
453 len = G_N_ELEMENTS(widgets.popup_copy_items);
454 for (i = 0; i < len; i++)
455 ui_widget_set_sensitive(widgets.popup_copy_items[i], enable);
459 void ui_update_popup_goto_items(gboolean enable)
461 guint i, len;
462 len = G_N_ELEMENTS(widgets.popup_goto_items);
463 for (i = 0; i < len; i++)
464 ui_widget_set_sensitive(widgets.popup_goto_items[i], enable);
468 void ui_update_menu_copy_items(GeanyDocument *doc)
470 gboolean enable = FALSE;
471 guint i, len;
472 GtkWidget *focusw = gtk_window_get_focus(GTK_WINDOW(main_widgets.window));
474 if (IS_SCINTILLA(focusw))
475 enable = (doc == NULL) ? FALSE : sci_has_selection(doc->editor->sci);
476 else
477 if (GTK_IS_EDITABLE(focusw))
478 enable = gtk_editable_get_selection_bounds(GTK_EDITABLE(focusw), NULL, NULL);
479 else
480 if (GTK_IS_TEXT_VIEW(focusw))
482 GtkTextBuffer *buffer = gtk_text_view_get_buffer(
483 GTK_TEXT_VIEW(focusw));
484 enable = gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL);
487 len = G_N_ELEMENTS(widgets.menu_copy_items);
488 for (i = 0; i < len; i++)
489 ui_widget_set_sensitive(widgets.menu_copy_items[i], enable);
493 void ui_update_insert_include_item(GeanyDocument *doc, gint item)
495 gboolean enable = FALSE;
497 if (doc == NULL || doc->file_type == NULL)
498 enable = FALSE;
499 else if (doc->file_type->id == GEANY_FILETYPES_C || doc->file_type->id == GEANY_FILETYPES_CPP)
500 enable = TRUE;
502 ui_widget_set_sensitive(widgets.menu_insert_include_items[item], enable);
506 void ui_update_fold_items(void)
508 ui_widget_show_hide(ui_lookup_widget(main_widgets.window, "menu_fold_all1"), editor_prefs.folding);
509 ui_widget_show_hide(ui_lookup_widget(main_widgets.window, "menu_unfold_all1"), editor_prefs.folding);
510 ui_widget_show_hide(ui_lookup_widget(main_widgets.window, "separator22"), editor_prefs.folding);
514 static void insert_include_items(GtkMenu *me, GtkMenu *mp, gchar **includes, gchar *label)
516 guint i = 0;
517 GtkWidget *tmp_menu;
518 GtkWidget *tmp_popup;
519 GtkWidget *edit_menu, *edit_menu_item;
520 GtkWidget *popup_menu, *popup_menu_item;
522 edit_menu = gtk_menu_new();
523 popup_menu = gtk_menu_new();
524 edit_menu_item = gtk_menu_item_new_with_label(label);
525 popup_menu_item = gtk_menu_item_new_with_label(label);
526 gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_menu_item), edit_menu);
527 gtk_menu_item_set_submenu(GTK_MENU_ITEM(popup_menu_item), popup_menu);
529 while (includes[i] != NULL)
531 tmp_menu = gtk_menu_item_new_with_label(includes[i]);
532 tmp_popup = gtk_menu_item_new_with_label(includes[i]);
533 gtk_container_add(GTK_CONTAINER(edit_menu), tmp_menu);
534 gtk_container_add(GTK_CONTAINER(popup_menu), tmp_popup);
535 g_signal_connect(tmp_menu, "activate",
536 G_CALLBACK(on_menu_insert_include_activate), (gpointer) includes[i]);
537 g_signal_connect(tmp_popup, "activate",
538 G_CALLBACK(on_insert_include_activate), (gpointer) includes[i]);
539 i++;
541 gtk_widget_show_all(edit_menu_item);
542 gtk_widget_show_all(popup_menu_item);
543 gtk_container_add(GTK_CONTAINER(me), edit_menu_item);
544 gtk_container_add(GTK_CONTAINER(mp), popup_menu_item);
548 void ui_create_insert_menu_items(void)
550 GtkMenu *menu_edit = GTK_MENU(ui_lookup_widget(main_widgets.window, "insert_include2_menu"));
551 GtkMenu *menu_popup = GTK_MENU(ui_lookup_widget(main_widgets.editor_menu, "insert_include1_menu"));
552 GtkWidget *blank;
553 const gchar *c_includes_stdlib[] = {
554 "assert.h", "ctype.h", "errno.h", "float.h", "limits.h", "locale.h", "math.h", "setjmp.h",
555 "signal.h", "stdarg.h", "stddef.h", "stdio.h", "stdlib.h", "string.h", "time.h", NULL
557 const gchar *c_includes_c99[] = {
558 "complex.h", "fenv.h", "inttypes.h", "iso646.h", "stdbool.h", "stdint.h",
559 "tgmath.h", "wchar.h", "wctype.h", NULL
561 const gchar *c_includes_cpp[] = {
562 "cstdio", "cstring", "cctype", "cmath", "ctime", "cstdlib", "cstdarg", NULL
564 const gchar *c_includes_cppstdlib[] = {
565 "iostream", "fstream", "iomanip", "sstream", "exception", "stdexcept",
566 "memory", "locale", NULL
568 const gchar *c_includes_stl[] = {
569 "bitset", "dequev", "list", "map", "set", "queue", "stack", "vector", "algorithm",
570 "iterator", "functional", "string", "complex", "valarray", NULL
573 blank = gtk_menu_item_new_with_label("#include \"...\"");
574 gtk_container_add(GTK_CONTAINER(menu_edit), blank);
575 gtk_widget_show(blank);
576 g_signal_connect(blank, "activate", G_CALLBACK(on_menu_insert_include_activate),
577 (gpointer) "blank");
578 blank = gtk_separator_menu_item_new ();
579 gtk_container_add(GTK_CONTAINER(menu_edit), blank);
580 gtk_widget_show(blank);
582 blank = gtk_menu_item_new_with_label("#include \"...\"");
583 gtk_container_add(GTK_CONTAINER(menu_popup), blank);
584 gtk_widget_show(blank);
585 g_signal_connect(blank, "activate", G_CALLBACK(on_insert_include_activate),
586 (gpointer) "blank");
587 blank = gtk_separator_menu_item_new();
588 gtk_container_add(GTK_CONTAINER(menu_popup), blank);
589 gtk_widget_show(blank);
591 insert_include_items(menu_edit, menu_popup, (gchar**) c_includes_stdlib, _("C Standard Library"));
592 insert_include_items(menu_edit, menu_popup, (gchar**) c_includes_c99, _("ISO C99"));
593 insert_include_items(menu_edit, menu_popup, (gchar**) c_includes_cpp, _("C++ (C Standard Library)"));
594 insert_include_items(menu_edit, menu_popup, (gchar**) c_includes_cppstdlib, _("C++ Standard Library"));
595 insert_include_items(menu_edit, menu_popup, (gchar**) c_includes_stl, _("C++ STL"));
599 static void insert_date_items(GtkMenu *me, GtkMenu *mp, gchar *label)
601 GtkWidget *item;
603 item = gtk_menu_item_new_with_mnemonic(label);
604 gtk_container_add(GTK_CONTAINER(me), item);
605 gtk_widget_show(item);
606 g_signal_connect(item, "activate", G_CALLBACK(on_menu_insert_date_activate), label);
608 item = gtk_menu_item_new_with_mnemonic(label);
609 gtk_container_add(GTK_CONTAINER(mp), item);
610 gtk_widget_show(item);
611 g_signal_connect(item, "activate", G_CALLBACK(on_insert_date_activate), label);
615 void ui_create_insert_date_menu_items(void)
617 GtkMenu *menu_edit = GTK_MENU(ui_lookup_widget(main_widgets.window, "insert_date1_menu"));
618 GtkMenu *menu_popup = GTK_MENU(ui_lookup_widget(main_widgets.editor_menu, "insert_date2_menu"));
619 GtkWidget *item;
620 gchar *str;
622 insert_date_items(menu_edit, menu_popup, _("dd.mm.yyyy"));
623 insert_date_items(menu_edit, menu_popup, _("mm.dd.yyyy"));
624 insert_date_items(menu_edit, menu_popup, _("yyyy/mm/dd"));
626 item = gtk_separator_menu_item_new();
627 gtk_container_add(GTK_CONTAINER(menu_edit), item);
628 gtk_widget_show(item);
629 item = gtk_separator_menu_item_new();
630 gtk_container_add(GTK_CONTAINER(menu_popup), item);
631 gtk_widget_show(item);
633 insert_date_items(menu_edit, menu_popup, _("dd.mm.yyyy hh:mm:ss"));
634 insert_date_items(menu_edit, menu_popup, _("mm.dd.yyyy hh:mm:ss"));
635 insert_date_items(menu_edit, menu_popup, _("yyyy/mm/dd hh:mm:ss"));
637 item = gtk_separator_menu_item_new();
638 gtk_container_add(GTK_CONTAINER(menu_edit), item);
639 gtk_widget_show(item);
640 item = gtk_separator_menu_item_new();
641 gtk_container_add(GTK_CONTAINER(menu_popup), item);
642 gtk_widget_show(item);
644 str = _("_Use Custom Date Format");
645 item = gtk_menu_item_new_with_mnemonic(str);
646 gtk_container_add(GTK_CONTAINER(menu_edit), item);
647 gtk_widget_show(item);
648 g_signal_connect(item, "activate", G_CALLBACK(on_menu_insert_date_activate), str);
649 ui_hookup_widget(main_widgets.window, item, "insert_date_custom1");
651 item = gtk_menu_item_new_with_mnemonic(str);
652 gtk_container_add(GTK_CONTAINER(menu_popup), item);
653 gtk_widget_show(item);
654 g_signal_connect(item, "activate", G_CALLBACK(on_insert_date_activate), str);
655 ui_hookup_widget(main_widgets.editor_menu, item, "insert_date_custom2");
657 insert_date_items(menu_edit, menu_popup, _("_Set Custom Date Format"));
661 void ui_save_buttons_toggle(gboolean enable)
663 guint i;
664 gboolean dirty_tabs = FALSE;
666 if (ui_prefs.allow_always_save)
667 enable = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook)) > 0 ? TRUE : FALSE;
669 ui_widget_set_sensitive(widgets.save_buttons[0], enable);
670 ui_widget_set_sensitive(widgets.save_buttons[1], enable);
672 /* save all menu item and tool button */
673 for (i = 0; i < documents_array->len; i++)
675 /* check whether there are files where changes were made and if there are some,
676 * we need the save all button / item */
677 if (documents[i]->is_valid && documents[i]->changed)
679 dirty_tabs = TRUE;
680 break;
684 ui_widget_set_sensitive(widgets.save_buttons[2], dirty_tabs);
685 ui_widget_set_sensitive(widgets.save_buttons[3], dirty_tabs);
689 #define add_doc_widget(widget_name) \
690 g_ptr_array_add(widgets.document_buttons, ui_lookup_widget(main_widgets.window, widget_name))
692 #define add_doc_toolitem(widget_name) \
693 g_ptr_array_add(widgets.document_buttons, toolbar_get_action_by_name(widget_name))
695 static void init_document_widgets(void)
697 widgets.document_buttons = g_ptr_array_new();
699 /* Cache the document-sensitive widgets so we don't have to keep looking them up
700 * when using ui_document_buttons_update(). */
701 add_doc_widget("menu_close1");
702 add_doc_widget("close_other_documents1");
703 add_doc_widget("menu_change_font1");
704 add_doc_widget("menu_close_all1");
705 add_doc_widget("menu_save1");
706 add_doc_widget("menu_save_all1");
707 add_doc_widget("menu_save_as1");
708 add_doc_widget("menu_count_words1");
709 add_doc_widget("menu_build1");
710 add_doc_widget("add_comments1");
711 add_doc_widget("menu_paste1");
712 add_doc_widget("menu_undo2");
713 add_doc_widget("preferences2");
714 add_doc_widget("menu_reload1");
715 add_doc_widget("menu_document1");
716 add_doc_widget("menu_choose_color1");
717 add_doc_widget("menu_zoom_in1");
718 add_doc_widget("menu_zoom_out1");
719 add_doc_widget("menu_view_editor1");
720 add_doc_widget("normal_size1");
721 add_doc_widget("treeview6");
722 add_doc_widget("print1");
723 add_doc_widget("menu_reload_as1");
724 add_doc_widget("menu_select_all1");
725 add_doc_widget("insert_date1");
726 add_doc_widget("insert_alternative_white_space1");
727 add_doc_widget("menu_format1");
728 add_doc_widget("commands2");
729 add_doc_widget("menu_open_selected_file1");
730 add_doc_widget("page_setup1");
731 add_doc_widget("find1");
732 add_doc_widget("find_next1");
733 add_doc_widget("find_previous1");
734 add_doc_widget("go_to_next_marker1");
735 add_doc_widget("go_to_previous_marker1");
736 add_doc_widget("replace1");
737 add_doc_widget("find_nextsel1");
738 add_doc_widget("find_prevsel1");
739 add_doc_widget("find_usage1");
740 add_doc_widget("find_document_usage1");
741 add_doc_widget("mark_all1");
742 add_doc_widget("go_to_line1");
743 add_doc_widget("goto_tag_definition1");
744 add_doc_widget("goto_tag_declaration1");
745 add_doc_widget("reset_indentation1");
746 add_doc_toolitem("Close");
747 add_doc_toolitem("CloseAll");
748 add_doc_toolitem("Search");
749 add_doc_toolitem("SearchEntry");
750 add_doc_toolitem("NavBack");
751 add_doc_toolitem("NavFor");
752 add_doc_toolitem("ZoomIn");
753 add_doc_toolitem("ZoomOut");
754 add_doc_toolitem("Indent");
755 add_doc_toolitem("UnIndent");
756 add_doc_toolitem("Cut");
757 add_doc_toolitem("Copy");
758 add_doc_toolitem("Paste");
759 add_doc_toolitem("Delete");
760 add_doc_toolitem("Save");
761 add_doc_toolitem("SaveAs");
762 add_doc_toolitem("SaveAll");
763 add_doc_toolitem("Compile");
764 add_doc_toolitem("Run");
765 add_doc_toolitem("Reload");
766 add_doc_toolitem("Color");
767 add_doc_toolitem("Goto");
768 add_doc_toolitem("GotoEntry");
769 add_doc_toolitem("Replace");
770 add_doc_toolitem("Print");
774 void ui_document_buttons_update(void)
776 guint i;
777 gboolean enable = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook)) ? TRUE : FALSE;
779 for (i = 0; i < widgets.document_buttons->len; i++)
781 GtkWidget *widget = g_ptr_array_index(widgets.document_buttons, i);
782 if (GTK_IS_ACTION(widget))
783 gtk_action_set_sensitive(GTK_ACTION(widget), enable);
784 else
785 ui_widget_set_sensitive(widget, enable);
790 static void on_doc_sensitive_widget_destroy(GtkWidget *widget, G_GNUC_UNUSED gpointer user_data)
792 g_ptr_array_remove_fast(widgets.document_buttons, widget);
796 /** Adds a widget to the list of widgets that should be set sensitive/insensitive
797 * when some documents are present/no documents are open.
798 * It will be removed when the widget is destroyed.
799 * @param widget The widget to add.
801 * @since 0.15
803 void ui_add_document_sensitive(GtkWidget *widget)
805 gboolean enable = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook)) ? TRUE : FALSE;
807 ui_widget_set_sensitive(widget, enable);
809 g_ptr_array_add(widgets.document_buttons, widget);
810 g_signal_connect(widget, "destroy", G_CALLBACK(on_doc_sensitive_widget_destroy), NULL);
814 void ui_widget_show_hide(GtkWidget *widget, gboolean show)
816 if (show)
818 gtk_widget_show(widget);
820 else
822 gtk_widget_hide(widget);
827 void ui_sidebar_show_hide(void)
829 GtkWidget *widget;
831 /* check that there are no other notebook pages before hiding the sidebar completely
832 * other pages could be e.g. the file browser plugin */
833 if (! interface_prefs.sidebar_openfiles_visible && ! interface_prefs.sidebar_symbol_visible &&
834 gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.sidebar_notebook)) <= 2)
836 ui_prefs.sidebar_visible = FALSE;
839 widget = ui_lookup_widget(main_widgets.window, "menu_show_sidebar1");
840 if (ui_prefs.sidebar_visible != gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
842 ignore_callback = TRUE;
843 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), ui_prefs.sidebar_visible);
844 ignore_callback = FALSE;
847 ui_widget_show_hide(main_widgets.sidebar_notebook, ui_prefs.sidebar_visible);
849 ui_widget_show_hide(gtk_notebook_get_nth_page(
850 GTK_NOTEBOOK(main_widgets.sidebar_notebook), 0), interface_prefs.sidebar_symbol_visible);
851 ui_widget_show_hide(gtk_notebook_get_nth_page(
852 GTK_NOTEBOOK(main_widgets.sidebar_notebook), 1), interface_prefs.sidebar_openfiles_visible);
856 void ui_document_show_hide(GeanyDocument *doc)
858 const gchar *widget_name;
859 GtkWidget *item;
860 const GeanyIndentPrefs *iprefs;
862 if (doc == NULL)
863 doc = document_get_current();
865 if (doc == NULL)
866 return;
868 ignore_callback = TRUE;
870 gtk_check_menu_item_set_active(
871 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_line_wrapping1")),
872 doc->editor->line_wrapping);
874 gtk_check_menu_item_set_active(
875 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "line_breaking1")),
876 doc->editor->line_breaking);
878 iprefs = editor_get_indent_prefs(doc->editor);
880 item = ui_lookup_widget(main_widgets.window, "menu_use_auto_indentation1");
881 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), doc->editor->auto_indent);
883 switch (iprefs->type)
885 case GEANY_INDENT_TYPE_SPACES:
886 widget_name = "spaces1"; break;
887 case GEANY_INDENT_TYPE_TABS:
888 widget_name = "tabs1"; break;
889 case GEANY_INDENT_TYPE_BOTH:
890 default:
891 widget_name = "tabs_and_spaces1"; break;
893 item = ui_lookup_widget(main_widgets.window, widget_name);
894 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
896 if (iprefs->width >= 1 && iprefs->width <= 8)
898 gchar *name;
900 name = g_strdup_printf("indent_width_%d", iprefs->width);
901 item = ui_lookup_widget(main_widgets.window, name);
902 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
903 g_free(name);
906 gtk_check_menu_item_set_active(
907 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "set_file_readonly1")),
908 doc->readonly);
910 item = ui_lookup_widget(main_widgets.window, "menu_write_unicode_bom1");
911 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), doc->has_bom);
912 ui_widget_set_sensitive(item, encodings_is_unicode_charset(doc->encoding));
914 switch (sci_get_eol_mode(doc->editor->sci))
916 case SC_EOL_CR: widget_name = "cr"; break;
917 case SC_EOL_LF: widget_name = "lf"; break;
918 default: widget_name = "crlf"; break;
920 gtk_check_menu_item_set_active(
921 GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, widget_name)), TRUE);
923 encodings_select_radio_item(doc->encoding);
924 filetypes_select_radio_item(doc->file_type);
926 ignore_callback = FALSE;
930 void ui_set_search_entry_background(GtkWidget *widget, gboolean success)
932 static const GdkColor red = {0, 0xffff, 0x6666, 0x6666};
933 static const GdkColor white = {0, 0xffff, 0xffff, 0xffff};
934 static gboolean old_value = TRUE;
936 g_return_if_fail(widget != NULL);
938 /* update only if really needed */
939 if (old_value != success)
941 gtk_widget_modify_base(widget, GTK_STATE_NORMAL, success ? NULL : &red);
942 gtk_widget_modify_text(widget, GTK_STATE_NORMAL, success ? NULL : &white);
944 old_value = success;
949 static gboolean have_tango_icon_theme(void)
951 static gboolean result = FALSE;
952 static gboolean checked = FALSE;
954 if (! checked)
956 gchar *theme_name;
958 g_object_get(G_OBJECT(gtk_settings_get_default()), "gtk-icon-theme-name", &theme_name, NULL);
959 setptr(theme_name, g_utf8_strdown(theme_name, -1));
961 result = (strstr(theme_name, "tango") != NULL);
962 checked = TRUE;
964 g_free(theme_name);
967 return result;
971 /* Note: remember to unref the pixbuf once an image or window has added a reference. */
972 GdkPixbuf *ui_new_pixbuf_from_inline(gint img)
974 switch (img)
976 case GEANY_IMAGE_LOGO:
977 return gdk_pixbuf_new_from_inline(-1, aladin_inline, FALSE, NULL);
978 break;
979 case GEANY_IMAGE_SAVE_ALL:
981 /* check whether the icon theme looks like a Gnome icon theme, if so use the
982 * old Gnome based Save All icon, otherwise assume a Tango-like icon theme */
983 if (have_tango_icon_theme())
984 return gdk_pixbuf_new_from_inline(-1, save_all_tango_inline, FALSE, NULL);
985 else
986 return gdk_pixbuf_new_from_inline(-1, save_all_gnome_inline, FALSE, NULL);
987 break;
989 case GEANY_IMAGE_CLOSE_ALL:
991 return gdk_pixbuf_new_from_inline(-1, close_all_inline, FALSE, NULL);
992 break;
994 case GEANY_IMAGE_BUILD:
996 return gdk_pixbuf_new_from_inline(-1, build_inline, FALSE, NULL);
997 break;
999 default:
1000 return NULL;
1005 static GdkPixbuf *ui_new_pixbuf_from_stock(const gchar *stock_id)
1007 if (utils_str_equal(stock_id, GEANY_STOCK_CLOSE_ALL))
1008 return ui_new_pixbuf_from_inline(GEANY_IMAGE_CLOSE_ALL);
1009 else if (utils_str_equal(stock_id, GEANY_STOCK_BUILD))
1010 return ui_new_pixbuf_from_inline(GEANY_IMAGE_BUILD);
1011 else if (utils_str_equal(stock_id, GEANY_STOCK_SAVE_ALL))
1012 return ui_new_pixbuf_from_inline(GEANY_IMAGE_SAVE_ALL);
1014 return NULL;
1018 GtkWidget *ui_new_image_from_inline(gint img)
1020 GtkWidget *wid;
1021 GdkPixbuf *pb;
1023 pb = ui_new_pixbuf_from_inline(img);
1024 wid = gtk_image_new_from_pixbuf(pb);
1025 g_object_unref(pb); /* the image doesn't adopt our reference, so remove our ref. */
1026 return wid;
1030 static void recent_create_menu(GeanyRecentFiles *grf)
1032 GtkWidget *tmp;
1033 guint i, len;
1034 gchar *filename;
1036 len = MIN(file_prefs.mru_length, g_queue_get_length(grf->recent_queue));
1037 for (i = 0; i < len; i++)
1039 filename = g_queue_peek_nth(grf->recent_queue, i);
1040 /* create menu item for the recent files menu in the menu bar */
1041 tmp = gtk_menu_item_new_with_label(filename);
1042 gtk_widget_show(tmp);
1043 gtk_container_add(GTK_CONTAINER(grf->menubar), tmp);
1044 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1045 /* create menu item for the recent files menu in the toolbar */
1046 if (grf->toolbar != NULL)
1048 tmp = gtk_menu_item_new_with_label(filename);
1049 gtk_widget_show(tmp);
1050 gtk_container_add(GTK_CONTAINER(grf->toolbar), tmp);
1051 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1057 static GeanyRecentFiles *recent_get_recent_files(void)
1059 static GeanyRecentFiles grf = { RECENT_FILE_FILE, NULL, NULL, NULL, NULL };
1061 if (G_UNLIKELY(grf.recent_queue == NULL))
1063 grf.recent_queue = ui_prefs.recent_queue;
1064 grf.menubar = ui_widgets.recent_files_menu_menubar;
1065 grf.toolbar = geany_menu_button_action_get_menu(GEANY_MENU_BUTTON_ACTION(
1066 toolbar_get_action_by_name("Open")));
1067 grf.activate_cb = recent_file_activate_cb;
1069 return &grf;
1073 static GeanyRecentFiles *recent_get_recent_projects(void)
1075 static GeanyRecentFiles grf = { RECENT_FILE_PROJECT, NULL, NULL, NULL, NULL };
1077 if (G_UNLIKELY(grf.recent_queue == NULL))
1079 grf.recent_queue = ui_prefs.recent_projects_queue;
1080 grf.menubar = ui_widgets.recent_projects_menu_menubar;
1081 grf.toolbar = NULL;
1082 grf.activate_cb = recent_project_activate_cb;
1084 return &grf;
1088 void ui_create_recent_menus(void)
1090 recent_create_menu(recent_get_recent_files());
1091 recent_create_menu(recent_get_recent_projects());
1095 static void recent_file_activate_cb(GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
1097 gchar *utf8_filename = ui_menu_item_get_text(menuitem);
1098 gchar *locale_filename = utils_get_locale_from_utf8(utf8_filename);
1100 if (document_open_file(locale_filename, FALSE, NULL, NULL) != NULL)
1101 recent_file_loaded(utf8_filename, recent_get_recent_files());
1103 g_free(locale_filename);
1104 g_free(utf8_filename);
1108 static void recent_project_activate_cb(GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer user_data)
1110 gchar *utf8_filename = ui_menu_item_get_text(menuitem);
1111 gchar *locale_filename = utils_get_locale_from_utf8(utf8_filename);
1113 if (project_ask_close() && project_load_file_with_session(locale_filename))
1114 recent_file_loaded(utf8_filename, recent_get_recent_projects());
1116 g_free(locale_filename);
1117 g_free(utf8_filename);
1121 static void add_recent_file(const gchar *utf8_filename, GeanyRecentFiles *grf)
1123 if (g_queue_find_custom(grf->recent_queue, utf8_filename, (GCompareFunc) strcmp) == NULL)
1125 #if GTK_CHECK_VERSION(2, 10, 0)
1126 if (grf->type == RECENT_FILE_FILE)
1128 GtkRecentManager *manager = gtk_recent_manager_get_default();
1129 gchar *uri = g_filename_to_uri(utf8_filename, NULL, NULL);
1130 if (uri != NULL)
1132 gtk_recent_manager_add_item(manager, uri);
1133 g_free(uri);
1136 #endif
1137 g_queue_push_head(grf->recent_queue, g_strdup(utf8_filename));
1138 if (g_queue_get_length(grf->recent_queue) > file_prefs.mru_length)
1140 g_free(g_queue_pop_tail(grf->recent_queue));
1142 update_recent_menu(grf);
1144 /* filename already in recent list */
1145 else
1146 recent_file_loaded(utf8_filename, grf);
1150 void ui_add_recent_file(const gchar *utf8_filename)
1152 add_recent_file(utf8_filename, recent_get_recent_files());
1156 void ui_add_recent_project_file(const gchar *utf8_filename)
1158 add_recent_file(utf8_filename, recent_get_recent_projects());
1162 /* Returns: newly allocated string with the UTF-8 menu text. */
1163 gchar *ui_menu_item_get_text(GtkMenuItem *menu_item)
1165 const gchar *text = NULL;
1167 if (GTK_BIN(menu_item)->child)
1169 GtkWidget *child = GTK_BIN(menu_item)->child;
1171 if (GTK_IS_LABEL(child))
1172 text = gtk_label_get_text(GTK_LABEL(child));
1174 /* GTK owns text so it's much safer to return a copy of it in case the memory is reallocated */
1175 return g_strdup(text);
1179 static gint find_recent_file_item(gconstpointer list_data, gconstpointer user_data)
1181 gchar *menu_text = ui_menu_item_get_text(GTK_MENU_ITEM(list_data));
1182 gint result;
1184 if (utils_str_equal(menu_text, user_data))
1185 result = 0;
1186 else
1187 result = 1;
1189 g_free(menu_text);
1190 return result;
1194 static void recent_file_loaded(const gchar *utf8_filename, GeanyRecentFiles *grf)
1196 GList *item, *children;
1197 void *data;
1198 GtkWidget *tmp;
1200 /* first reorder the queue */
1201 item = g_queue_find_custom(grf->recent_queue, utf8_filename, (GCompareFunc) strcmp);
1202 g_return_if_fail(item != NULL);
1204 data = item->data;
1205 g_queue_remove(grf->recent_queue, data);
1206 g_queue_push_head(grf->recent_queue, data);
1208 /* remove the old menuitem for the filename */
1209 children = gtk_container_get_children(GTK_CONTAINER(grf->menubar));
1210 item = g_list_find_custom(children, utf8_filename, (GCompareFunc) find_recent_file_item);
1211 if (item != NULL)
1212 gtk_widget_destroy(GTK_WIDGET(item->data));
1213 g_list_free(children);
1215 if (grf->toolbar != NULL)
1217 children = gtk_container_get_children(GTK_CONTAINER(grf->toolbar));
1218 item = g_list_find_custom(children, utf8_filename, (GCompareFunc) find_recent_file_item);
1219 if (item != NULL)
1220 gtk_widget_destroy(GTK_WIDGET(item->data));
1221 g_list_free(children);
1223 /* now prepend a new menuitem for the filename,
1224 * first for the recent files menu in the menu bar */
1225 tmp = gtk_menu_item_new_with_label(utf8_filename);
1226 gtk_widget_show(tmp);
1227 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf->menubar), tmp);
1228 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1229 /* then for the recent files menu in the tool bar */
1230 if (grf->toolbar != NULL)
1232 tmp = gtk_menu_item_new_with_label(utf8_filename);
1233 gtk_widget_show(tmp);
1234 gtk_container_add(GTK_CONTAINER(grf->toolbar), tmp);
1235 /* this is a bit ugly, but we need to use gtk_container_add(). Using
1236 * gtk_menu_shell_prepend() doesn't emit GtkContainer's "add" signal which we need in
1237 * GeanyMenubuttonAction */
1238 gtk_menu_reorder_child(GTK_MENU(grf->toolbar), tmp, 0);
1239 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1244 static void update_recent_menu(GeanyRecentFiles *grf)
1246 GtkWidget *tmp;
1247 gchar *filename;
1248 GList *children, *item;
1250 filename = g_queue_peek_head(grf->recent_queue);
1252 /* clean the MRU list before adding an item (menubar) */
1253 children = gtk_container_get_children(GTK_CONTAINER(grf->menubar));
1254 if (g_list_length(children) > file_prefs.mru_length - 1)
1256 item = g_list_nth(children, file_prefs.mru_length - 1);
1257 while (item != NULL)
1259 if (GTK_IS_MENU_ITEM(item->data))
1260 gtk_widget_destroy(GTK_WIDGET(item->data));
1261 item = g_list_next(item);
1264 g_list_free(children);
1266 /* create item for the menu bar menu */
1267 tmp = gtk_menu_item_new_with_label(filename);
1268 gtk_widget_show(tmp);
1269 gtk_menu_shell_prepend(GTK_MENU_SHELL(grf->menubar), tmp);
1270 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1272 /* clean the MRU list before adding an item (toolbar) */
1273 if (grf->toolbar != NULL)
1275 children = gtk_container_get_children(GTK_CONTAINER(grf->toolbar));
1276 if (g_list_length(children) > file_prefs.mru_length - 1)
1278 item = g_list_nth(children, file_prefs.mru_length - 1);
1279 while (item != NULL)
1281 if (GTK_IS_MENU_ITEM(item->data))
1282 gtk_widget_destroy(GTK_WIDGET(item->data));
1283 item = g_list_next(item);
1286 g_list_free(children);
1288 /* create item for the tool bar menu */
1289 tmp = gtk_menu_item_new_with_label(filename);
1290 gtk_widget_show(tmp);
1291 gtk_container_add(GTK_CONTAINER(grf->toolbar), tmp);
1292 gtk_menu_reorder_child(GTK_MENU(grf->toolbar), tmp, 0);
1293 g_signal_connect(tmp, "activate", G_CALLBACK(grf->activate_cb), NULL);
1298 void ui_toggle_editor_features(GeanyUIEditorFeatures feature)
1300 gint i, max = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
1301 GeanyDocument *doc;
1303 for (i = 0; i < max; i++)
1305 doc = document_get_from_page(i);
1307 switch (feature)
1309 case GEANY_EDITOR_SHOW_MARKERS_MARGIN:
1310 sci_set_symbol_margin(doc->editor->sci, editor_prefs.show_markers_margin);
1311 break;
1312 case GEANY_EDITOR_SHOW_LINE_NUMBERS:
1313 sci_set_line_numbers(doc->editor->sci, editor_prefs.show_linenumber_margin, 0);
1314 break;
1315 case GEANY_EDITOR_SHOW_WHITE_SPACE:
1316 sci_set_visible_white_spaces(doc->editor->sci, editor_prefs.show_white_space);
1317 break;
1318 case GEANY_EDITOR_SHOW_LINE_ENDINGS:
1319 sci_set_visible_eols(doc->editor->sci, editor_prefs.show_line_endings);
1320 break;
1321 case GEANY_EDITOR_SHOW_INDENTATION_GUIDES:
1322 editor_set_indentation_guides(doc->editor);
1323 break;
1329 void ui_update_view_editor_menu_items(void)
1331 ignore_callback = TRUE;
1332 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_markers_margin1")), editor_prefs.show_markers_margin);
1333 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_linenumber_margin1")), editor_prefs.show_linenumber_margin);
1334 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);
1335 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);
1336 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);
1337 ignore_callback = FALSE;
1341 /** Creates a GNOME HIG-style frame (with no border and indented child alignment).
1342 * @param label_text The label text.
1343 * @param alignment An address to store the alignment widget pointer.
1344 * @return The frame widget, setting the alignment container for packing child widgets. */
1345 GtkWidget *ui_frame_new_with_alignment(const gchar *label_text, GtkWidget **alignment)
1347 GtkWidget *label, *align;
1348 GtkWidget *frame = gtk_frame_new(NULL);
1350 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1352 align = gtk_alignment_new(0.5, 0.5, 1, 1);
1353 gtk_container_add(GTK_CONTAINER(frame), align);
1354 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
1356 label = ui_label_new_bold(label_text);
1357 gtk_frame_set_label_widget(GTK_FRAME(frame), label);
1359 *alignment = align;
1360 return frame;
1364 /** Makes a fixed border for dialogs without increasing the button box border.
1365 * @param dialog The parent container for the @c GtkVBox.
1366 * @return The packed @c GtkVBox. */
1367 GtkWidget *ui_dialog_vbox_new(GtkDialog *dialog)
1369 GtkWidget *vbox = gtk_vbox_new(FALSE, 12); /* need child vbox to set a separate border. */
1371 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
1372 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), vbox);
1373 return vbox;
1377 /** Creates a @c GtkButton with custom text and a stock image similar to
1378 * @c gtk_button_new_from_stock().
1379 * @param stock_id A @c GTK_STOCK_NAME string.
1380 * @param text Button label text, can include mnemonics.
1381 * @return The new @c GtkButton.
1383 GtkWidget *ui_button_new_with_image(const gchar *stock_id, const gchar *text)
1385 GtkWidget *image, *button;
1387 button = gtk_button_new_with_mnemonic(text);
1388 gtk_widget_show(button);
1389 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON);
1390 gtk_button_set_image(GTK_BUTTON(button), image);
1391 /* note: image is shown by gtk */
1392 return button;
1396 /** Creates a @c GtkImageMenuItem with a stock image and a custom label.
1397 * @param stock_id Stock image ID, e.g. @c GTK_STOCK_OPEN.
1398 * @param label Menu item label, can include mnemonics.
1399 * @return The new @c GtkImageMenuItem.
1401 * @since 0.16
1403 GtkWidget *
1404 ui_image_menu_item_new(const gchar *stock_id, const gchar *label)
1406 GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(label);
1407 GtkWidget *image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
1409 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
1410 gtk_widget_show(image);
1411 return item;
1415 static void entry_clear_icon_release_cb(GtkEntry *entry, gint icon_pos,
1416 GdkEvent *event, gpointer data)
1418 if (event->button.button == 1 && icon_pos == 1)
1420 gtk_entry_set_text(entry, "");
1421 gtk_widget_grab_focus(GTK_WIDGET(entry));
1426 /** Adds a small clear icon to the right end of the passed @a entry.
1427 * A callback to clear the contents of the GtkEntry is automatically added.
1429 * This feature is only available with GTK 2.16 but implemented as a runtime check,
1430 * so it is safe to just use this function, if the code is ran with older versions,
1431 * nothing happens. If ran with GTK 2.16 or newer, the icon is displayed.
1433 * @param entry The GtkEntry object to which the icon should be attached.
1435 * @since 0.16
1437 void ui_entry_add_clear_icon(GtkEntry *entry)
1439 if (gtk_check_version(2, 15, 2) == NULL)
1441 g_object_set(entry, "secondary-icon-stock", "gtk-clear", NULL);
1442 g_signal_connect(entry, "icon-release", G_CALLBACK(entry_clear_icon_release_cb), NULL);
1447 /* Adds a :activate-backwards signal emitted by default when <Shift>Return is pressed */
1448 void ui_entry_add_activate_backward_signal(GtkEntry *entry)
1450 static gboolean installed = FALSE;
1452 g_return_if_fail(GTK_IS_ENTRY(entry));
1454 if (G_UNLIKELY(! installed))
1456 GtkBindingSet *binding_set;
1458 installed = TRUE;
1460 /* try to handle the unexpected case where GTK would already have installed the signal */
1461 if (g_signal_lookup("activate-backward", G_TYPE_FROM_INSTANCE(entry)))
1463 g_warning("Signal GtkEntry:activate-backward is unexpectedly already installed");
1464 return;
1467 g_signal_new("activate-backward", G_TYPE_FROM_INSTANCE(entry),
1468 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL,
1469 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1470 binding_set = gtk_binding_set_by_class(GTK_ENTRY_GET_CLASS(entry));
1471 gtk_binding_entry_add_signal(binding_set, GDK_Return, GDK_SHIFT_MASK, "activate-backward", 0);
1476 static void add_to_size_group(GtkWidget *widget, gpointer size_group)
1478 g_return_if_fail(GTK_IS_SIZE_GROUP(size_group));
1479 gtk_size_group_add_widget(GTK_SIZE_GROUP(size_group), widget);
1483 /* Copies the spacing and layout of the master GtkHButtonBox and synchronises
1484 * the width of each button box's children.
1485 * Should be called after all child widgets have been packed. */
1486 void ui_hbutton_box_copy_layout(GtkButtonBox *master, GtkButtonBox *copy)
1488 GtkSizeGroup *size_group;
1490 gtk_box_set_spacing(GTK_BOX(copy), 10);
1491 gtk_button_box_set_layout(copy, gtk_button_box_get_layout(master));
1493 /* now we need to put the widest widget from each button box in a size group,
1494 * but we don't know the width before they are drawn, and for different label
1495 * translations the widest widget can vary, so we just add all widgets. */
1496 size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1497 gtk_container_foreach(GTK_CONTAINER(master), add_to_size_group, size_group);
1498 gtk_container_foreach(GTK_CONTAINER(copy), add_to_size_group, size_group);
1499 g_object_unref(size_group);
1503 static gboolean tree_model_find_text(GtkTreeModel *model,
1504 GtkTreeIter *iter, gint column, const gchar *text)
1506 gchar *combo_text;
1507 gboolean found = FALSE;
1509 if (gtk_tree_model_get_iter_first(model, iter))
1513 gtk_tree_model_get(model, iter, 0, &combo_text, -1);
1514 found = utils_str_equal(combo_text, text);
1515 g_free(combo_text);
1517 if (found)
1518 return TRUE;
1520 while (gtk_tree_model_iter_next(model, iter));
1522 return FALSE;
1526 /** Prepends @a text to the drop down list, removing a duplicate element in
1527 * the list if found. Also ensures there are <= @a history_len elements.
1528 * @param combo_entry .
1529 * @param text Text to add, or @c NULL for current entry text.
1530 * @param history_len Max number of items, or @c 0 for default. */
1531 void ui_combo_box_add_to_history(GtkComboBoxEntry *combo_entry,
1532 const gchar *text, gint history_len)
1534 GtkComboBox *combo = GTK_COMBO_BOX(combo_entry);
1535 GtkTreeModel *model;
1536 GtkTreeIter iter;
1537 GtkTreePath *path;
1539 if (history_len <= 0)
1540 history_len = 10;
1541 if (!text)
1542 text = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combo)->child));
1544 model = gtk_combo_box_get_model(combo);
1546 if (tree_model_find_text(model, &iter, 0, text))
1548 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1550 gtk_combo_box_prepend_text(combo, text);
1552 /* limit history */
1553 path = gtk_tree_path_new_from_indices(history_len, -1);
1554 if (gtk_tree_model_get_iter(model, &iter, path))
1556 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1558 gtk_tree_path_free(path);
1562 /* Same as gtk_combo_box_prepend_text(), except that text is only prepended if it not already
1563 * exists in the combo's model. */
1564 void ui_combo_box_prepend_text_once(GtkComboBox *combo, const gchar *text)
1566 GtkTreeModel *model;
1567 GtkTreeIter iter;
1569 model = gtk_combo_box_get_model(combo);
1570 if (tree_model_find_text(model, &iter, 0, text))
1571 return; /* don't prepend duplicate */
1573 gtk_combo_box_prepend_text(combo, text);
1577 /* Changes the color of the notebook tab text and open files items according to
1578 * document status. */
1579 void ui_update_tab_status(GeanyDocument *doc)
1581 const GdkColor *color = document_get_status_color(doc);
1583 /* NULL color will reset to default */
1584 gtk_widget_modify_fg(doc->priv->tab_label, GTK_STATE_NORMAL, color);
1585 gtk_widget_modify_fg(doc->priv->tab_label, GTK_STATE_ACTIVE, color);
1587 sidebar_openfiles_update(doc);
1591 static gboolean tree_model_iter_get_next(GtkTreeModel *model, GtkTreeIter *iter,
1592 gboolean down)
1594 GtkTreePath *path;
1595 gboolean result;
1597 if (down)
1598 return gtk_tree_model_iter_next(model, iter);
1600 path = gtk_tree_model_get_path(model, iter);
1601 result = gtk_tree_path_prev(path) && gtk_tree_model_get_iter(model, iter, path);
1602 gtk_tree_path_free(path);
1603 return result;
1607 /* note: the while loop might be more efficient when searching upwards if it
1608 * used tree paths instead of tree iters, but in practice it probably doesn't matter much. */
1609 static gboolean tree_view_find(GtkTreeView *treeview, TVMatchCallback cb, gboolean down)
1611 GtkTreeSelection *treesel;
1612 GtkTreeIter iter;
1613 GtkTreeModel *model;
1615 treesel = gtk_tree_view_get_selection(treeview);
1616 if (gtk_tree_selection_get_selected(treesel, &model, &iter))
1618 /* get the next selected item */
1619 if (! tree_model_iter_get_next(model, &iter, down))
1620 return FALSE; /* no more items */
1622 else /* no selection */
1624 if (! gtk_tree_model_get_iter_first(model, &iter))
1625 return TRUE; /* no items */
1627 while (TRUE)
1629 gtk_tree_selection_select_iter(treesel, &iter);
1630 if (cb(FALSE))
1631 break; /* found next message */
1633 if (! tree_model_iter_get_next(model, &iter, down))
1634 return FALSE; /* no more items */
1636 /* scroll item in view */
1637 if (ui_prefs.msgwindow_visible)
1639 GtkTreePath *path = gtk_tree_model_get_path(
1640 gtk_tree_view_get_model(treeview), &iter);
1642 gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.5);
1643 gtk_tree_path_free(path);
1645 return TRUE;
1649 /* Returns FALSE if the treeview has items but no matching next item. */
1650 gboolean ui_tree_view_find_next(GtkTreeView *treeview, TVMatchCallback cb)
1652 return tree_view_find(treeview, cb, TRUE);
1656 /* Returns FALSE if the treeview has items but no matching next item. */
1657 gboolean ui_tree_view_find_previous(GtkTreeView *treeview, TVMatchCallback cb)
1659 return tree_view_find(treeview, cb, FALSE);
1664 * Modifies the font of a widget using gtk_widget_modify_font().
1666 * @param widget The widget.
1667 * @param str The font name as expected by pango_font_description_from_string().
1669 void ui_widget_modify_font_from_string(GtkWidget *widget, const gchar *str)
1671 PangoFontDescription *pfd;
1673 pfd = pango_font_description_from_string(str);
1674 gtk_widget_modify_font(widget, pfd);
1675 pango_font_description_free(pfd);
1679 /** Creates a @c GtkHBox with @a entry packed into it and an open button which runs a
1680 * file chooser, replacing entry text (if successful) with the path returned from the
1681 * @c GtkFileChooser.
1682 * @note @a entry can be the child of an unparented widget, such as @c GtkComboBoxEntry.
1683 * @param title The file chooser dialog title, or @c NULL.
1684 * @param action The mode of the file chooser.
1685 * @param entry Can be an unpacked @c GtkEntry, or the child of an unpacked widget,
1686 * such as @c GtkComboBoxEntry.
1687 * @return The @c GtkHBox.
1689 /* @see ui_setup_open_button_callback(). */
1690 GtkWidget *ui_path_box_new(const gchar *title, GtkFileChooserAction action, GtkEntry *entry)
1692 GtkWidget *vbox, *dirbtn, *openimg, *hbox, *path_entry;
1694 hbox = gtk_hbox_new(FALSE, 6);
1695 path_entry = GTK_WIDGET(entry);
1697 /* prevent path_entry being vertically stretched to the height of dirbtn */
1698 vbox = gtk_vbox_new(FALSE, 0);
1699 if (gtk_widget_get_parent(path_entry)) /* entry->parent may be a GtkComboBoxEntry */
1701 GtkWidget *parent = gtk_widget_get_parent(path_entry);
1703 gtk_box_pack_start(GTK_BOX(vbox), parent, TRUE, FALSE, 0);
1705 else
1706 gtk_box_pack_start(GTK_BOX(vbox), path_entry, TRUE, FALSE, 0);
1708 dirbtn = gtk_button_new();
1709 openimg = gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON);
1710 gtk_container_add(GTK_CONTAINER(dirbtn), openimg);
1711 ui_setup_open_button_callback(dirbtn, title, action, entry);
1713 gtk_box_pack_end(GTK_BOX(hbox), dirbtn, FALSE, FALSE, 0);
1714 gtk_box_pack_end(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1715 return hbox;
1719 static void ui_path_box_open_clicked(GtkButton *button, gpointer user_data);
1722 /* Setup a GtkButton to run a GtkFileChooser, setting entry text if successful.
1723 * title can be NULL.
1724 * action is the file chooser mode to use. */
1725 void ui_setup_open_button_callback(GtkWidget *open_btn, const gchar *title,
1726 GtkFileChooserAction action, GtkEntry *entry)
1728 GtkWidget *path_entry = GTK_WIDGET(entry);
1730 if (title)
1731 g_object_set_data_full(G_OBJECT(open_btn), "title", g_strdup(title),
1732 (GDestroyNotify) g_free);
1733 g_object_set_data(G_OBJECT(open_btn), "action", (gpointer) action);
1734 ui_hookup_widget(open_btn, path_entry, "entry");
1735 g_signal_connect(open_btn, "clicked", G_CALLBACK(ui_path_box_open_clicked), open_btn);
1739 #ifndef G_OS_WIN32
1740 static gchar *run_file_chooser(const gchar *title, GtkFileChooserAction action,
1741 const gchar *utf8_path)
1743 GtkWidget *dialog = gtk_file_chooser_dialog_new(title,
1744 GTK_WINDOW(main_widgets.window), action,
1745 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1746 GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
1747 gchar *locale_path;
1748 gchar *ret_path = NULL;
1750 gtk_widget_set_name(dialog, "GeanyDialog");
1751 locale_path = utils_get_locale_from_utf8(utf8_path);
1752 if (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
1754 if (g_path_is_absolute(locale_path) && g_file_test(locale_path, G_FILE_TEST_IS_DIR))
1755 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), locale_path);
1757 else if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
1759 if (g_path_is_absolute(locale_path))
1760 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), locale_path);
1762 g_free(locale_path);
1764 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
1766 gchar *dir_locale;
1768 dir_locale = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
1769 ret_path = utils_get_utf8_from_locale(dir_locale);
1770 g_free(dir_locale);
1772 gtk_widget_destroy(dialog);
1773 return ret_path;
1775 #endif
1778 static void ui_path_box_open_clicked(GtkButton *button, gpointer user_data)
1780 GtkWidget *path_box = GTK_WIDGET(user_data);
1781 GtkFileChooserAction action =
1782 (GtkFileChooserAction) g_object_get_data(G_OBJECT(path_box), "action");
1783 GtkEntry *entry =
1784 (GtkEntry *) g_object_get_data(G_OBJECT(path_box), "entry");
1785 const gchar *title = g_object_get_data(G_OBJECT(path_box), "title");
1786 gchar *utf8_path = NULL;
1788 /* TODO: extend for other actions */
1789 g_return_if_fail(action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
1790 action == GTK_FILE_CHOOSER_ACTION_OPEN);
1792 if (title == NULL)
1793 title = (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ?
1794 _("Select Folder") : _("Select File");
1796 if (action == GTK_FILE_CHOOSER_ACTION_OPEN)
1798 #ifdef G_OS_WIN32
1799 utf8_path = win32_show_file_dialog(GTK_WINDOW(ui_widgets.prefs_dialog), title,
1800 gtk_entry_get_text(GTK_ENTRY(entry)));
1801 #else
1802 utf8_path = run_file_chooser(title, action, gtk_entry_get_text(GTK_ENTRY(entry)));
1803 #endif
1805 else if (action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
1807 gchar *path = g_path_get_dirname(gtk_entry_get_text(GTK_ENTRY(entry)));
1808 #ifdef G_OS_WIN32
1809 utf8_path = win32_show_folder_dialog(ui_widgets.prefs_dialog, title,
1810 gtk_entry_get_text(GTK_ENTRY(entry)));
1811 #else
1812 utf8_path = run_file_chooser(title, action, path);
1813 #endif
1814 g_free(path);
1817 if (utf8_path != NULL)
1819 gtk_entry_set_text(GTK_ENTRY(entry), utf8_path);
1820 g_free(utf8_path);
1825 void ui_statusbar_showhide(gboolean state)
1827 /* handle statusbar visibility */
1828 if (state)
1830 gtk_widget_show(ui_widgets.statusbar);
1831 ui_update_statusbar(NULL, -1);
1833 else
1834 gtk_widget_hide(ui_widgets.statusbar);
1838 /** Packs all @c GtkWidgets passed after the row argument into a table, using
1839 * one widget per cell. The first widget is not expanded as the table grows,
1840 * as this is usually a label.
1841 * @param table
1842 * @param row The row number of the table.
1844 void ui_table_add_row(GtkTable *table, gint row, ...)
1846 va_list args;
1847 gint i;
1848 GtkWidget *widget;
1850 va_start(args, row);
1851 for (i = 0; (widget = va_arg(args, GtkWidget*), widget != NULL); i++)
1853 gint options = (i == 0) ? GTK_FILL : GTK_EXPAND | GTK_FILL;
1855 gtk_table_attach(GTK_TABLE(table), widget, i, i + 1, row, row + 1,
1856 options, 0, 0, 0);
1858 va_end(args);
1862 static void on_config_file_clicked(GtkWidget *widget, gpointer user_data)
1864 const gchar *file_name = user_data;
1865 GeanyFiletype *ft = NULL;
1867 if (strstr(file_name, G_DIR_SEPARATOR_S "filetypes."))
1868 ft = filetypes[GEANY_FILETYPES_CONF];
1870 if (g_file_test(file_name, G_FILE_TEST_EXISTS))
1871 document_open_file(file_name, FALSE, ft, NULL);
1872 else
1874 gchar *utf8_filename = utils_get_utf8_from_locale(file_name);
1875 gchar *base_name = g_path_get_basename(file_name);
1876 gchar *global_file = g_build_filename(app->datadir, base_name, NULL);
1877 gchar *global_content = NULL;
1879 /* if the requested file doesn't exist in the user's config dir, try loading the file
1880 * from the global data directory and use its contents for the newly created file */
1881 if (g_file_test(global_file, G_FILE_TEST_EXISTS))
1882 g_file_get_contents(global_file, &global_content, NULL, NULL);
1884 document_new_file(utf8_filename, ft, global_content);
1886 utils_free_pointers(4, utf8_filename, base_name, global_file, global_content, NULL);
1891 /* @note You should connect to the "document-save" signal yourself to detect
1892 * if the user has just saved the config file, reloading it. */
1893 void ui_add_config_file_menu_item(const gchar *real_path, const gchar *label, GtkContainer *parent)
1895 GtkWidget *item;
1897 if (!parent)
1898 parent = GTK_CONTAINER(widgets.config_files_menu);
1900 if (!label)
1902 gchar *base_name;
1904 base_name = g_path_get_basename(real_path);
1905 item = gtk_menu_item_new_with_label(base_name);
1906 g_free(base_name);
1908 else
1909 item = gtk_menu_item_new_with_mnemonic(label);
1911 gtk_widget_show(item);
1912 gtk_container_add(parent, item);
1913 g_signal_connect(item, "activate", G_CALLBACK(on_config_file_clicked),
1914 /* this memory is kept */
1915 g_strdup(real_path));
1919 static gboolean sort_menu(gpointer data)
1921 ui_menu_sort_by_label(GTK_MENU(data));
1922 return FALSE;
1926 static void create_config_files_menu(void)
1928 GtkWidget *menu, *item;
1930 widgets.config_files_menu = menu = gtk_menu_new();
1932 item = ui_lookup_widget(main_widgets.window, "configuration_files1");
1933 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
1935 /* sort menu after all items added */
1936 g_idle_add(sort_menu, widgets.config_files_menu);
1940 void ui_init_stock_items(void)
1942 GtkIconSet *icon_set;
1943 GtkIconFactory *factory = gtk_icon_factory_new();
1944 GdkPixbuf *pb;
1945 gsize i, len;
1946 GtkStockItem items[] =
1948 { GEANY_STOCK_SAVE_ALL, N_("Save All"), 0, 0, GETTEXT_PACKAGE },
1949 { GEANY_STOCK_CLOSE_ALL, N_("Close All"), 0, 0, GETTEXT_PACKAGE },
1950 { GEANY_STOCK_BUILD, N_("Build"), 0, 0, GETTEXT_PACKAGE }
1953 len = G_N_ELEMENTS(items);
1954 for (i = 0; i < len; i++)
1956 pb = ui_new_pixbuf_from_stock(items[i].stock_id);
1957 icon_set = gtk_icon_set_new_from_pixbuf(pb);
1959 gtk_icon_factory_add(factory, items[i].stock_id, icon_set);
1961 gtk_icon_set_unref(icon_set);
1962 g_object_unref(pb);
1964 gtk_stock_add((GtkStockItem *) items, len);
1965 gtk_icon_factory_add_default(factory);
1966 g_object_unref(factory);
1970 void ui_init_toolbar_widgets(void)
1972 widgets.save_buttons[1] = toolbar_get_widget_by_name("Save");
1973 widgets.save_buttons[3] = toolbar_get_widget_by_name("SaveAll");
1974 widgets.redo_items[2] = toolbar_get_widget_by_name("Redo");
1975 widgets.undo_items[2] = toolbar_get_widget_by_name("Undo");
1979 void ui_swap_sidebar_pos(void)
1981 GtkWidget *pane = ui_lookup_widget(main_widgets.window, "hpaned1");
1982 GtkWidget *left = gtk_paned_get_child1(GTK_PANED(pane));
1983 GtkWidget *right = gtk_paned_get_child2(GTK_PANED(pane));
1984 GtkWidget *box = ui_lookup_widget(main_widgets.window, "vbox1");
1986 /* reparenting avoids scintilla problem with middle click paste */
1987 gtk_widget_reparent(left, box);
1988 gtk_widget_reparent(right, box);
1989 gtk_widget_reparent(right, pane);
1990 gtk_widget_reparent(left, pane);
1992 gtk_paned_set_position(GTK_PANED(pane), pane->allocation.width
1993 - gtk_paned_get_position(GTK_PANED(pane)));
1997 static void init_recent_files(void)
1999 GtkWidget *toolbar_recent_files_menu;
2001 /* add recent files to the File menu */
2002 ui_widgets.recent_files_menuitem = ui_lookup_widget(main_widgets.window, "recent_files1");
2003 ui_widgets.recent_files_menu_menubar = gtk_menu_new();
2004 gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_widgets.recent_files_menuitem),
2005 ui_widgets.recent_files_menu_menubar);
2007 /* add recent files to the toolbar Open button */
2008 toolbar_recent_files_menu = gtk_menu_new();
2009 g_object_ref(toolbar_recent_files_menu);
2010 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
2011 toolbar_get_action_by_name("Open")), toolbar_recent_files_menu);
2015 static void ui_menu_move(GtkWidget *menu, GtkWidget *old, GtkWidget *new)
2017 g_object_ref(menu);
2018 gtk_menu_item_set_submenu(GTK_MENU_ITEM(old), NULL);
2019 gtk_menu_item_set_submenu(GTK_MENU_ITEM(new), menu);
2020 g_object_unref(menu);
2024 typedef struct GeanySharedMenu
2026 const gchar *menu;
2027 const gchar *menubar_item;
2028 const gchar *popup_item;
2030 GeanySharedMenu;
2032 #define foreach_menu(item, array) \
2033 for (item = array; item->menu; item++)
2035 static void on_editor_menu_show(GtkWidget *widget, GeanySharedMenu *items)
2037 GeanySharedMenu *item;
2039 foreach_menu(item, items)
2041 GtkWidget *popup = ui_lookup_widget(main_widgets.editor_menu, item->popup_item);
2042 GtkWidget *bar = ui_lookup_widget(main_widgets.window, item->menubar_item);
2043 GtkWidget *menu = ui_lookup_widget(main_widgets.window, item->menu);
2045 ui_menu_move(menu, bar, popup);
2050 static void on_editor_menu_hide(GtkWidget *widget, GeanySharedMenu *items)
2052 GeanySharedMenu *item;
2054 foreach_menu(item, items)
2056 GtkWidget *popup = ui_lookup_widget(main_widgets.editor_menu, item->popup_item);
2057 GtkWidget *bar = ui_lookup_widget(main_widgets.window, item->menubar_item);
2058 GtkWidget *menu = ui_lookup_widget(main_widgets.window, item->menu);
2060 ui_menu_move(menu, popup, bar);
2065 /* Currently ui_init() is called before keyfile.c stash group code is initialized,
2066 * so this is called after that's done. */
2067 void ui_init_prefs(void)
2069 StashGroup *group = stash_group_new(PACKAGE);
2071 /* various prefs */
2072 configuration_add_various_pref_group(group);
2074 stash_group_add_boolean(group, &interface_prefs.show_symbol_list_expanders,
2075 "show_symbol_list_expanders", TRUE);
2076 stash_group_add_boolean(group, &interface_prefs.compiler_tab_autoscroll,
2077 "compiler_tab_autoscroll", TRUE);
2078 stash_group_add_boolean(group, &ui_prefs.allow_always_save,
2079 "allow_always_save", FALSE);
2080 stash_group_add_string(group, &statusbar_template,
2081 "statusbar_template", "");
2082 stash_group_add_boolean(group, &ui_prefs.new_document_after_close,
2083 "new_document_after_close", FALSE);
2084 stash_group_add_boolean(group, &interface_prefs.msgwin_status_visible,
2085 "msgwin_status_visible", TRUE);
2086 stash_group_add_boolean(group, &interface_prefs.msgwin_compiler_visible,
2087 "msgwin_compiler_visible", TRUE);
2088 stash_group_add_boolean(group, &interface_prefs.msgwin_messages_visible,
2089 "msgwin_messages_visible", TRUE);
2090 stash_group_add_boolean(group, &interface_prefs.msgwin_scribble_visible,
2091 "msgwin_scribble_visible", TRUE);
2095 /* Used to find out the name of the GtkBuilder retrieved object since
2096 * some objects will be GTK_IS_BUILDABLE() and use the GtkBuildable
2097 * 'name' property for that and those that don't implement GtkBuildable
2098 * will have a "gtk-builder-name" stored in the GObject's data list. */
2099 static const gchar *ui_guess_object_name(GObject *obj)
2101 const gchar *name;
2103 g_return_val_if_fail(G_IS_OBJECT(obj), NULL);
2105 if (GTK_IS_BUILDABLE(obj))
2106 name = gtk_buildable_get_name(GTK_BUILDABLE(obj));
2107 if (! name)
2108 name = g_object_get_data(obj, "gtk-builder-name");
2109 if (! name)
2110 return NULL;
2112 return name;
2116 void ui_init_builder(void)
2118 gchar *interface_file;
2119 const gchar *name;
2120 GError *error;
2121 GSList *iter, *all_objects;
2122 GtkBuilder *builder;
2123 GtkCellRenderer *renderer;
2125 g_return_if_fail(builder == NULL);
2127 builder = gtk_builder_new();
2128 if (! builder)
2130 g_error("Failed to initialize the user-interface");
2131 return;
2134 error = NULL;
2135 interface_file = g_build_filename(GEANY_DATADIR, "geany", "geany.glade", NULL);
2136 if (! gtk_builder_add_from_file(builder, interface_file, &error))
2138 g_error("Failed to load the user-interface file: %s", error->message);
2139 g_error_free(error);
2140 g_free(interface_file);
2141 g_object_unref(builder);
2142 return;
2144 g_free(interface_file);
2146 gtk_builder_connect_signals(builder, NULL);
2148 objects_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
2150 all_objects = gtk_builder_get_objects(builder);
2151 for (iter = all_objects; iter != NULL; iter = g_slist_next(iter))
2153 name = ui_guess_object_name(iter->data);
2154 if (! name)
2156 g_warning("Unable to get name from GtkBuilder object");
2157 continue;
2160 ui_hookup_object(iter->data, name);
2162 /* Glade doesn't seem to add cell renderers for the combo boxes,
2163 * so they are added here. */
2164 if (GTK_IS_COMBO_BOX(iter->data))
2166 renderer = gtk_cell_renderer_text_new();
2167 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(iter->data), renderer, TRUE);
2168 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(iter->data),
2169 renderer, "text", 0, NULL);
2172 g_slist_free(all_objects);
2174 g_object_unref(builder);
2178 void ui_init(void)
2180 init_recent_files();
2182 ui_widgets.statusbar = ui_lookup_widget(main_widgets.window, "statusbar");
2183 ui_widgets.print_page_setup = ui_lookup_widget(main_widgets.window, "page_setup1");
2185 main_widgets.progressbar = progress_bar_create();
2187 /* current word sensitive items */
2188 widgets.popup_goto_items[0] = ui_lookup_widget(main_widgets.editor_menu, "goto_tag_definition1");
2189 widgets.popup_goto_items[1] = ui_lookup_widget(main_widgets.editor_menu, "context_action1");
2190 widgets.popup_goto_items[2] = ui_lookup_widget(main_widgets.editor_menu, "find_usage1");
2191 widgets.popup_goto_items[3] = ui_lookup_widget(main_widgets.editor_menu, "find_document_usage1");
2193 widgets.popup_copy_items[0] = ui_lookup_widget(main_widgets.editor_menu, "cut1");
2194 widgets.popup_copy_items[1] = ui_lookup_widget(main_widgets.editor_menu, "copy1");
2195 widgets.popup_copy_items[2] = ui_lookup_widget(main_widgets.editor_menu, "delete1");
2196 widgets.menu_copy_items[0] = ui_lookup_widget(main_widgets.window, "menu_cut1");
2197 widgets.menu_copy_items[1] = ui_lookup_widget(main_widgets.window, "menu_copy1");
2198 widgets.menu_copy_items[2] = ui_lookup_widget(main_widgets.window, "menu_delete1");
2199 widgets.menu_insert_include_items[0] = ui_lookup_widget(main_widgets.editor_menu, "insert_include1");
2200 widgets.menu_insert_include_items[1] = ui_lookup_widget(main_widgets.window, "insert_include2");
2201 widgets.save_buttons[0] = ui_lookup_widget(main_widgets.window, "menu_save1");
2202 widgets.save_buttons[2] = ui_lookup_widget(main_widgets.window, "menu_save_all1");
2203 widgets.redo_items[0] = ui_lookup_widget(main_widgets.editor_menu, "redo1");
2204 widgets.redo_items[1] = ui_lookup_widget(main_widgets.window, "menu_redo2");
2205 widgets.undo_items[0] = ui_lookup_widget(main_widgets.editor_menu, "undo1");
2206 widgets.undo_items[1] = ui_lookup_widget(main_widgets.window, "menu_undo2");
2208 /* reparent context submenus as needed */
2210 GeanySharedMenu arr[] = {
2211 {"commands2_menu", "commands2", "commands1"},
2212 {"menu_format1_menu", "menu_format1", "menu_format2"},
2213 {"more1_menu", "more1", "search2"},
2214 {NULL, NULL, NULL}
2216 static GeanySharedMenu items[G_N_ELEMENTS(arr)];
2218 memcpy(items, arr, sizeof(arr));
2219 g_signal_connect(main_widgets.editor_menu, "show", G_CALLBACK(on_editor_menu_show), items);
2220 g_signal_connect(main_widgets.editor_menu, "hide", G_CALLBACK(on_editor_menu_hide), items);
2223 ui_init_toolbar_widgets();
2224 init_document_widgets();
2225 create_config_files_menu();
2229 void ui_finalize_builder(void)
2231 if (objects_table != NULL)
2232 g_hash_table_destroy(objects_table);
2236 void ui_finalize(void)
2238 g_free(statusbar_template);
2242 static void auto_separator_update(GeanyAutoSeparator *autosep)
2244 g_return_if_fail(autosep->ref_count >= 0);
2246 if (autosep->widget)
2247 ui_widget_show_hide(autosep->widget, autosep->ref_count > 0);
2251 static void on_auto_separator_item_show_hide(GtkWidget *widget, gpointer user_data)
2253 GeanyAutoSeparator *autosep = user_data;
2255 if (GTK_WIDGET_VISIBLE(widget))
2256 autosep->ref_count++;
2257 else
2258 autosep->ref_count--;
2259 auto_separator_update(autosep);
2263 static void on_auto_separator_item_destroy(GtkWidget *widget, gpointer user_data)
2265 GeanyAutoSeparator *autosep = user_data;
2267 /* GTK_WIDGET_VISIBLE won't work now the widget is being destroyed,
2268 * so assume widget was visible */
2269 autosep->ref_count--;
2270 autosep->ref_count = MAX(autosep->ref_count, 0);
2271 auto_separator_update(autosep);
2275 /* Show the separator widget if @a item or another is visible. */
2276 /* Note: This would be neater taking a widget argument, setting a "visible-count"
2277 * property, and using reference counting to keep the widget alive whilst its visible group
2278 * is alive. */
2279 void ui_auto_separator_add_ref(GeanyAutoSeparator *autosep, GtkWidget *item)
2281 /* set widget ptr NULL when widget destroyed */
2282 if (autosep->ref_count == 0)
2283 g_signal_connect(autosep->widget, "destroy",
2284 G_CALLBACK(gtk_widget_destroyed), &autosep->widget);
2286 if (GTK_WIDGET_VISIBLE(item))
2288 autosep->ref_count++;
2289 auto_separator_update(autosep);
2291 g_signal_connect(item, "show", G_CALLBACK(on_auto_separator_item_show_hide), autosep);
2292 g_signal_connect(item, "hide", G_CALLBACK(on_auto_separator_item_show_hide), autosep);
2293 g_signal_connect(item, "destroy", G_CALLBACK(on_auto_separator_item_destroy), autosep);
2298 * Sets @a text as the contents of the tooltip for @a widget.
2300 * @param widget The widget the tooltip should be set for.
2301 * @param text The text for the tooltip.
2303 * @since 0.16
2304 * @deprecated 0.21 use gtk_widget_set_tooltip_text() instead
2306 void ui_widget_set_tooltip_text(GtkWidget *widget, const gchar *text)
2308 gtk_widget_set_tooltip_text(widget, text);
2312 /** Returns a widget from a name in a component, usually created by Glade.
2313 * Call it with the toplevel widget in the component (i.e. a window/dialog),
2314 * or alternatively any widget in the component, and the name of the widget
2315 * you want returned.
2317 * @note Since 0.21 the @a widget parameter is not used and can be set
2318 * to NULL if you like.
2320 * @param widget Widget with the @a widget_name property set.
2321 * @param widget_name Name to lookup.
2322 * @return The widget found.
2323 * @see ui_hookup_object().
2324 * @see ui_lookup_object().
2325 * @deprecated Use ui_lookup_object instead.
2327 * @since 0.16
2329 GtkWidget *ui_lookup_widget(GtkWidget *widget, const gchar *widget_name)
2331 GtkWidget *found_widget;
2333 (void) widget; /* not used anymore */
2335 g_return_val_if_fail(widget_name != NULL, NULL);
2337 found_widget = GTK_WIDGET(ui_lookup_object(widget_name));
2338 if (G_UNLIKELY(found_widget == NULL))
2339 g_warning("Widget not found: %s", widget_name);
2341 return found_widget;
2345 /** Returns a widget from a name.
2347 * Call it with the name of the GObject you want returned.
2349 * @note The GObject must either be in the GtkBuilder/Glade file or
2350 * have been added with the function @a ui_hookup_object.
2352 * @param widget Widget with the @a widget_name property set.
2353 * @param widget_name Name to lookup.
2354 * @return The widget found.
2356 * @see ui_hookup_object()
2357 * @since 0.21
2359 GObject *ui_lookup_object(const gchar *object_name)
2361 gpointer *found;
2363 g_return_val_if_fail(object_name != NULL, NULL);
2364 g_return_val_if_fail(objects_table != NULL, NULL);
2366 found = g_hash_table_lookup(objects_table, (gconstpointer) object_name);
2368 if (found == NULL)
2369 return NULL;
2371 return G_OBJECT(found);
2375 /** Sets a name to lookup GObject @a obj.
2377 * The GObject can later be looked-up by the @a name using the
2378 * @a ui_lookup_object() function.
2380 * @param obj GObject.
2381 * @param name Name.
2383 * @see ui_lookup_object()
2384 * @since 0.21
2386 void ui_hookup_object(GObject *obj, const gchar *object_name)
2388 g_return_if_fail(G_IS_OBJECT(obj));
2389 g_return_if_fail(object_name != NULL);
2390 g_return_if_fail(objects_table != NULL);
2392 g_hash_table_insert(objects_table, g_strdup(object_name), g_object_ref(obj));
2396 /* Progress Bar */
2397 static guint progress_bar_timer_id = (guint) -1;
2400 static GtkWidget *progress_bar_create(void)
2402 GtkWidget *bar = gtk_progress_bar_new();
2404 /* Set the progressbar's height to 1 to fit it in the statusbar */
2405 gtk_widget_set_size_request(bar, -1, 1);
2406 gtk_box_pack_start (GTK_BOX(ui_widgets.statusbar), bar, FALSE, FALSE, 3);
2408 return bar;
2412 static gboolean progress_bar_pulse(gpointer data)
2414 gtk_progress_bar_pulse(GTK_PROGRESS_BAR(main_widgets.progressbar));
2416 return TRUE;
2421 * Starts a constantly pulsing progressbar in the right corner of the statusbar
2422 * (if the statusbar is visible). This is a convenience function which adds a timer to
2423 * pulse the progressbar constantly until ui_progress_bar_stop() is called.
2424 * You can use this function when you have time consuming asynchronous operation and want to
2425 * display some activity in the GUI and when you don't know about detailed progress steps.
2426 * The progressbar widget is hidden by default when it is not active. This function and
2427 * ui_progress_bar_stop() will show and hide it automatically for you.
2429 * You can also access the progressbar widget directly using @c geany->main_widgets->progressbar
2430 * and use the GtkProgressBar API to set discrete fractions to display better progress information.
2431 * In this case, you need to show and hide the widget yourself. You can find some example code
2432 * in @c src/printing.c.
2434 * @param text The text to be shown as the progress bar label or NULL to leave it empty.
2436 * @since 0.16
2438 void ui_progress_bar_start(const gchar *text)
2440 g_return_if_fail(progress_bar_timer_id == (guint) -1);
2442 if (! interface_prefs.statusbar_visible)
2443 return;
2445 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(main_widgets.progressbar), text);
2447 progress_bar_timer_id = g_timeout_add(200, progress_bar_pulse, NULL);
2449 gtk_widget_show(GTK_WIDGET(main_widgets.progressbar));
2453 /** Stops a running progress bar and hides the widget again.
2455 * @since 0.16
2457 void ui_progress_bar_stop(void)
2459 gtk_widget_hide(GTK_WIDGET(main_widgets.progressbar));
2461 if (progress_bar_timer_id != (guint) -1)
2463 g_source_remove(progress_bar_timer_id);
2464 progress_bar_timer_id = (guint) -1;
2469 static gint compare_menu_item_labels(gconstpointer a, gconstpointer b)
2471 GtkMenuItem *item_a = GTK_MENU_ITEM(a);
2472 GtkMenuItem *item_b = GTK_MENU_ITEM(b);
2473 gchar *sa, *sb;
2474 gint result;
2476 sa = ui_menu_item_get_text(item_a);
2477 sb = ui_menu_item_get_text(item_b);
2478 result = utils_str_casecmp(sa, sb);
2479 g_free(sa);
2480 g_free(sb);
2481 return result;
2485 /* Currently @a menu should contain only GtkMenuItems with labels. */
2486 void ui_menu_sort_by_label(GtkMenu *menu)
2488 GList *list = gtk_container_get_children(GTK_CONTAINER(menu));
2489 GList *node;
2490 gint pos;
2492 list = g_list_sort(list, compare_menu_item_labels);
2493 pos = 0;
2494 foreach_list(node, list)
2496 gtk_menu_reorder_child(menu, node->data, pos);
2497 pos++;
2499 g_list_free(list);
2503 void ui_label_set_markup(GtkLabel *label, const gchar *format, ...)
2505 va_list a;
2506 gchar *text;
2508 va_start(a, format);
2509 text = g_strdup_vprintf(format, a);
2510 va_end(a);
2512 gtk_label_set_text(label, text);
2513 gtk_label_set_use_markup(label, TRUE);
2514 g_free(text);
2518 GtkWidget *ui_label_new_bold(const gchar *text)
2520 GtkWidget *label;
2521 gchar *label_text;
2523 label_text = g_markup_escape_text(text, -1);
2524 label = gtk_label_new(NULL);
2525 ui_label_set_markup(GTK_LABEL(label), "<b>%s</b>", label_text);
2526 g_free(label_text);
2527 return label;
2531 /** Adds a list of document items to @a menu.
2532 * @param menu Menu.
2533 * @param active Which document to highlight, or @c NULL.
2534 * @param callback is used for each menu item's @c "activate" signal and will be passed
2535 * the corresponding document pointer as @c user_data.
2536 * @warning You should check @c doc->is_valid in the callback.
2537 * @since 0.19 */
2538 void ui_menu_add_document_items(GtkMenu *menu, GeanyDocument *active, GCallback callback)
2540 ui_menu_add_document_items_sorted(menu, active, callback, NULL);
2544 /** Adds a list of document items to @a menu.
2546 * @a compare_func might be NULL to not sort the documents in the menu. In this case,
2547 * the order of the document tabs is used.
2549 * See document_sort_by_display_name() for an example sort function.
2551 * @param menu Menu.
2552 * @param active Which document to highlight, or @c NULL.
2553 * @param callback is used for each menu item's @c "activate" signal and will be passed
2554 * the corresponding document pointer as @c user_data.
2555 * @param compare_func is used to sort the list. Might be @c NULL to not sort the list.
2556 * @warning You should check @c doc->is_valid in the callback.
2557 * @since 0.21 */
2558 void ui_menu_add_document_items_sorted(GtkMenu *menu, GeanyDocument *active,
2559 GCallback callback, GCompareFunc compare_func)
2561 GtkWidget *menu_item, *menu_item_label, *image;
2562 const GdkColor *color;
2563 GeanyDocument *doc;
2564 guint i, len;
2565 gchar *base_name, *label;
2566 GPtrArray *sorted_documents;
2568 len = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
2570 sorted_documents = g_ptr_array_sized_new(len);
2571 /* copy the documents_array into the new one */
2572 foreach_document(i)
2574 g_ptr_array_add(sorted_documents, documents[i]);
2576 if (compare_func == NULL)
2577 compare_func = document_compare_by_tab_order;
2579 /* and now sort it */
2580 g_ptr_array_sort(sorted_documents, compare_func);
2582 for (i = 0; i < sorted_documents->len; i++)
2584 doc = g_ptr_array_index(sorted_documents, i);
2586 base_name = g_path_get_basename(DOC_FILENAME(doc));
2587 menu_item = gtk_image_menu_item_new_with_label(base_name);
2588 image = gtk_image_new_from_pixbuf(doc->file_type->icon);
2589 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
2591 gtk_widget_show(menu_item);
2592 gtk_container_add(GTK_CONTAINER(menu), menu_item);
2593 g_signal_connect(menu_item, "activate", callback, doc);
2595 color = document_get_status_color(doc);
2596 menu_item_label = gtk_bin_get_child(GTK_BIN(menu_item));
2597 gtk_widget_modify_fg(menu_item_label, GTK_STATE_NORMAL, color);
2598 gtk_widget_modify_fg(menu_item_label, GTK_STATE_ACTIVE, color);
2600 if (doc == active)
2602 label = g_markup_escape_text(base_name, -1);
2603 ui_label_set_markup(GTK_LABEL(menu_item_label), "<b>%s</b>", label);
2604 g_free(label);
2607 g_free(base_name);
2609 g_ptr_array_free(sorted_documents, TRUE);
2613 /** Checks whether the passed @a keyval is the Enter or Return key.
2614 * There are three different Enter/Return key values
2615 * (@c GDK_Return, @c GDK_ISO_Enter, @c GDK_KP_Enter).
2616 * This is just a convenience function.
2617 * @param keyval A keyval.
2618 * @return @c TRUE if @a keyval is the one of the Enter/Return key values, otherwise @c FALSE.
2619 * @since 0.19 */
2620 gboolean ui_is_keyval_enter_or_return(guint keyval)
2622 return (keyval == GDK_Return || keyval == GDK_ISO_Enter|| keyval == GDK_KP_Enter);
2626 /** Reads an integer from the GTK default settings registry
2627 * (see http://library.gnome.org/devel/gtk/stable/GtkSettings.html).
2628 * @param property_name The property to read.
2629 * @param default_value The default value in case the value could not be read.
2630 * @return The value for the property if it exists, otherwise the @a default_value.
2631 * @since 0.19 */
2632 gint ui_get_gtk_settings_integer(const gchar *property_name, gint default_value)
2634 if (g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(
2635 gtk_settings_get_default())), property_name))
2637 gint value;
2638 g_object_get(G_OBJECT(gtk_settings_get_default()), property_name, &value, NULL);
2639 return value;
2641 else
2642 return default_value;
2646 void ui_editable_insert_text_callback(GtkEditable *editable, gchar *new_text,
2647 gint new_text_len, gint *position, gpointer data)
2649 gboolean first = position != NULL && *position == 0;
2650 gint i;
2652 if (new_text_len == -1)
2653 new_text_len = strlen(new_text);
2655 for (i = 0; i < new_text_len; i++, new_text++)
2657 if ((!first || !strchr("+-", *new_text)) && !isdigit(*new_text))
2659 g_signal_stop_emission_by_name(editable, "insert-text");
2660 break;
2662 first = FALSE;
2667 /* gets the icon that applies to a particular MIME type */
2668 GdkPixbuf *ui_get_mime_icon(const gchar *mime_type, GtkIconSize size)
2670 GdkPixbuf *icon = NULL;
2671 #if GTK_CHECK_VERSION(2, 14, 0)
2672 gchar *ctype;
2673 GIcon *gicon;
2674 GtkIconInfo *info;
2675 GtkIconTheme *theme;
2676 gint real_size;
2678 if (!gtk_icon_size_lookup(size, &real_size, NULL))
2680 g_return_val_if_reached(NULL);
2682 ctype = g_content_type_from_mime_type(mime_type);
2683 if (ctype != NULL)
2685 gicon = g_content_type_get_icon(ctype);
2686 theme = gtk_icon_theme_get_default();
2687 info = gtk_icon_theme_lookup_by_gicon(theme, gicon, real_size, 0);
2688 g_object_unref(gicon);
2689 g_free(ctype);
2691 if (info != NULL)
2693 icon = gtk_icon_info_load_icon(info, NULL);
2694 gtk_icon_info_free(info);
2697 #endif
2698 /* fallback for builds with GIO < 2.18 or if icon lookup failed, like it might happen on Windows */
2699 if (icon == NULL)
2701 const gchar *stock_id = GTK_STOCK_FILE;
2702 GtkIconSet *icon_set;
2704 if (strstr(mime_type, "directory"))
2705 stock_id = GTK_STOCK_DIRECTORY;
2707 icon_set = gtk_icon_factory_lookup_default(stock_id);
2708 if (icon_set)
2710 icon = gtk_icon_set_render_icon(icon_set, gtk_widget_get_default_style(),
2711 gtk_widget_get_default_direction(),
2712 GTK_STATE_NORMAL, size, NULL, NULL);
2715 return icon;
2719 void ui_focus_current_document(void)
2721 GeanyDocument *doc = document_get_current();
2723 if (doc != NULL)
2724 document_grab_focus(doc);