/* * gedit-window.c * This file is part of gedit * * Copyright (C) 2005 - Paolo Maggi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "gedit-window.h" #include #include #include #include #include #include #include "gedit-app.h" #include "gedit-app-private.h" #include "gedit-notebook.h" #include "gedit-notebook-popup-menu.h" #include "gedit-multi-notebook.h" #include "gedit-statusbar.h" #include "gedit-tab.h" #include "gedit-tab-private.h" #include "gedit-view-frame.h" #include "gedit-utils.h" #include "gedit-commands.h" #include "gedit-commands-private.h" #include "gedit-debug.h" #include "gedit-document.h" #include "gedit-document-private.h" #include "gedit-documents-panel.h" #include "gedit-plugins-engine.h" #include "gedit-window-activatable.h" #include "gedit-enum-types.h" #include "gedit-dirs.h" #include "gedit-status-menu-button.h" #include "gedit-settings.h" #include "gedit-menu-stack-switcher.h" struct _GeditWindowPrivate { GSettings *editor_settings; GSettings *ui_settings; GSettings *window_settings; GeditMultiNotebook *multi_notebook; GtkWidget *side_panel; GtkWidget *side_stack_switcher; GtkWidget *side_panel_inline_stack_switcher; GtkWidget *bottom_panel; GtkWidget *hpaned; GtkWidget *vpaned; GeditMessageBus *message_bus; PeasExtensionSet *extensions; /* Widgets for fullscreen mode */ GtkWidget *fullscreen_eventbox; GtkRevealer *fullscreen_revealer; GtkWidget *fullscreen_headerbar; GtkMenuButton *fullscreen_gear_button; GtkMenuButton *fullscreen_open_recent_button; /* statusbar and context ids for statusbar messages */ GtkWidget *statusbar; TeplOverwriteIndicator *overwrite_indicator; TeplLineColumnIndicator *line_column_indicator; GtkWidget *tab_width_button; GtkWidget *language_button; GtkWidget *language_popover; guint bracket_match_message_cid; guint tab_width_id; guint language_changed_id; /* Headerbars */ GtkWidget *side_headerbar; GtkWidget *headerbar; GtkMenuButton *gear_button; gint num_tabs_with_error; gint width; gint height; GdkWindowState window_state; gint side_panel_size; gint bottom_panel_size; GeditWindowState state; guint inhibition_cookie; gint bottom_panel_item_removed_handler_id; GtkWindowGroup *window_group; gchar *file_chooser_folder_uri; gchar *direct_save_uri; GSList *closed_docs_stack; guint removing_tabs : 1; guint dispose_has_run : 1; guint in_fullscreen_eventbox : 1; }; enum { PROP_0, PROP_STATE, LAST_PROP }; static GParamSpec *properties[LAST_PROP]; enum { TAB_ADDED, TAB_REMOVED, TABS_REORDERED, ACTIVE_TAB_CHANGED, ACTIVE_TAB_STATE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; enum { TARGET_URI_LIST = 100, TARGET_XDNDDIRECTSAVE }; static const GtkTargetEntry drop_types [] = { { "XdndDirectSave0", 0, TARGET_XDNDDIRECTSAVE }, /* XDS Protocol Type */ { "text/uri-list", 0, TARGET_URI_LIST} }; G_DEFINE_TYPE_WITH_PRIVATE (GeditWindow, gedit_window, GTK_TYPE_APPLICATION_WINDOW) /* Prototypes */ static void remove_actions (GeditWindow *window); static void gedit_window_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GeditWindow *window = GEDIT_WINDOW (object); switch (prop_id) { case PROP_STATE: g_value_set_flags (value, gedit_window_get_state (window)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void save_panels_state (GeditWindow *window) { const gchar *panel_page; gedit_debug (DEBUG_WINDOW); if (window->priv->side_panel_size > 0) { g_settings_set_int (window->priv->window_settings, GEDIT_SETTINGS_SIDE_PANEL_SIZE, window->priv->side_panel_size); } panel_page = gtk_stack_get_visible_child_name (GTK_STACK (window->priv->side_panel)); if (panel_page != NULL) { g_settings_set_string (window->priv->window_settings, GEDIT_SETTINGS_SIDE_PANEL_ACTIVE_PAGE, panel_page); } if (window->priv->bottom_panel_size > 0) { g_settings_set_int (window->priv->window_settings, GEDIT_SETTINGS_BOTTOM_PANEL_SIZE, window->priv->bottom_panel_size); } panel_page = gtk_stack_get_visible_child_name (GTK_STACK (window->priv->bottom_panel)); if (panel_page != NULL) { g_settings_set_string (window->priv->window_settings, GEDIT_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE, panel_page); } g_settings_apply (window->priv->window_settings); } static void save_window_state (GtkWidget *widget) { GeditWindow *window = GEDIT_WINDOW (widget); if ((window->priv->window_state & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) { gtk_window_get_size (GTK_WINDOW (widget), &window->priv->width, &window->priv->height); g_settings_set (window->priv->window_settings, GEDIT_SETTINGS_WINDOW_SIZE, "(ii)", window->priv->width, window->priv->height); } } static void gedit_window_dispose (GObject *object) { GeditWindow *window; gedit_debug (DEBUG_WINDOW); window = GEDIT_WINDOW (object); /* Stop tracking removal of panels otherwise we always * end up with thinking we had no panel active, since they * should all be removed below */ if (window->priv->bottom_panel_item_removed_handler_id != 0) { g_signal_handler_disconnect (window->priv->bottom_panel, window->priv->bottom_panel_item_removed_handler_id); window->priv->bottom_panel_item_removed_handler_id = 0; } /* First of all, force collection so that plugins * really drop some of the references. */ peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); /* save the panels position and make sure to deactivate plugins * for this window, but only once */ if (!window->priv->dispose_has_run) { save_window_state (GTK_WIDGET (window)); save_panels_state (window); /* Note that unreffing the extensions will automatically remove all extensions which in turn will deactivate the extension */ g_object_unref (window->priv->extensions); peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); window->priv->dispose_has_run = TRUE; } g_clear_object (&window->priv->message_bus); g_clear_object (&window->priv->window_group); /* We must free the settings after saving the panels */ g_clear_object (&window->priv->editor_settings); g_clear_object (&window->priv->ui_settings); g_clear_object (&window->priv->window_settings); /* Now that there have broken some reference loops, * force collection again. */ peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); g_clear_object (&window->priv->side_stack_switcher); /* GTK+/GIO unref the action map in an idle. For the last GeditWindow, * the application quits before the idle, so the action map is not * unreffed, and some objects are not finalized on application shutdown * (GeditView for example). * So this is just for making the debugging of object references a bit * nicer. */ remove_actions (window); window->priv->fullscreen_open_recent_button = NULL; G_OBJECT_CLASS (gedit_window_parent_class)->dispose (object); } static void gedit_window_finalize (GObject *object) { GeditWindow *window = GEDIT_WINDOW (object); g_free (window->priv->file_chooser_folder_uri); g_slist_free_full (window->priv->closed_docs_stack, (GDestroyNotify)g_object_unref); G_OBJECT_CLASS (gedit_window_parent_class)->finalize (object); } static void update_fullscreen (GeditWindow *window, gboolean is_fullscreen) { GAction *fullscreen_action; _gedit_multi_notebook_set_show_tabs (window->priv->multi_notebook, !is_fullscreen); if (is_fullscreen) { gtk_widget_hide (window->priv->statusbar); } else { if (g_settings_get_boolean (window->priv->ui_settings, "statusbar-visible")) { gtk_widget_show (window->priv->statusbar); } } #ifndef OS_OSX if (is_fullscreen) { gtk_widget_show_all (window->priv->fullscreen_eventbox); } else { gtk_widget_hide (window->priv->fullscreen_eventbox); } #endif fullscreen_action = g_action_map_lookup_action (G_ACTION_MAP (window), "fullscreen"); g_simple_action_set_state (G_SIMPLE_ACTION (fullscreen_action), g_variant_new_boolean (is_fullscreen)); } static gboolean gedit_window_window_state_event (GtkWidget *widget, GdkEventWindowState *event) { GeditWindow *window = GEDIT_WINDOW (widget); window->priv->window_state = event->new_window_state; g_settings_set_int (window->priv->window_settings, GEDIT_SETTINGS_WINDOW_STATE, window->priv->window_state); if ((event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) != 0) { update_fullscreen (window, (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0); } return GTK_WIDGET_CLASS (gedit_window_parent_class)->window_state_event (widget, event); } static gboolean gedit_window_configure_event (GtkWidget *widget, GdkEventConfigure *event) { GeditWindow *window = GEDIT_WINDOW (widget); if (gtk_widget_get_realized (widget) && (window->priv->window_state & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) == 0) { save_window_state (widget); } return GTK_WIDGET_CLASS (gedit_window_parent_class)->configure_event (widget, event); } /* * GtkWindow catches keybindings for the menu items _before_ passing them to * the focused widget. This is unfortunate and means that pressing ctrl+V * in an entry on a panel ends up pasting text in the TextView. * Here we override GtkWindow's handler to do the same things that it * does, but in the opposite order and then we chain up to the grand * parent handler, skipping gtk_window_key_press_event. */ static gboolean gedit_window_key_press_event (GtkWidget *widget, GdkEventKey *event) { static gpointer grand_parent_class = NULL; GtkWindow *window = GTK_WINDOW (widget); gboolean handled = FALSE; if (grand_parent_class == NULL) { grand_parent_class = g_type_class_peek_parent (gedit_window_parent_class); } /* handle focus widget key events */ if (!handled) { handled = gtk_window_propagate_key_event (window, event); } /* handle mnemonics and accelerators */ if (!handled) { handled = gtk_window_activate_key (window, event); } /* Chain up, invokes binding set on window */ if (!handled) { handled = GTK_WIDGET_CLASS (grand_parent_class)->key_press_event (widget, event); } if (!handled) { return gedit_app_process_window_event (GEDIT_APP (g_application_get_default ()), GEDIT_WINDOW (widget), (GdkEvent *)event); } return TRUE; } static void gedit_window_tab_removed (GeditWindow *window, GeditTab *tab) { peas_engine_garbage_collect (PEAS_ENGINE (gedit_plugins_engine_get_default ())); } static void gedit_window_class_init (GeditWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); klass->tab_removed = gedit_window_tab_removed; object_class->dispose = gedit_window_dispose; object_class->finalize = gedit_window_finalize; object_class->get_property = gedit_window_get_property; widget_class->window_state_event = gedit_window_window_state_event; widget_class->configure_event = gedit_window_configure_event; widget_class->key_press_event = gedit_window_key_press_event; properties[PROP_STATE] = g_param_spec_flags ("state", "State", "The window's state", GEDIT_TYPE_WINDOW_STATE, GEDIT_WINDOW_STATE_NORMAL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, LAST_PROP, properties); signals[TAB_ADDED] = g_signal_new ("tab-added", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GeditWindowClass, tab_added), NULL, NULL, NULL, G_TYPE_NONE, 1, GEDIT_TYPE_TAB); signals[TAB_REMOVED] = g_signal_new ("tab-removed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GeditWindowClass, tab_removed), NULL, NULL, NULL, G_TYPE_NONE, 1, GEDIT_TYPE_TAB); signals[TABS_REORDERED] = g_signal_new ("tabs-reordered", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GeditWindowClass, tabs_reordered), NULL, NULL, NULL, G_TYPE_NONE, 0); signals[ACTIVE_TAB_CHANGED] = g_signal_new ("active-tab-changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GeditWindowClass, active_tab_changed), NULL, NULL, NULL, G_TYPE_NONE, 1, GEDIT_TYPE_TAB); signals[ACTIVE_TAB_STATE_CHANGED] = g_signal_new ("active-tab-state-changed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GeditWindowClass, active_tab_state_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /* Bind class to template */ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/gedit/ui/gedit-window.ui"); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_headerbar); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, headerbar); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, gear_button); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, hpaned); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_panel); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, side_panel_inline_stack_switcher); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, vpaned); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, multi_notebook); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, bottom_panel); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, statusbar); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, language_button); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, tab_width_button); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_eventbox); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_revealer); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_headerbar); gtk_widget_class_bind_template_child_private (widget_class, GeditWindow, fullscreen_gear_button); } static void received_clipboard_contents (GtkClipboard *clipboard, GtkSelectionData *selection_data, GeditWindow *window) { GeditTab *tab; gboolean enabled; GAction *action; /* getting clipboard contents is async, so we need to * get the current tab and its state */ tab = gedit_window_get_active_tab (window); if (tab != NULL) { GeditTabState state; gboolean state_normal; state = gedit_tab_get_state (tab); state_normal = (state == GEDIT_TAB_STATE_NORMAL); enabled = state_normal && gtk_selection_data_targets_include_text (selection_data); } else { enabled = FALSE; } action = g_action_map_lookup_action (G_ACTION_MAP (window), "paste"); /* Since this is emitted async, the disposal of the actions may have * already happened. Ensure that we have an action before setting the * state. */ if (action != NULL) { g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); } g_object_unref (window); } static void set_paste_sensitivity_according_to_clipboard (GeditWindow *window, GtkClipboard *clipboard) { GdkDisplay *display; display = gtk_clipboard_get_display (clipboard); if (gdk_display_supports_selection_notification (display)) { gtk_clipboard_request_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"), (GtkClipboardReceivedFunc) received_clipboard_contents, g_object_ref (window)); } else { GAction *action; action = g_action_map_lookup_action (G_ACTION_MAP (window), "paste"); /* XFIXES extension not availbale, make * Paste always sensitive */ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE); } } static void extension_update_state (PeasExtensionSet *extensions, PeasPluginInfo *info, PeasExtension *exten, GeditWindow *window) { gedit_window_activatable_update_state (GEDIT_WINDOW_ACTIVATABLE (exten)); } static void update_actions_sensitivity (GeditWindow *window) { GeditNotebook *notebook; GeditTab *tab; gint num_notebooks; gint num_tabs; GeditTabState state = GEDIT_TAB_STATE_NORMAL; GeditDocument *doc = NULL; GtkSourceFile *file = NULL; GeditView *view = NULL; gint tab_number = -1; GAction *action; gboolean editable = FALSE; gboolean empty_search = FALSE; GtkClipboard *clipboard; gboolean enable_syntax_highlighting; gedit_debug (DEBUG_WINDOW); notebook = gedit_multi_notebook_get_active_notebook (window->priv->multi_notebook); tab = gedit_multi_notebook_get_active_tab (window->priv->multi_notebook); num_notebooks = gedit_multi_notebook_get_n_notebooks (window->priv->multi_notebook); num_tabs = gedit_multi_notebook_get_n_tabs (window->priv->multi_notebook); if (notebook != NULL && tab != NULL) { state = gedit_tab_get_state (tab); view = gedit_tab_get_view (tab); doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); file = gedit_document_get_file (doc); tab_number = gtk_notebook_page_num (GTK_NOTEBOOK (notebook), GTK_WIDGET (tab)); editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view)); empty_search = _gedit_document_get_empty_search (doc); } clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD); action = g_action_map_lookup_action (G_ACTION_MAP (window), "save"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (file != NULL) && !gtk_source_file_is_readonly (file)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "save-as"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_SAVING_ERROR) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "revert"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL) && !_gedit_document_is_untitled (doc)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "reopen-closed-tab"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (window->priv->closed_docs_stack != NULL)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "print"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && (doc != NULL)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "close"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state != GEDIT_TAB_STATE_CLOSING) && (state != GEDIT_TAB_STATE_SAVING) && (state != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW) && (state != GEDIT_TAB_STATE_PRINTING) && (state != GEDIT_TAB_STATE_SAVING_ERROR)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "undo"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state == GEDIT_TAB_STATE_NORMAL) && (doc != NULL) && gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc))); action = g_action_map_lookup_action (G_ACTION_MAP (window), "redo"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state == GEDIT_TAB_STATE_NORMAL) && (doc != NULL) && gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc))); action = g_action_map_lookup_action (G_ACTION_MAP (window), "cut"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state == GEDIT_TAB_STATE_NORMAL) && editable && (doc != NULL) && gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); action = g_action_map_lookup_action (G_ACTION_MAP (window), "copy"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL) && gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); action = g_action_map_lookup_action (G_ACTION_MAP (window), "paste"); if (num_tabs > 0 && (state == GEDIT_TAB_STATE_NORMAL) && editable) { set_paste_sensitivity_according_to_clipboard (window, clipboard); } else { g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); } action = g_action_map_lookup_action (G_ACTION_MAP (window), "delete"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state == GEDIT_TAB_STATE_NORMAL) && editable && (doc != NULL) && gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); action = g_action_map_lookup_action (G_ACTION_MAP (window), "overwrite-mode"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), doc != NULL); action = g_action_map_lookup_action (G_ACTION_MAP (window), "find"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "replace"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state == GEDIT_TAB_STATE_NORMAL) && (doc != NULL) && editable); action = g_action_map_lookup_action (G_ACTION_MAP (window), "find-next"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL) && !empty_search); action = g_action_map_lookup_action (G_ACTION_MAP (window), "find-prev"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL) && !empty_search); action = g_action_map_lookup_action (G_ACTION_MAP (window), "clear-highlight"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL) && !empty_search); action = g_action_map_lookup_action (G_ACTION_MAP (window), "goto-line"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), ((state == GEDIT_TAB_STATE_NORMAL) || (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && (doc != NULL)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "highlight-mode"); enable_syntax_highlighting = g_settings_get_boolean (window->priv->editor_settings, GEDIT_SETTINGS_SYNTAX_HIGHLIGHTING); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (state != GEDIT_TAB_STATE_CLOSING) && (doc != NULL) && enable_syntax_highlighting); action = g_action_map_lookup_action (G_ACTION_MAP (window), "move-to-new-window"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), num_tabs > 1); action = g_action_map_lookup_action (G_ACTION_MAP (window), "previous-document"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), tab_number > 0); action = g_action_map_lookup_action (G_ACTION_MAP (window), "next-document"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), tab_number >= 0 && tab_number < gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) - 1); action = g_action_map_lookup_action (G_ACTION_MAP (window), "new-tab-group"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), num_tabs > 0); action = g_action_map_lookup_action (G_ACTION_MAP (window), "previous-tab-group"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), num_notebooks > 1); action = g_action_map_lookup_action (G_ACTION_MAP (window), "next-tab-group"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), num_notebooks > 1); /* We disable File->Quit/SaveAll/CloseAll while printing to avoid to have two operations (save and print/print preview) that uses the message area at the same time (may be we can remove this limitation in the future) */ /* We disable File->Quit/CloseAll if state is saving since saving cannot be cancelled (may be we can remove this limitation in the future) */ action = g_action_map_lookup_action (G_ACTION_MAP (g_application_get_default ()), "quit"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "save-all"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && num_tabs > 0); action = g_action_map_lookup_action (G_ACTION_MAP (window), "close-all"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), num_tabs > 0 && !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && num_tabs > 0); peas_extension_set_foreach (window->priv->extensions, (PeasExtensionSetForeachFunc) extension_update_state, window); } static void language_chooser_show_cb (TeplLanguageChooser *language_chooser, GeditWindow *window) { GeditDocument *active_document; active_document = gedit_window_get_active_document (window); if (active_document != NULL) { GtkSourceLanguage *language; language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (active_document)); tepl_language_chooser_select_language (language_chooser, language); } } static void language_activated_cb (TeplLanguageChooser *language_chooser, GtkSourceLanguage *language, GeditWindow *window) { GeditDocument *active_document; active_document = gedit_window_get_active_document (window); if (active_document != NULL) { gedit_document_set_language (active_document, language); } gtk_widget_hide (window->priv->language_popover); } static void setup_statusbar (GeditWindow *window) { TeplLanguageChooserWidget *language_chooser; gedit_debug (DEBUG_WINDOW); window->priv->bracket_match_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "bracket_match_message"); g_settings_bind (window->priv->ui_settings, "statusbar-visible", window->priv->statusbar, "visible", G_SETTINGS_BIND_GET); /* Insert/Overwrite indicator */ window->priv->overwrite_indicator = tepl_overwrite_indicator_new (); gtk_widget_show (GTK_WIDGET (window->priv->overwrite_indicator)); gtk_box_pack_end (GTK_BOX (window->priv->statusbar), GTK_WIDGET (window->priv->overwrite_indicator), FALSE, FALSE, 0); // Explicit positioning. gtk_box_reorder_child (GTK_BOX (window->priv->statusbar), GTK_WIDGET (window->priv->overwrite_indicator), 0); /* Line/Column indicator */ window->priv->line_column_indicator = tepl_line_column_indicator_new (); gtk_widget_show (GTK_WIDGET (window->priv->line_column_indicator)); gtk_box_pack_end (GTK_BOX (window->priv->statusbar), GTK_WIDGET (window->priv->line_column_indicator), FALSE, FALSE, 0); // Explicit positioning. gtk_box_reorder_child (GTK_BOX (window->priv->statusbar), GTK_WIDGET (window->priv->line_column_indicator), 1); /* Tab Width button */ gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (window->priv->tab_width_button), _gedit_app_get_tab_width_menu (GEDIT_APP (g_application_get_default ()))); /* Language button */ window->priv->language_popover = gtk_popover_new (window->priv->language_button); gtk_menu_button_set_popover (GTK_MENU_BUTTON (window->priv->language_button), window->priv->language_popover); language_chooser = tepl_language_chooser_widget_new (); g_signal_connect (language_chooser, "show", G_CALLBACK (language_chooser_show_cb), window); g_signal_connect (language_chooser, "language-activated", G_CALLBACK (language_activated_cb), window); gtk_container_add (GTK_CONTAINER (window->priv->language_popover), GTK_WIDGET (language_chooser)); gtk_widget_show (GTK_WIDGET (language_chooser)); } static GeditWindow * clone_window (GeditWindow *origin) { GeditWindow *window; GdkScreen *screen; GeditApp *app; const gchar *panel_page; gedit_debug (DEBUG_WINDOW); app = GEDIT_APP (g_application_get_default ()); screen = gtk_window_get_screen (GTK_WINDOW (origin)); window = gedit_app_create_window (app, screen); gtk_window_set_default_size (GTK_WINDOW (window), origin->priv->width, origin->priv->height); if ((origin->priv->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) gtk_window_maximize (GTK_WINDOW (window)); else gtk_window_unmaximize (GTK_WINDOW (window)); if ((origin->priv->window_state & GDK_WINDOW_STATE_STICKY) != 0) gtk_window_stick (GTK_WINDOW (window)); else gtk_window_unstick (GTK_WINDOW (window)); /* set the panels size, the paned position will be set when * they are mapped */ window->priv->side_panel_size = origin->priv->side_panel_size; window->priv->bottom_panel_size = origin->priv->bottom_panel_size; panel_page = gtk_stack_get_visible_child_name (GTK_STACK (origin->priv->side_panel)); if (panel_page) { gtk_stack_set_visible_child_name (GTK_STACK (window->priv->side_panel), panel_page); } panel_page = gtk_stack_get_visible_child_name (GTK_STACK (origin->priv->bottom_panel)); if (panel_page) { gtk_stack_set_visible_child_name (GTK_STACK (window->priv->bottom_panel), panel_page); } gtk_widget_set_visible (window->priv->side_panel, gtk_widget_get_visible (origin->priv->side_panel)); gtk_widget_set_visible (window->priv->bottom_panel, gtk_widget_get_visible (origin->priv->bottom_panel)); return window; } static void bracket_matched_cb (GtkSourceBuffer *buffer, GtkTextIter *iter, GtkSourceBracketMatchType result, GeditWindow *window) { if (buffer != GTK_SOURCE_BUFFER (gedit_window_get_active_document (window))) return; switch (result) { case GTK_SOURCE_BRACKET_MATCH_NONE: gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->bracket_match_message_cid); break; case GTK_SOURCE_BRACKET_MATCH_OUT_OF_RANGE: gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), window->priv->bracket_match_message_cid, _("Bracket match is out of range")); break; case GTK_SOURCE_BRACKET_MATCH_NOT_FOUND: gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), window->priv->bracket_match_message_cid, _("Bracket match not found")); break; case GTK_SOURCE_BRACKET_MATCH_FOUND: gedit_statusbar_flash_message (GEDIT_STATUSBAR (window->priv->statusbar), window->priv->bracket_match_message_cid, _("Bracket match found on line: %d"), gtk_text_iter_get_line (iter) + 1); break; default: g_assert_not_reached (); } } static void set_overwrite_mode (GeditWindow *window, gboolean overwrite) { GAction *action; tepl_overwrite_indicator_set_overwrite (window->priv->overwrite_indicator, overwrite); gtk_widget_show (GTK_WIDGET (window->priv->overwrite_indicator)); action = g_action_map_lookup_action (G_ACTION_MAP (window), "overwrite-mode"); g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (overwrite)); } static void overwrite_mode_changed (GtkTextView *view, GParamSpec *pspec, GeditWindow *window) { if (view != GTK_TEXT_VIEW (gedit_window_get_active_view (window))) return; set_overwrite_mode (window, gtk_text_view_get_overwrite (view)); } #define MAX_TITLE_LENGTH 100 static void set_title (GeditWindow *window) { GeditTab *tab; GeditDocument *doc = NULL; GtkSourceFile *file; gchar *name; gchar *dirname = NULL; gchar *main_title = NULL; gchar *title = NULL; gchar *subtitle = NULL; gint len; tab = gedit_window_get_active_tab (window); if (tab == NULL) { gedit_app_set_window_title (GEDIT_APP (g_application_get_default ()), window, "gedit"); gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->headerbar), "gedit"); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->headerbar), NULL); gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), "gedit"); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), NULL); return; } doc = gedit_tab_get_document (tab); g_return_if_fail (doc != NULL); file = gedit_document_get_file (doc); name = tepl_file_get_short_name (tepl_buffer_get_file (TEPL_BUFFER (doc))); len = g_utf8_strlen (name, -1); /* if the name is awfully long, truncate it and be done with it, * otherwise also show the directory (ellipsized if needed) */ if (len > MAX_TITLE_LENGTH) { gchar *tmp; tmp = tepl_utils_str_middle_truncate (name, MAX_TITLE_LENGTH); g_free (name); name = tmp; } else { GFile *location = gtk_source_file_get_location (file); if (location != NULL) { gchar *str = gedit_utils_location_get_dirname_for_display (location); /* use the remaining space for the dir, but use a min of 20 chars * so that we do not end up with a dirname like "(a...b)". * This means that in the worst case when the filename is long 99 * we have a title long 99 + 20, but I think it's a rare enough * case to be acceptable. It's justa darn title afterall :) */ dirname = tepl_utils_str_middle_truncate (str, MAX (20, MAX_TITLE_LENGTH - len)); g_free (str); } } if (gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (doc))) { gchar *tmp_name; tmp_name = g_strdup_printf ("*%s", name); g_free (name); name = tmp_name; } if (gtk_source_file_is_readonly (file)) { title = g_strdup_printf ("%s [%s]", name, _("Read-Only")); if (dirname != NULL) { main_title = g_strdup_printf ("%s [%s] (%s) - gedit", name, _("Read-Only"), dirname); subtitle = dirname; } else { main_title = g_strdup_printf ("%s [%s] - gedit", name, _("Read-Only")); } } else { title = g_strdup (name); if (dirname != NULL) { main_title = g_strdup_printf ("%s (%s) - gedit", name, dirname); subtitle = dirname; } else { main_title = g_strdup_printf ("%s - gedit", name); } } gedit_app_set_window_title (GEDIT_APP (g_application_get_default ()), window, main_title); gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->headerbar), title); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->headerbar), subtitle); gtk_header_bar_set_title (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), title); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (window->priv->fullscreen_headerbar), subtitle); g_free (dirname); g_free (name); g_free (title); g_free (main_title); } #undef MAX_TITLE_LENGTH static void tab_width_changed (GObject *object, GParamSpec *pspec, GeditWindow *window) { guint new_tab_width; gchar *label; new_tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (object)); label = g_strdup_printf (_("Tab Width: %u"), new_tab_width); gedit_status_menu_button_set_label (GEDIT_STATUS_MENU_BUTTON (window->priv->tab_width_button), label); g_free (label); } static void language_changed (GObject *object, GParamSpec *pspec, GeditWindow *window) { GtkSourceLanguage *new_language; const gchar *label; new_language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (object)); if (new_language) label = gtk_source_language_get_name (new_language); else label = _("Plain Text"); gedit_status_menu_button_set_label (GEDIT_STATUS_MENU_BUTTON (window->priv->language_button), label); peas_extension_set_foreach (window->priv->extensions, (PeasExtensionSetForeachFunc) extension_update_state, window); } static void remove_actions (GeditWindow *window) { g_action_map_remove_action (G_ACTION_MAP (window), "tab-width"); g_action_map_remove_action (G_ACTION_MAP (window), "use-spaces"); } static void sync_current_tab_actions (GeditWindow *window, GeditView *old_view, GeditView *new_view) { if (old_view != NULL) { remove_actions (window); } if (new_view != NULL) { GPropertyAction *action; action = g_property_action_new ("tab-width", new_view, "tab-width"); g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (action)); g_object_unref (action); action = g_property_action_new ("use-spaces", new_view, "insert-spaces-instead-of-tabs"); g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (action)); g_object_unref (action); } } static void update_statusbar (GeditWindow *window, GeditView *old_view, GeditView *new_view) { if (old_view) { if (window->priv->tab_width_id) { g_signal_handler_disconnect (old_view, window->priv->tab_width_id); window->priv->tab_width_id = 0; } if (window->priv->language_changed_id) { g_signal_handler_disconnect (gtk_text_view_get_buffer (GTK_TEXT_VIEW (old_view)), window->priv->language_changed_id); window->priv->language_changed_id = 0; } } if (new_view) { GeditDocument *doc; doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (new_view))); set_overwrite_mode (window, gtk_text_view_get_overwrite (GTK_TEXT_VIEW (new_view))); tepl_line_column_indicator_set_view (window->priv->line_column_indicator, TEPL_VIEW (new_view)); gtk_widget_show (GTK_WIDGET (window->priv->line_column_indicator)); gtk_widget_show (window->priv->tab_width_button); gtk_widget_show (window->priv->language_button); window->priv->tab_width_id = g_signal_connect (new_view, "notify::tab-width", G_CALLBACK (tab_width_changed), window); window->priv->language_changed_id = g_signal_connect (doc, "notify::language", G_CALLBACK (language_changed), window); /* call it for the first time */ tab_width_changed (G_OBJECT (new_view), NULL, window); language_changed (G_OBJECT (doc), NULL, window); } } static void tab_switched (GeditMultiNotebook *mnb, GeditNotebook *old_notebook, GeditTab *old_tab, GeditNotebook *new_notebook, GeditTab *new_tab, GeditWindow *window) { GeditView *old_view, *new_view; old_view = old_tab ? gedit_tab_get_view (old_tab) : NULL; new_view = new_tab ? gedit_tab_get_view (new_tab) : NULL; sync_current_tab_actions (window, old_view, new_view); update_statusbar (window, old_view, new_view); if (new_tab == NULL || window->priv->dispose_has_run) return; set_title (window); update_actions_sensitivity (window); g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_CHANGED], 0, new_tab); } static void analyze_tab_state (GeditTab *tab, GeditWindow *window) { GeditTabState ts; ts = gedit_tab_get_state (tab); switch (ts) { case GEDIT_TAB_STATE_LOADING: case GEDIT_TAB_STATE_REVERTING: window->priv->state |= GEDIT_WINDOW_STATE_LOADING; break; case GEDIT_TAB_STATE_SAVING: window->priv->state |= GEDIT_WINDOW_STATE_SAVING; break; case GEDIT_TAB_STATE_PRINTING: window->priv->state |= GEDIT_WINDOW_STATE_PRINTING; break; case GEDIT_TAB_STATE_LOADING_ERROR: case GEDIT_TAB_STATE_REVERTING_ERROR: case GEDIT_TAB_STATE_SAVING_ERROR: case GEDIT_TAB_STATE_GENERIC_ERROR: window->priv->state |= GEDIT_WINDOW_STATE_ERROR; ++window->priv->num_tabs_with_error; default: /* NOP */ break; } } static void update_window_state (GeditWindow *window) { GeditWindowState old_ws; gint old_num_of_errors; gedit_debug_message (DEBUG_WINDOW, "Old state: %x", window->priv->state); old_ws = window->priv->state; old_num_of_errors = window->priv->num_tabs_with_error; window->priv->state = 0; window->priv->num_tabs_with_error = 0; gedit_multi_notebook_foreach_tab (window->priv->multi_notebook, (GtkCallback)analyze_tab_state, window); gedit_debug_message (DEBUG_WINDOW, "New state: %x", window->priv->state); if (old_ws != window->priv->state) { update_actions_sensitivity (window); gedit_statusbar_set_window_state (GEDIT_STATUSBAR (window->priv->statusbar), window->priv->state, window->priv->num_tabs_with_error); g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); } else if (old_num_of_errors != window->priv->num_tabs_with_error) { gedit_statusbar_set_window_state (GEDIT_STATUSBAR (window->priv->statusbar), window->priv->state, window->priv->num_tabs_with_error); } } static void update_can_close (GeditWindow *window) { GeditWindowPrivate *priv = window->priv; GList *tabs; GList *l; gboolean can_close = TRUE; gedit_debug (DEBUG_WINDOW); tabs = gedit_multi_notebook_get_all_tabs (priv->multi_notebook); for (l = tabs; l != NULL; l = g_list_next (l)) { GeditTab *tab = l->data; if (!_gedit_tab_get_can_close (tab)) { can_close = FALSE; break; } } if (can_close && (priv->inhibition_cookie != 0)) { gtk_application_uninhibit (GTK_APPLICATION (g_application_get_default ()), priv->inhibition_cookie); priv->inhibition_cookie = 0; } else if (!can_close && (priv->inhibition_cookie == 0)) { priv->inhibition_cookie = gtk_application_inhibit (GTK_APPLICATION (g_application_get_default ()), GTK_WINDOW (window), GTK_APPLICATION_INHIBIT_LOGOUT, _("There are unsaved documents")); } g_list_free (tabs); } static void sync_state (GeditTab *tab, GParamSpec *pspec, GeditWindow *window) { gedit_debug (DEBUG_WINDOW); update_window_state (window); if (tab == gedit_window_get_active_tab (window)) { update_actions_sensitivity (window); g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_STATE_CHANGED], 0); } } static void sync_name (GeditTab *tab, GParamSpec *pspec, GeditWindow *window) { if (tab == gedit_window_get_active_tab (window)) { set_title (window); update_actions_sensitivity (window); } } static void sync_can_close (GeditTab *tab, GParamSpec *pspec, GeditWindow *window) { update_can_close (window); } static GeditWindow * get_drop_window (GtkWidget *widget) { GtkWidget *target_window; target_window = gtk_widget_get_toplevel (widget); g_return_val_if_fail (GEDIT_IS_WINDOW (target_window), NULL); return GEDIT_WINDOW (target_window); } static void load_uris_from_drop (GeditWindow *window, gchar **uri_list) { GSList *locations = NULL; gint i; GSList *loaded; if (uri_list == NULL) return; for (i = 0; uri_list[i] != NULL; ++i) { locations = g_slist_prepend (locations, g_file_new_for_uri (uri_list[i])); } locations = g_slist_reverse (locations); loaded = gedit_commands_load_locations (window, locations, NULL, 0, 0); g_slist_free (loaded); g_slist_free_full (locations, g_object_unref); } /* Handle drops on the GeditWindow */ static void drag_data_received_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint info, guint timestamp, gpointer data) { GeditWindow *window; gchar **uri_list; window = get_drop_window (widget); if (window == NULL) return; switch (info) { case TARGET_URI_LIST: uri_list = gedit_utils_drop_get_uris(selection_data); load_uris_from_drop (window, uri_list); g_strfreev (uri_list); gtk_drag_finish (context, TRUE, FALSE, timestamp); break; case TARGET_XDNDDIRECTSAVE: /* Indicate that we don't provide "F" fallback */ if (gtk_selection_data_get_format (selection_data) == 8 && gtk_selection_data_get_length (selection_data) == 1 && gtk_selection_data_get_data (selection_data)[0] == 'F') { gdk_property_change (gdk_drag_context_get_source_window (context), gdk_atom_intern ("XdndDirectSave0", FALSE), gdk_atom_intern ("text/plain", FALSE), 8, GDK_PROP_MODE_REPLACE, (const guchar *) "", 0); } else if (gtk_selection_data_get_format (selection_data) == 8 && gtk_selection_data_get_length (selection_data) == 1 && gtk_selection_data_get_data (selection_data)[0] == 'S' && window->priv->direct_save_uri != NULL) { gchar **uris; uris = g_new (gchar *, 2); uris[0] = window->priv->direct_save_uri; uris[1] = NULL; load_uris_from_drop (window, uris); g_free (uris); } g_free (window->priv->direct_save_uri); window->priv->direct_save_uri = NULL; gtk_drag_finish (context, TRUE, FALSE, timestamp); break; } } static void drag_drop_cb (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data) { GeditWindow *window; GtkTargetList *target_list; GdkAtom target; window = get_drop_window (widget); target_list = gtk_drag_dest_get_target_list (widget); target = gtk_drag_dest_find_target (widget, context, target_list); if (target != GDK_NONE) { guint info; gboolean found; found = gtk_target_list_find (target_list, target, &info); g_assert (found); if (info == TARGET_XDNDDIRECTSAVE) { gchar *uri; uri = gedit_utils_set_direct_save_filename (context); if (uri != NULL) { g_free (window->priv->direct_save_uri); window->priv->direct_save_uri = uri; } } gtk_drag_get_data (GTK_WIDGET (widget), context, target, time); } } /* Handle drops on the GeditView */ static void drop_uris_cb (GtkWidget *widget, gchar **uri_list, GeditWindow *window) { load_uris_from_drop (window, uri_list); } static void update_fullscreen_revealer_state (GeditWindow *window) { gboolean open_recent_menu_is_active; gboolean hamburger_menu_is_active; open_recent_menu_is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_recent_button)); hamburger_menu_is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (window->priv->fullscreen_gear_button)); gtk_revealer_set_reveal_child (window->priv->fullscreen_revealer, (window->priv->in_fullscreen_eventbox || open_recent_menu_is_active || hamburger_menu_is_active)); } static gboolean on_fullscreen_eventbox_enter_notify_event (GtkWidget *fullscreen_eventbox, GdkEventCrossing *event, GeditWindow *window) { window->priv->in_fullscreen_eventbox = TRUE; update_fullscreen_revealer_state (window); return GDK_EVENT_PROPAGATE; } static gboolean on_fullscreen_eventbox_leave_notify_event (GtkWidget *fullscreen_eventbox, GdkEventCrossing *event, GeditWindow *window) { if (-1.0 <= event->y && event->y <= 0.0) { /* Ignore the event. * * Leave notify events are received with -1 <= y <= 0 * coordinates, although the GeditWindow is in fullscreen mode * and when there are no screens above (it's maybe a bug in an * underlying library). * If we hide the headerbar when those events happen, then it * makes the headerbar to be shown/hidden a lot of time in a * short period of time, i.e. a "stuttering". In other words * lots of leave/enter events are received when moving the mouse * upwards on the screen when the mouse is already at the top. * The expected leave event has a positive event->y value being * >= to the height of the headerbar (approximately * 40 <= y <= 50). So clearly when we receive a leave event with * event->y <= 0, it means that the mouse has left the eventbox * on the wrong side. * The -1.0 <= event->y is there (instead of just <= 0.0) in the * case that there is another screen *above*, even if this * heuristic/workaround is not perfect in that case. But that * case is quite rare, so it's probably a good enough solution. * * Note that apparently the "stuttering" occurs only on an Xorg * session, not on Wayland (tested with GNOME). * * If you see a better solution... */ return GDK_EVENT_PROPAGATE; } window->priv->in_fullscreen_eventbox = FALSE; update_fullscreen_revealer_state (window); return GDK_EVENT_PROPAGATE; } static void setup_fullscreen_eventbox (GeditWindow *window) { gtk_widget_set_size_request (window->priv->fullscreen_eventbox, -1, 1); gtk_widget_hide (window->priv->fullscreen_eventbox); g_signal_connect (window->priv->fullscreen_eventbox, "enter-notify-event", G_CALLBACK (on_fullscreen_eventbox_enter_notify_event), window); g_signal_connect (window->priv->fullscreen_eventbox, "leave-notify-event", G_CALLBACK (on_fullscreen_eventbox_leave_notify_event), window); } static void empty_search_notify_cb (GeditDocument *doc, GParamSpec *pspec, GeditWindow *window) { if (doc == gedit_window_get_active_document (window)) { update_actions_sensitivity (window); } } static void can_undo (GeditDocument *doc, GParamSpec *pspec, GeditWindow *window) { if (doc == gedit_window_get_active_document (window)) { update_actions_sensitivity (window); } } static void can_redo (GeditDocument *doc, GParamSpec *pspec, GeditWindow *window) { if (doc == gedit_window_get_active_document (window)) { update_actions_sensitivity (window); } } static void selection_changed (GeditDocument *doc, GParamSpec *pspec, GeditWindow *window) { if (doc == gedit_window_get_active_document (window)) { update_actions_sensitivity (window); } } static void readonly_changed (GtkSourceFile *file, GParamSpec *pspec, GeditWindow *window) { update_actions_sensitivity (window); sync_name (gedit_window_get_active_tab (window), NULL, window); peas_extension_set_foreach (window->priv->extensions, (PeasExtensionSetForeachFunc) extension_update_state, window); } static void editable_changed (GeditView *view, GParamSpec *arg1, GeditWindow *window) { peas_extension_set_foreach (window->priv->extensions, (PeasExtensionSetForeachFunc) extension_update_state, window); } static void on_tab_added (GeditMultiNotebook *multi, GeditNotebook *notebook, GeditTab *tab, GeditWindow *window) { GeditView *view; GeditDocument *doc; GtkSourceFile *file; gedit_debug (DEBUG_WINDOW); update_actions_sensitivity (window); view = gedit_tab_get_view (tab); doc = gedit_tab_get_document (tab); file = gedit_document_get_file (doc); /* IMPORTANT: remember to disconnect the signal in notebook_tab_removed * if a new signal is connected here */ g_signal_connect (tab, "notify::name", G_CALLBACK (sync_name), window); g_signal_connect (tab, "notify::state", G_CALLBACK (sync_state), window); g_signal_connect (tab, "notify::can-close", G_CALLBACK (sync_can_close), window); g_signal_connect (tab, "drop_uris", G_CALLBACK (drop_uris_cb), window); g_signal_connect (doc, "bracket-matched", G_CALLBACK (bracket_matched_cb), window); g_signal_connect (doc, "notify::empty-search", G_CALLBACK (empty_search_notify_cb), window); g_signal_connect (doc, "notify::can-undo", G_CALLBACK (can_undo), window); g_signal_connect (doc, "notify::can-redo", G_CALLBACK (can_redo), window); g_signal_connect (doc, "notify::has-selection", G_CALLBACK (selection_changed), window); g_signal_connect (view, "notify::overwrite", G_CALLBACK (overwrite_mode_changed), window); g_signal_connect (view, "notify::editable", G_CALLBACK (editable_changed), window); g_signal_connect (file, "notify::read-only", G_CALLBACK (readonly_changed), window); update_window_state (window); update_can_close (window); g_signal_emit (G_OBJECT (window), signals[TAB_ADDED], 0, tab); } static void push_last_closed_doc (GeditWindow *window, GeditDocument *doc) { GeditWindowPrivate *priv = window->priv; GtkSourceFile *file = gedit_document_get_file (doc); GFile *location = gtk_source_file_get_location (file); if (location != NULL) { priv->closed_docs_stack = g_slist_prepend (priv->closed_docs_stack, location); g_object_ref (location); } } GFile * _gedit_window_pop_last_closed_doc (GeditWindow *window) { GeditWindowPrivate *priv = window->priv; GFile *f = NULL; if (window->priv->closed_docs_stack != NULL) { f = priv->closed_docs_stack->data; priv->closed_docs_stack = g_slist_remove (priv->closed_docs_stack, f); } return f; } static void on_tab_removed (GeditMultiNotebook *multi, GeditNotebook *notebook, GeditTab *tab, GeditWindow *window) { GeditView *view; GeditDocument *doc; gint num_tabs; gedit_debug (DEBUG_WINDOW); num_tabs = gedit_multi_notebook_get_n_tabs (multi); view = gedit_tab_get_view (tab); doc = gedit_tab_get_document (tab); g_signal_handlers_disconnect_by_func (tab, G_CALLBACK (sync_name), window); g_signal_handlers_disconnect_by_func (tab, G_CALLBACK (sync_state), window); g_signal_handlers_disconnect_by_func (tab, G_CALLBACK (sync_can_close), window); g_signal_handlers_disconnect_by_func (tab, G_CALLBACK (drop_uris_cb), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (bracket_matched_cb), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (empty_search_notify_cb), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (can_undo), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (can_redo), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (selection_changed), window); g_signal_handlers_disconnect_by_func (doc, G_CALLBACK (readonly_changed), window); g_signal_handlers_disconnect_by_func (view, G_CALLBACK (overwrite_mode_changed), window); g_signal_handlers_disconnect_by_func (view, G_CALLBACK (editable_changed), window); if (tab == gedit_multi_notebook_get_active_tab (multi)) { if (window->priv->tab_width_id) { g_signal_handler_disconnect (view, window->priv->tab_width_id); window->priv->tab_width_id = 0; } if (window->priv->language_changed_id) { g_signal_handler_disconnect (doc, window->priv->language_changed_id); window->priv->language_changed_id = 0; } gedit_multi_notebook_set_active_tab (multi, NULL); } g_return_if_fail (num_tabs >= 0); if (num_tabs == 0) { set_title (window); /* hide the additional widgets */ gtk_widget_hide (GTK_WIDGET (window->priv->overwrite_indicator)); gtk_widget_hide (GTK_WIDGET (window->priv->line_column_indicator)); gtk_widget_hide (window->priv->tab_width_button); gtk_widget_hide (window->priv->language_button); } if (!window->priv->dispose_has_run) { push_last_closed_doc (window, doc); if ((!window->priv->removing_tabs && gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)) > 0) || num_tabs == 0) { update_actions_sensitivity (window); } } update_window_state (window); update_can_close (window); g_signal_emit (G_OBJECT (window), signals[TAB_REMOVED], 0, tab); } static void on_page_reordered (GeditMultiNotebook *multi, GeditNotebook *notebook, GtkWidget *page, gint page_num, GeditWindow *window) { update_actions_sensitivity (window); g_signal_emit (G_OBJECT (window), signals[TABS_REORDERED], 0); } static GtkNotebook * on_notebook_create_window (GeditMultiNotebook *mnb, GtkNotebook *notebook, GtkWidget *page, gint x, gint y, GeditWindow *window) { GeditWindow *new_window; GtkWidget *new_notebook; new_window = clone_window (window); gtk_window_move (GTK_WINDOW (new_window), x, y); gtk_widget_show (GTK_WIDGET (new_window)); new_notebook = _gedit_window_get_notebook (GEDIT_WINDOW (new_window)); return GTK_NOTEBOOK (new_notebook); } static void on_tab_close_request (GeditMultiNotebook *multi, GeditNotebook *notebook, GeditTab *tab, GtkWindow *window) { /* Note: we are destroying the tab before the default handler * seems to be ok, but we need to keep an eye on this. */ _gedit_cmd_file_close_tab (tab, GEDIT_WINDOW (window)); } static void on_show_popup_menu (GeditMultiNotebook *multi, GdkEventButton *event, GeditTab *tab, GeditWindow *window) { GtkWidget *menu; if (event == NULL) { return; } menu = gedit_notebook_popup_menu_new (window, tab); g_signal_connect (menu, "selection-done", G_CALLBACK (gtk_widget_destroy), NULL); gtk_widget_show (menu); gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *)event); } static void on_notebook_changed (GeditMultiNotebook *mnb, GParamSpec *pspec, GeditWindow *window) { update_actions_sensitivity (window); } static void on_notebook_removed (GeditMultiNotebook *mnb, GeditNotebook *notebook, GeditWindow *window) { update_actions_sensitivity (window); } static void on_fullscreen_toggle_button_toggled (GtkToggleButton *fullscreen_toggle_button, GeditWindow *window) { update_fullscreen_revealer_state (window); } static void side_panel_size_allocate (GtkWidget *widget, GtkAllocation *allocation, GeditWindow *window) { window->priv->side_panel_size = allocation->width; } static void bottom_panel_size_allocate (GtkWidget *widget, GtkAllocation *allocation, GeditWindow *window) { window->priv->bottom_panel_size = allocation->height; } static void hpaned_restore_position (GtkWidget *widget, GeditWindow *window) { gint pos; gedit_debug_message (DEBUG_WINDOW, "Restoring hpaned position: side panel size %d", window->priv->side_panel_size); pos = MAX (100, window->priv->side_panel_size); gtk_paned_set_position (GTK_PANED (window->priv->hpaned), pos); /* start monitoring the size */ g_signal_connect (window->priv->side_panel, "size-allocate", G_CALLBACK (side_panel_size_allocate), window); /* run this only once */ g_signal_handlers_disconnect_by_func (widget, hpaned_restore_position, window); } static void vpaned_restore_position (GtkWidget *widget, GeditWindow *window) { gint pos; GtkAllocation allocation; gedit_debug_message (DEBUG_WINDOW, "Restoring vpaned position: bottom panel size %d", window->priv->bottom_panel_size); gtk_widget_get_allocation (widget, &allocation); pos = allocation.height - MAX (50, window->priv->bottom_panel_size); gtk_paned_set_position (GTK_PANED (window->priv->vpaned), pos); /* start monitoring the size */ g_signal_connect (window->priv->bottom_panel, "size-allocate", G_CALLBACK (bottom_panel_size_allocate), window); /* run this only once */ g_signal_handlers_disconnect_by_func (widget, vpaned_restore_position, window); } static void side_panel_visibility_changed (GtkWidget *panel, GParamSpec *pspec, GeditWindow *window) { gboolean visible; GAction *action; gchar *layout_desc; visible = gtk_widget_get_visible (panel); g_settings_set_boolean (window->priv->ui_settings, GEDIT_SETTINGS_SIDE_PANEL_VISIBLE, visible); /* sync the action state if the panel visibility was changed programmatically */ action = g_action_map_lookup_action (G_ACTION_MAP (window), "side-panel"); g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible)); /* focus the right widget and set the right styles */ if (visible) { gtk_widget_grab_focus (window->priv->side_panel); } else { gtk_widget_grab_focus (GTK_WIDGET (window->priv->multi_notebook)); } g_object_get (gtk_settings_get_default (), "gtk-decoration-layout", &layout_desc, NULL); if (visible) { gchar **tokens; tokens = g_strsplit (layout_desc, ":", 2); if (tokens) { gchar *layout_headerbar; layout_headerbar = g_strdup_printf ("%c%s", ':', tokens[1]); gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (window->priv->headerbar), layout_headerbar); gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (window->priv->side_headerbar), tokens[0]); g_free (layout_headerbar); g_strfreev (tokens); } } else { gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (window->priv->headerbar), layout_desc); gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (window->priv->side_headerbar), NULL); } g_free (layout_desc); } static void on_side_panel_stack_children_number_changed (GtkStack *stack, GtkWidget *widget, GeditWindow *window) { GeditWindowPrivate *priv = window->priv; GList *children; children = gtk_container_get_children (GTK_CONTAINER (priv->side_panel)); if (children != NULL && children->next != NULL) { gtk_widget_show (priv->side_stack_switcher); #ifndef OS_OSX gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->side_headerbar), priv->side_stack_switcher); #endif } else { /* side_stack_switcher can get NULL in dispose, before stack children are being removed */ if (priv->side_stack_switcher != NULL) { gtk_widget_hide (priv->side_stack_switcher); } #ifndef OS_OSX gtk_header_bar_set_custom_title (GTK_HEADER_BAR (priv->side_headerbar), NULL); #endif } g_list_free (children); } static void setup_side_panel (GeditWindow *window) { GeditWindowPrivate *priv = window->priv; GtkWidget *documents_panel; gedit_debug (DEBUG_WINDOW); g_signal_connect_after (priv->side_panel, "notify::visible", G_CALLBACK (side_panel_visibility_changed), window); #ifdef OS_OSX priv->side_stack_switcher = priv->side_panel_inline_stack_switcher; #else priv->side_stack_switcher = gedit_menu_stack_switcher_new (); #endif gtk_button_set_relief (GTK_BUTTON (priv->side_stack_switcher), GTK_RELIEF_NONE); g_object_ref_sink (priv->side_stack_switcher); gedit_utils_set_atk_name_description (priv->side_stack_switcher, _("Change side panel page"), NULL); gedit_menu_stack_switcher_set_stack (GEDIT_MENU_STACK_SWITCHER (priv->side_stack_switcher), GTK_STACK (priv->side_panel)); g_signal_connect (priv->side_panel, "add", G_CALLBACK (on_side_panel_stack_children_number_changed), window); g_signal_connect (priv->side_panel, "remove", G_CALLBACK (on_side_panel_stack_children_number_changed), window); documents_panel = gedit_documents_panel_new (window); gtk_widget_show_all (documents_panel); gtk_stack_add_titled (GTK_STACK (priv->side_panel), documents_panel, "GeditWindowDocumentsPanel", _("Documents")); } static void bottom_panel_visibility_changed (GtkWidget *panel_box, GParamSpec *pspec, GeditWindow *window) { gboolean visible; GAction *action; visible = gtk_widget_get_visible (panel_box); g_settings_set_boolean (window->priv->ui_settings, GEDIT_SETTINGS_BOTTOM_PANEL_VISIBLE, visible); /* sync the action state if the panel visibility was changed programmatically */ action = g_action_map_lookup_action (G_ACTION_MAP (window), "bottom-panel"); g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible)); /* focus the right widget */ if (visible) { gtk_widget_grab_focus (window->priv->side_panel); } else { gtk_widget_grab_focus (GTK_WIDGET (window->priv->multi_notebook)); } } static void bottom_panel_item_removed (GtkStack *panel, GtkWidget *item, GeditWindow *window) { gtk_widget_set_visible (window->priv->bottom_panel, gtk_stack_get_visible_child (panel) != NULL); update_actions_sensitivity (window); } static void bottom_panel_item_added (GtkStack *panel, GtkWidget *item, GeditWindow *window) { GList *children; int n_children; children = gtk_container_get_children (GTK_CONTAINER (panel)); n_children = g_list_length (children); g_list_free (children); /* First item added. */ if (n_children == 1) { gboolean show; show = g_settings_get_boolean (window->priv->ui_settings, "bottom-panel-visible"); if (show) { gtk_widget_show (window->priv->bottom_panel); } update_actions_sensitivity (window); } } static void setup_bottom_panel (GeditWindow *window) { gedit_debug (DEBUG_WINDOW); g_signal_connect_after (window->priv->bottom_panel, "notify::visible", G_CALLBACK (bottom_panel_visibility_changed), window); } static void init_panels_visibility (GeditWindow *window) { gchar *panel_page; GtkWidget *panel_child; gboolean side_panel_visible; gboolean bottom_panel_visible; gedit_debug (DEBUG_WINDOW); /* side panel */ panel_page = g_settings_get_string (window->priv->window_settings, GEDIT_SETTINGS_SIDE_PANEL_ACTIVE_PAGE); panel_child = gtk_stack_get_child_by_name (GTK_STACK (window->priv->side_panel), panel_page); if (panel_child != NULL) { gtk_stack_set_visible_child (GTK_STACK (window->priv->side_panel), panel_child); } g_free (panel_page); side_panel_visible = g_settings_get_boolean (window->priv->ui_settings, GEDIT_SETTINGS_SIDE_PANEL_VISIBLE); bottom_panel_visible = g_settings_get_boolean (window->priv->ui_settings, GEDIT_SETTINGS_BOTTOM_PANEL_VISIBLE); if (side_panel_visible) { gtk_widget_show (window->priv->side_panel); } /* bottom pane, it can be empty */ if (gtk_stack_get_visible_child (GTK_STACK (window->priv->bottom_panel)) != NULL) { panel_page = g_settings_get_string (window->priv->window_settings, GEDIT_SETTINGS_BOTTOM_PANEL_ACTIVE_PAGE); panel_child = gtk_stack_get_child_by_name (GTK_STACK (window->priv->side_panel), panel_page); if (panel_child) { gtk_stack_set_visible_child (GTK_STACK (window->priv->bottom_panel), panel_child); } if (bottom_panel_visible) { gtk_widget_show (window->priv->bottom_panel); } g_free (panel_page); } /* start track sensitivity after the initial state is set */ window->priv->bottom_panel_item_removed_handler_id = g_signal_connect (window->priv->bottom_panel, "remove", G_CALLBACK (bottom_panel_item_removed), window); g_signal_connect_after (window->priv->bottom_panel, "add", G_CALLBACK (bottom_panel_item_added), window); } static void clipboard_owner_change (GtkClipboard *clipboard, GdkEventOwnerChange *event, GeditWindow *window) { set_paste_sensitivity_according_to_clipboard (window, clipboard); } static void window_realized (GtkWidget *window, gpointer *data) { GtkClipboard *clipboard; clipboard = gtk_widget_get_clipboard (window, GDK_SELECTION_CLIPBOARD); g_signal_connect (clipboard, "owner_change", G_CALLBACK (clipboard_owner_change), window); } static void window_unrealized (GtkWidget *window, gpointer *data) { GtkClipboard *clipboard; clipboard = gtk_widget_get_clipboard (window, GDK_SELECTION_CLIPBOARD); g_signal_handlers_disconnect_by_func (clipboard, G_CALLBACK (clipboard_owner_change), window); } static void extension_added (PeasExtensionSet *extensions, PeasPluginInfo *info, PeasExtension *exten, GeditWindow *window) { gedit_window_activatable_activate (GEDIT_WINDOW_ACTIVATABLE (exten)); } static void extension_removed (PeasExtensionSet *extensions, PeasPluginInfo *info, PeasExtension *exten, GeditWindow *window) { gedit_window_activatable_deactivate (GEDIT_WINDOW_ACTIVATABLE (exten)); } static GActionEntry win_entries[] = { { "new-tab", _gedit_cmd_file_new }, { "open", _gedit_cmd_file_open }, { "revert", _gedit_cmd_file_revert }, { "reopen-closed-tab", _gedit_cmd_file_reopen_closed_tab }, { "save", _gedit_cmd_file_save }, { "save-as", _gedit_cmd_file_save_as }, { "save-all", _gedit_cmd_file_save_all }, { "close", _gedit_cmd_file_close }, { "close-all", _gedit_cmd_file_close_all }, { "print", _gedit_cmd_file_print }, { "focus-active-view", NULL, NULL, "false", _gedit_cmd_view_focus_active }, { "side-panel", NULL, NULL, "false", _gedit_cmd_view_toggle_side_panel }, { "bottom-panel", NULL, NULL, "false", _gedit_cmd_view_toggle_bottom_panel }, { "fullscreen", NULL, NULL, "false", _gedit_cmd_view_toggle_fullscreen_mode }, { "leave-fullscreen", _gedit_cmd_view_leave_fullscreen_mode }, { "find", _gedit_cmd_search_find }, { "find-next", _gedit_cmd_search_find_next }, { "find-prev", _gedit_cmd_search_find_prev }, { "replace", _gedit_cmd_search_replace }, { "clear-highlight", _gedit_cmd_search_clear_highlight }, { "goto-line", _gedit_cmd_search_goto_line }, { "new-tab-group", _gedit_cmd_documents_new_tab_group }, { "previous-tab-group", _gedit_cmd_documents_previous_tab_group }, { "next-tab-group", _gedit_cmd_documents_next_tab_group }, { "previous-document", _gedit_cmd_documents_previous_document }, { "next-document", _gedit_cmd_documents_next_document }, { "move-to-new-window", _gedit_cmd_documents_move_to_new_window }, { "undo", _gedit_cmd_edit_undo }, { "redo", _gedit_cmd_edit_redo }, { "cut", _gedit_cmd_edit_cut }, { "copy", _gedit_cmd_edit_copy }, { "paste", _gedit_cmd_edit_paste }, { "delete", _gedit_cmd_edit_delete }, { "select-all", _gedit_cmd_edit_select_all }, { "highlight-mode", _gedit_cmd_view_highlight_mode }, { "overwrite-mode", NULL, NULL, "false", _gedit_cmd_edit_overwrite_mode } }; static void sync_fullscreen_actions (GeditWindow *window, gboolean fullscreen) { GtkMenuButton *button; GPropertyAction *action; button = fullscreen ? window->priv->fullscreen_gear_button : window->priv->gear_button; g_action_map_remove_action (G_ACTION_MAP (window), "hamburger-menu"); action = g_property_action_new ("hamburger-menu", button, "active"); g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (action)); g_object_unref (action); } static void init_amtk_application_window (GeditWindow *gedit_window) { AmtkApplicationWindow *amtk_window; amtk_window = amtk_application_window_get_from_gtk_application_window (GTK_APPLICATION_WINDOW (gedit_window)); amtk_application_window_set_statusbar (amtk_window, GTK_STATUSBAR (gedit_window->priv->statusbar)); } static void open_recent_menu_item_activated_cb (GtkRecentChooser *recent_chooser, gpointer user_data) { GeditWindow *window = GEDIT_WINDOW (user_data); gchar *uri; GFile *location; uri = gtk_recent_chooser_get_current_uri (recent_chooser); location = g_file_new_for_uri (uri); gedit_commands_load_location (window, location, NULL, 0, 0); g_free (uri); g_object_unref (location); } static GtkWidget * create_open_buttons (GeditWindow *window, GtkMenuButton **open_recent_button) { GtkWidget *hbox; GtkStyleContext *style_context; GtkWidget *open_dialog_button; GtkWidget *my_open_recent_button; AmtkApplicationWindow *amtk_window; GtkRecentChooserMenu *recent_menu; /* It currently needs to be a GtkBox, not a GtkGrid, because GtkGrid and * GTK_STYLE_CLASS_LINKED doesn't work as expected in a RTL locale. * Probably a GtkGrid bug. */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); style_context = gtk_widget_get_style_context (hbox); gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LINKED); open_dialog_button = gtk_button_new_with_mnemonic (_("_Open")); gtk_widget_set_tooltip_text (open_dialog_button, _("Open a file")); gtk_actionable_set_action_name (GTK_ACTIONABLE (open_dialog_button), "win.open"); my_open_recent_button = gtk_menu_button_new (); gtk_widget_set_tooltip_text (my_open_recent_button, _("Open a recently used file")); recent_menu = amtk_application_window_create_open_recent_menu_base (); amtk_window = amtk_application_window_get_from_gtk_application_window (GTK_APPLICATION_WINDOW (window)); amtk_application_window_connect_recent_chooser_menu_to_statusbar (amtk_window, recent_menu); g_signal_connect_object (recent_menu, "item-activated", G_CALLBACK (open_recent_menu_item_activated_cb), window, 0); gtk_menu_button_set_popup (GTK_MENU_BUTTON (my_open_recent_button), GTK_WIDGET (recent_menu)); gtk_container_add (GTK_CONTAINER (hbox), open_dialog_button); gtk_container_add (GTK_CONTAINER (hbox), my_open_recent_button); gtk_widget_show_all (hbox); if (open_recent_button != NULL) { *open_recent_button = GTK_MENU_BUTTON (my_open_recent_button); } return hbox; } static void init_open_buttons (GeditWindow *window) { gtk_container_add_with_properties (GTK_CONTAINER (window->priv->headerbar), create_open_buttons (window, NULL), "position", 0, /* The first on the left. */ NULL); gtk_container_add_with_properties (GTK_CONTAINER (window->priv->fullscreen_headerbar), create_open_buttons (window, &(window->priv->fullscreen_open_recent_button)), "position", 0, /* The first on the left. */ NULL); g_signal_connect (GTK_TOGGLE_BUTTON (window->priv->fullscreen_open_recent_button), "toggled", G_CALLBACK (on_fullscreen_toggle_button_toggled), window); } static void gedit_window_init (GeditWindow *window) { GtkTargetList *tl; GMenuModel *hamburger_menu; gedit_debug (DEBUG_WINDOW); window->priv = gedit_window_get_instance_private (window); window->priv->removing_tabs = FALSE; window->priv->state = GEDIT_WINDOW_STATE_NORMAL; window->priv->inhibition_cookie = 0; window->priv->dispose_has_run = FALSE; window->priv->direct_save_uri = NULL; window->priv->closed_docs_stack = NULL; window->priv->editor_settings = g_settings_new ("org.gnome.gedit.preferences.editor"); window->priv->ui_settings = g_settings_new ("org.gnome.gedit.preferences.ui"); /* window settings are applied only once the window is closed. We do not want to keep writing to disk when the window is dragged around */ window->priv->window_settings = g_settings_new ("org.gnome.gedit.state.window"); g_settings_delay (window->priv->window_settings); window->priv->message_bus = gedit_message_bus_new (); gtk_widget_init_template (GTK_WIDGET (window)); init_amtk_application_window (window); init_open_buttons (window); g_action_map_add_action_entries (G_ACTION_MAP (window), win_entries, G_N_ELEMENTS (win_entries), window); window->priv->window_group = gtk_window_group_new (); gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window)); setup_fullscreen_eventbox (window); sync_fullscreen_actions (window, FALSE); hamburger_menu = _gedit_app_get_hamburger_menu (GEDIT_APP (g_application_get_default ())); if (hamburger_menu) { gtk_menu_button_set_menu_model (window->priv->gear_button, hamburger_menu); gtk_menu_button_set_menu_model (window->priv->fullscreen_gear_button, hamburger_menu); } else { gtk_widget_hide (GTK_WIDGET (window->priv->gear_button)); gtk_widget_hide (GTK_WIDGET (window->priv->fullscreen_gear_button)); gtk_widget_set_no_show_all (GTK_WIDGET (window->priv->gear_button), TRUE); gtk_widget_set_no_show_all (GTK_WIDGET (window->priv->fullscreen_gear_button), TRUE); } g_signal_connect (GTK_TOGGLE_BUTTON (window->priv->fullscreen_gear_button), "toggled", G_CALLBACK (on_fullscreen_toggle_button_toggled), window); /* Setup status bar */ setup_statusbar (window); /* Setup main area */ g_signal_connect (window->priv->multi_notebook, "notebook-removed", G_CALLBACK (on_notebook_removed), window); g_signal_connect (window->priv->multi_notebook, "notify::active-notebook", G_CALLBACK (on_notebook_changed), window); g_signal_connect (window->priv->multi_notebook, "tab-added", G_CALLBACK (on_tab_added), window); g_signal_connect (window->priv->multi_notebook, "tab-removed", G_CALLBACK (on_tab_removed), window); g_signal_connect (window->priv->multi_notebook, "switch-tab", G_CALLBACK (tab_switched), window); g_signal_connect (window->priv->multi_notebook, "tab-close-request", G_CALLBACK (on_tab_close_request), window); g_signal_connect (window->priv->multi_notebook, "page-reordered", G_CALLBACK (on_page_reordered), window); g_signal_connect (window->priv->multi_notebook, "create-window", G_CALLBACK (on_notebook_create_window), window); g_signal_connect (window->priv->multi_notebook, "show-popup-menu", G_CALLBACK (on_show_popup_menu), window); /* side and bottom panels */ setup_side_panel (window); setup_bottom_panel (window); /* panels' state must be restored after panels have been mapped, * since the bottom panel position depends on the size of the vpaned. */ window->priv->side_panel_size = g_settings_get_int (window->priv->window_settings, GEDIT_SETTINGS_SIDE_PANEL_SIZE); window->priv->bottom_panel_size = g_settings_get_int (window->priv->window_settings, GEDIT_SETTINGS_BOTTOM_PANEL_SIZE); g_signal_connect_after (window->priv->hpaned, "map", G_CALLBACK (hpaned_restore_position), window); g_signal_connect_after (window->priv->vpaned, "map", G_CALLBACK (vpaned_restore_position), window); /* Drag and drop support */ gtk_drag_dest_set (GTK_WIDGET (window), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, drop_types, G_N_ELEMENTS (drop_types), GDK_ACTION_COPY); /* Add uri targets */ tl = gtk_drag_dest_get_target_list (GTK_WIDGET (window)); if (tl == NULL) { tl = gtk_target_list_new (drop_types, G_N_ELEMENTS (drop_types)); gtk_drag_dest_set_target_list (GTK_WIDGET (window), tl); gtk_target_list_unref (tl); } gtk_target_list_add_uri_targets (tl, TARGET_URI_LIST); /* connect instead of override, so that we can * share the cb code with the view */ g_signal_connect (window, "drag_data_received", G_CALLBACK (drag_data_received_cb), NULL); g_signal_connect (window, "drag_drop", G_CALLBACK (drag_drop_cb), NULL); /* we can get the clipboard only after the widget * is realized */ g_signal_connect (window, "realize", G_CALLBACK (window_realized), NULL); g_signal_connect (window, "unrealize", G_CALLBACK (window_unrealized), NULL); gedit_debug_message (DEBUG_WINDOW, "Update plugins ui"); window->priv->extensions = peas_extension_set_new (PEAS_ENGINE (gedit_plugins_engine_get_default ()), GEDIT_TYPE_WINDOW_ACTIVATABLE, "window", window, NULL); g_signal_connect (window->priv->extensions, "extension-added", G_CALLBACK (extension_added), window); g_signal_connect (window->priv->extensions, "extension-removed", G_CALLBACK (extension_removed), window); peas_extension_set_foreach (window->priv->extensions, (PeasExtensionSetForeachFunc) extension_added, window); /* set visibility of panels. * This needs to be done after plugins activatation */ init_panels_visibility (window); update_actions_sensitivity (window); gedit_debug_message (DEBUG_WINDOW, "END"); } /** * gedit_window_get_active_view: * @window: a #GeditWindow * * Gets the active #GeditView. * * Returns: (transfer none): the active #GeditView */ GeditView * gedit_window_get_active_view (GeditWindow *window) { GeditTab *tab; GeditView *view; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); tab = gedit_window_get_active_tab (window); if (tab == NULL) return NULL; view = gedit_tab_get_view (tab); return view; } /** * gedit_window_get_active_document: * @window: a #GeditWindow * * Gets the active #GeditDocument. * * Returns: (transfer none): the active #GeditDocument */ GeditDocument * gedit_window_get_active_document (GeditWindow *window) { GeditView *view; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); view = gedit_window_get_active_view (window); if (view == NULL) return NULL; return GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); } GtkWidget * _gedit_window_get_multi_notebook (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return GTK_WIDGET (window->priv->multi_notebook); } GtkWidget * _gedit_window_get_notebook (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return GTK_WIDGET (gedit_multi_notebook_get_active_notebook (window->priv->multi_notebook)); } static GeditTab * process_create_tab (GeditWindow *window, GtkWidget *notebook, GeditTab *tab, gboolean jump_to) { if (tab == NULL) { return NULL; } gedit_debug (DEBUG_WINDOW); gtk_widget_show (GTK_WIDGET (tab)); gedit_notebook_add_tab (GEDIT_NOTEBOOK (notebook), tab, -1, jump_to); if (!gtk_widget_get_visible (GTK_WIDGET (window))) { gtk_window_present (GTK_WINDOW (window)); } return tab; } /** * gedit_window_create_tab: * @window: a #GeditWindow * @jump_to: %TRUE to set the new #GeditTab as active * * Creates a new #GeditTab and adds the new tab to the #GtkNotebook. * In case @jump_to is %TRUE the #GtkNotebook switches to that new #GeditTab. * * Returns: (transfer none): a new #GeditTab */ GeditTab * gedit_window_create_tab (GeditWindow *window, gboolean jump_to) { GtkWidget *notebook; GeditTab *tab; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); gedit_debug (DEBUG_WINDOW); notebook = _gedit_window_get_notebook (window); tab = _gedit_tab_new (); gtk_widget_show (GTK_WIDGET (tab)); return process_create_tab (window, notebook, tab, jump_to); } /** * gedit_window_create_tab_from_location: * @window: a #GeditWindow * @location: the location of the document * @encoding: (allow-none): a #GtkSourceEncoding, or %NULL * @line_pos: the line position to visualize * @column_pos: the column position to visualize * @create: %TRUE to create a new document in case @uri does exist * @jump_to: %TRUE to set the new #GeditTab as active * * Creates a new #GeditTab loading the document specified by @uri. * In case @jump_to is %TRUE the #GtkNotebook swithes to that new #GeditTab. * Whether @create is %TRUE, creates a new empty document if location does * not refer to an existing file * * Returns: (transfer none): a new #GeditTab */ GeditTab * gedit_window_create_tab_from_location (GeditWindow *window, GFile *location, const GtkSourceEncoding *encoding, gint line_pos, gint column_pos, gboolean create, gboolean jump_to) { GtkWidget *notebook; GeditTab *tab; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); g_return_val_if_fail (G_IS_FILE (location), NULL); gedit_debug (DEBUG_WINDOW); tab = _gedit_tab_new (); _gedit_tab_load (tab, location, encoding, line_pos, column_pos, create); notebook = _gedit_window_get_notebook (window); return process_create_tab (window, notebook, tab, jump_to); } /** * gedit_window_create_tab_from_stream: * @window: a #GeditWindow * @stream: a #GInputStream * @encoding: (allow-none): a #GtkSourceEncoding, or %NULL * @line_pos: the line position to visualize * @column_pos: the column position to visualize * @jump_to: %TRUE to set the new #GeditTab as active * * Returns: (transfer none): a new #GeditTab */ GeditTab * gedit_window_create_tab_from_stream (GeditWindow *window, GInputStream *stream, const GtkSourceEncoding *encoding, gint line_pos, gint column_pos, gboolean jump_to) { GtkWidget *notebook; GeditTab *tab; gedit_debug (DEBUG_WINDOW); g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL); tab = _gedit_tab_new (); _gedit_tab_load_stream (tab, stream, encoding, line_pos, column_pos); notebook = _gedit_window_get_notebook (window); return process_create_tab (window, notebook, tab, jump_to); } /** * gedit_window_get_active_tab: * @window: a GeditWindow * * Gets the active #GeditTab in the @window. * * Returns: (transfer none): the active #GeditTab in the @window. */ GeditTab * gedit_window_get_active_tab (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return (window->priv->multi_notebook == NULL) ? NULL : gedit_multi_notebook_get_active_tab (window->priv->multi_notebook); } static void add_document (GeditTab *tab, GList **res) { GeditDocument *doc; doc = gedit_tab_get_document (tab); *res = g_list_prepend (*res, doc); } /** * gedit_window_get_documents: * @window: a #GeditWindow * * Gets a newly allocated list with all the documents in the window. * This list must be freed. * * Returns: (element-type Gedit.Document) (transfer container): a newly * allocated list with all the documents in the window */ GList * gedit_window_get_documents (GeditWindow *window) { GList *res = NULL; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); gedit_multi_notebook_foreach_tab (window->priv->multi_notebook, (GtkCallback)add_document, &res); res = g_list_reverse (res); return res; } static void add_view (GeditTab *tab, GList **res) { GeditView *view; view = gedit_tab_get_view (tab); *res = g_list_prepend (*res, view); } /** * gedit_window_get_views: * @window: a #GeditWindow * * Gets a list with all the views in the window. This list must be freed. * * Returns: (element-type Gedit.View) (transfer container): a newly allocated * list with all the views in the window */ GList * gedit_window_get_views (GeditWindow *window) { GList *res = NULL; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); gedit_multi_notebook_foreach_tab (window->priv->multi_notebook, (GtkCallback)add_view, &res); res = g_list_reverse (res); return res; } /** * gedit_window_close_tab: * @window: a #GeditWindow * @tab: the #GeditTab to close * * Closes the @tab. */ void gedit_window_close_tab (GeditWindow *window, GeditTab *tab) { GList *tabs = NULL; g_return_if_fail (GEDIT_IS_WINDOW (window)); g_return_if_fail (GEDIT_IS_TAB (tab)); g_return_if_fail ((gedit_tab_get_state (tab) != GEDIT_TAB_STATE_SAVING) && (gedit_tab_get_state (tab) != GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)); tabs = g_list_append (tabs, tab); gedit_multi_notebook_close_tabs (window->priv->multi_notebook, tabs); g_list_free (tabs); } /** * gedit_window_close_all_tabs: * @window: a #GeditWindow * * Closes all opened tabs. */ void gedit_window_close_all_tabs (GeditWindow *window) { g_return_if_fail (GEDIT_IS_WINDOW (window)); g_return_if_fail (!(window->priv->state & GEDIT_WINDOW_STATE_SAVING)); window->priv->removing_tabs = TRUE; gedit_multi_notebook_close_all_tabs (window->priv->multi_notebook); window->priv->removing_tabs = FALSE; } /** * gedit_window_close_tabs: * @window: a #GeditWindow * @tabs: (element-type Gedit.Tab): a list of #GeditTab * * Closes all tabs specified by @tabs. */ void gedit_window_close_tabs (GeditWindow *window, const GList *tabs) { g_return_if_fail (GEDIT_IS_WINDOW (window)); g_return_if_fail (!(window->priv->state & GEDIT_WINDOW_STATE_SAVING)); window->priv->removing_tabs = TRUE; gedit_multi_notebook_close_tabs (window->priv->multi_notebook, tabs); window->priv->removing_tabs = FALSE; } GeditWindow * _gedit_window_move_tab_to_new_window (GeditWindow *window, GeditTab *tab) { GeditWindow *new_window; GeditNotebook *old_notebook; GeditNotebook *new_notebook; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); g_return_val_if_fail (GEDIT_IS_TAB (tab), NULL); g_return_val_if_fail (gedit_multi_notebook_get_n_notebooks ( window->priv->multi_notebook) > 1 || gedit_multi_notebook_get_n_tabs ( window->priv->multi_notebook) > 1, NULL); new_window = clone_window (window); old_notebook = GEDIT_NOTEBOOK (gtk_widget_get_parent (GTK_WIDGET (tab))); new_notebook = gedit_multi_notebook_get_active_notebook (new_window->priv->multi_notebook); gedit_notebook_move_tab (old_notebook, new_notebook, tab, -1); gtk_widget_show (GTK_WIDGET (new_window)); return new_window; } void _gedit_window_move_tab_to_new_tab_group (GeditWindow *window, GeditTab *tab) { g_return_if_fail (GEDIT_IS_WINDOW (window)); g_return_if_fail (GEDIT_IS_TAB (tab)); gedit_multi_notebook_add_new_notebook_with_tab (window->priv->multi_notebook, tab); } /** * gedit_window_set_active_tab: * @window: a #GeditWindow * @tab: a #GeditTab * * Switches to the tab that matches with @tab. */ void gedit_window_set_active_tab (GeditWindow *window, GeditTab *tab) { g_return_if_fail (GEDIT_IS_WINDOW (window)); gedit_multi_notebook_set_active_tab (window->priv->multi_notebook, tab); } /** * gedit_window_get_group: * @window: a #GeditWindow * * Gets the #GtkWindowGroup in which @window resides. * * Returns: (transfer none): the #GtkWindowGroup */ GtkWindowGroup * gedit_window_get_group (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return window->priv->window_group; } gboolean _gedit_window_is_removing_tabs (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); return window->priv->removing_tabs; } /** * gedit_window_get_side_panel: * @window: a #GeditWindow * * Gets the side panel of the @window. * * Returns: (transfer none): the side panel's #GtkStack. */ GtkWidget * gedit_window_get_side_panel (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return window->priv->side_panel; } /** * gedit_window_get_bottom_panel: * @window: a #GeditWindow * * Gets the bottom panel of the @window. * * Returns: (transfer none): the bottom panel's #GtkStack. */ GtkWidget * gedit_window_get_bottom_panel (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return window->priv->bottom_panel; } /** * gedit_window_get_statusbar: * @window: a #GeditWindow * * Gets the #GeditStatusbar of the @window. * * Returns: (transfer none): the #GeditStatusbar of the @window. */ GtkWidget * gedit_window_get_statusbar (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return window->priv->statusbar; } /** * gedit_window_get_state: * @window: a #GeditWindow * * Retrieves the state of the @window. * * Returns: the current #GeditWindowState of the @window. */ GeditWindowState gedit_window_get_state (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), GEDIT_WINDOW_STATE_NORMAL); return window->priv->state; } const gchar * _gedit_window_get_file_chooser_folder_uri (GeditWindow *window, GtkFileChooserAction action) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); g_return_val_if_fail ((action == GTK_FILE_CHOOSER_ACTION_OPEN) || (action == GTK_FILE_CHOOSER_ACTION_SAVE), NULL); if (action == GTK_FILE_CHOOSER_ACTION_OPEN) { GeditSettings *settings; GSettings *file_chooser_state_settings; settings = _gedit_settings_get_singleton (); file_chooser_state_settings = _gedit_settings_peek_file_chooser_state_settings (settings); if (g_settings_get_boolean (file_chooser_state_settings, GEDIT_SETTINGS_FILE_CHOOSER_OPEN_RECENT)) { return NULL; } } return window->priv->file_chooser_folder_uri; } void _gedit_window_set_file_chooser_folder_uri (GeditWindow *window, GtkFileChooserAction action, const gchar *folder_uri) { g_return_if_fail (GEDIT_IS_WINDOW (window)); g_return_if_fail ((action == GTK_FILE_CHOOSER_ACTION_OPEN) || (action == GTK_FILE_CHOOSER_ACTION_SAVE)); if (action == GTK_FILE_CHOOSER_ACTION_OPEN) { GeditSettings *settings; GSettings *file_chooser_state_settings; gboolean open_recent = folder_uri == NULL; settings = _gedit_settings_get_singleton (); file_chooser_state_settings = _gedit_settings_peek_file_chooser_state_settings (settings); g_settings_set_boolean (file_chooser_state_settings, GEDIT_SETTINGS_FILE_CHOOSER_OPEN_RECENT, open_recent); if (open_recent) { /* Do not set window->priv->file_chooser_folder_uri to * NULL, to not lose the folder for the Save action. */ return; } } g_free (window->priv->file_chooser_folder_uri); window->priv->file_chooser_folder_uri = g_strdup (folder_uri); } static void add_unsaved_doc (GeditTab *tab, GList **res) { if (!_gedit_tab_get_can_close (tab)) { GeditDocument *doc; doc = gedit_tab_get_document (tab); *res = g_list_prepend (*res, doc); } } /** * gedit_window_get_unsaved_documents: * @window: a #GeditWindow * * Gets the list of documents that need to be saved before closing the window. * * Returns: (element-type Gedit.Document) (transfer container): a list of * #GeditDocument that need to be saved before closing the window */ GList * gedit_window_get_unsaved_documents (GeditWindow *window) { GList *res = NULL; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); gedit_multi_notebook_foreach_tab (window->priv->multi_notebook, (GtkCallback)add_unsaved_doc, &res); return g_list_reverse (res); } GList * _gedit_window_get_all_tabs (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return gedit_multi_notebook_get_all_tabs (window->priv->multi_notebook); } void _gedit_window_fullscreen (GeditWindow *window) { g_return_if_fail (GEDIT_IS_WINDOW (window)); if (_gedit_window_is_fullscreen (window)) return; sync_fullscreen_actions (window, TRUE); /* Go to fullscreen mode and hide bars */ gtk_window_fullscreen (GTK_WINDOW (&window->window)); } void _gedit_window_unfullscreen (GeditWindow *window) { g_return_if_fail (GEDIT_IS_WINDOW (window)); if (!_gedit_window_is_fullscreen (window)) return; sync_fullscreen_actions (window, FALSE); /* Unfullscreen and show bars */ gtk_window_unfullscreen (GTK_WINDOW (&window->window)); } gboolean _gedit_window_is_fullscreen (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), FALSE); return window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN; } /** * gedit_window_get_tab_from_location: * @window: a #GeditWindow * @location: a #GFile * * Gets the #GeditTab that matches with the given @location. * * Returns: (transfer none): the #GeditTab that matches with the given @location. */ GeditTab * gedit_window_get_tab_from_location (GeditWindow *window, GFile *location) { GList *tabs; GList *l; GeditTab *ret = NULL; g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); g_return_val_if_fail (G_IS_FILE (location), NULL); tabs = gedit_multi_notebook_get_all_tabs (window->priv->multi_notebook); for (l = tabs; l != NULL; l = g_list_next (l)) { GeditDocument *doc; GtkSourceFile *file; GeditTab *tab; GFile *cur_location; tab = GEDIT_TAB (l->data); doc = gedit_tab_get_document (tab); file = gedit_document_get_file (doc); cur_location = gtk_source_file_get_location (file); if (cur_location != NULL) { gboolean found = g_file_equal (location, cur_location); if (found) { ret = tab; break; } } } g_list_free (tabs); return ret; } /** * gedit_window_get_message_bus: * @window: a #GeditWindow * * Gets the #GeditMessageBus associated with @window. The returned reference * is owned by the window and should not be unreffed. * * Return value: (transfer none): the #GeditMessageBus associated with @window */ GeditMessageBus * gedit_window_get_message_bus (GeditWindow *window) { g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); return window->priv->message_bus; } /* ex:set ts=8 noet: */