diff options
Diffstat (limited to 'gedit/gedit-window.c')
-rwxr-xr-x | gedit/gedit-window.c | 4798 |
1 files changed, 4798 insertions, 0 deletions
diff --git a/gedit/gedit-window.c b/gedit/gedit-window.c new file mode 100755 index 00000000..c4bf2403 --- /dev/null +++ b/gedit/gedit-window.c @@ -0,0 +1,4798 @@ +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gedit Team. + * See the ChangeLog files for a list of changes. + * + * $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <time.h> +#include <sys/types.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "gedit-ui.h" +#include "gedit-window.h" +#include "gedit-window-private.h" +#include "gedit-app.h" +#include "gedit-notebook.h" +#include "gedit-statusbar.h" +#include "gedit-utils.h" +#include "gedit-commands.h" +#include "gedit-debug.h" +#include "gedit-language-manager.h" +#include "gedit-prefs-manager-app.h" +#include "gedit-panel.h" +#include "gedit-documents-panel.h" +#include "gedit-plugins-engine.h" +#include "gedit-enum-types.h" +#include "gedit-dirs.h" +#include "gedit-status-combo-box.h" + +#ifdef OS_OSX +#include "osx/gedit-osx.h" +#endif + +#define LANGUAGE_NONE (const gchar *)"LangNone" +#define GEDIT_UIFILE "gedit-ui.xml" +#define TAB_WIDTH_DATA "GeditWindowTabWidthData" +#define LANGUAGE_DATA "GeditWindowLanguageData" +#define FULLSCREEN_ANIMATION_SPEED 4 + +#define GEDIT_WINDOW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object),\ + GEDIT_TYPE_WINDOW, \ + GeditWindowPrivate)) + +/* Signals */ +enum +{ + TAB_ADDED, + TAB_REMOVED, + TABS_REORDERED, + ACTIVE_TAB_CHANGED, + ACTIVE_TAB_STATE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum +{ + PROP_0, + PROP_STATE +}; + +enum +{ + TARGET_URI_LIST = 100 +}; + +G_DEFINE_TYPE(GeditWindow, gedit_window, GTK_TYPE_WINDOW) + +static void recent_manager_changed (GtkRecentManager *manager, + 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_enum (value, + gedit_window_get_state (window)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +save_panes_state (GeditWindow *window) +{ + gint pane_page; + + gedit_debug (DEBUG_WINDOW); + + if (gedit_prefs_manager_window_size_can_set ()) + gedit_prefs_manager_set_window_size (window->priv->width, + window->priv->height); + + if (gedit_prefs_manager_window_state_can_set ()) + gedit_prefs_manager_set_window_state (window->priv->window_state); + + if ((window->priv->side_panel_size > 0) && + gedit_prefs_manager_side_panel_size_can_set ()) + gedit_prefs_manager_set_side_panel_size ( + window->priv->side_panel_size); + + pane_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (window->priv->side_panel)); + if (pane_page != 0 && + gedit_prefs_manager_side_panel_active_page_can_set ()) + gedit_prefs_manager_set_side_panel_active_page (pane_page); + + if ((window->priv->bottom_panel_size > 0) && + gedit_prefs_manager_bottom_panel_size_can_set ()) + gedit_prefs_manager_set_bottom_panel_size ( + window->priv->bottom_panel_size); + + pane_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (window->priv->bottom_panel)); + if (pane_page != 0 && + gedit_prefs_manager_bottom_panel_active_page_can_set ()) + gedit_prefs_manager_set_bottom_panel_active_page (pane_page); +} + +#ifdef OS_OSX +static GtkMenuItem * +ui_manager_menu_item (GtkUIManager *uimanager, + const gchar *path) +{ + return GTK_MENU_ITEM (gtk_ui_manager_get_widget (uimanager, path)); +} + +static void +add_mac_root_menu (GeditWindow *window) +{ + if (window->priv->mac_menu_group != NULL) + { + return; + } + + window->priv->mac_menu_group = ige_mac_menu_add_app_menu_group (); + + ige_mac_menu_add_app_menu_item (window->priv->mac_menu_group, + ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/HelpMenu/HelpAboutMenu"), + NULL); +} + +static void +remove_mac_root_menu (GeditWindow *window) +{ + if (window->priv->mac_menu_group == NULL) + { + return; + } + + ige_mac_menu_remove_app_menu_group (window->priv->mac_menu_group); + window->priv->mac_menu_group = NULL; +} + +static gboolean +gedit_window_focus_in_event (GtkWidget *widget, + GdkEventFocus *event) +{ + add_mac_root_menu (GEDIT_WINDOW (widget)); + return GTK_WIDGET_CLASS (gedit_window_parent_class)->focus_in_event (widget, event); +} + +static gboolean +gedit_window_focus_out_event (GtkWidget *widget, + GdkEventFocus *event) +{ + remove_mac_root_menu (GEDIT_WINDOW (widget)); + return GTK_WIDGET_CLASS (gedit_window_parent_class)->focus_out_event (widget, event); +} +#endif + +static void +gedit_window_dispose (GObject *object) +{ + GeditWindow *window; + + gedit_debug (DEBUG_WINDOW); + + window = GEDIT_WINDOW (object); + + /* Stop tracking removal of panes otherwise we always + * end up with thinking we had no pane 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. + */ + gedit_plugins_engine_garbage_collect (gedit_plugins_engine_get_default ()); + + /* save the panes position and make sure to deactivate plugins + * for this window, but only once */ + if (!window->priv->dispose_has_run) + { + save_panes_state (window); + + gedit_plugins_engine_deactivate_plugins (gedit_plugins_engine_get_default (), + window); + window->priv->dispose_has_run = TRUE; + } + + if (window->priv->fullscreen_animation_timeout_id != 0) + { + g_source_remove (window->priv->fullscreen_animation_timeout_id); + window->priv->fullscreen_animation_timeout_id = 0; + } + + if (window->priv->fullscreen_controls != NULL) + { + gtk_widget_destroy (window->priv->fullscreen_controls); + + window->priv->fullscreen_controls = NULL; + } + + if (window->priv->recents_handler_id != 0) + { + GtkRecentManager *recent_manager; + + recent_manager = gtk_recent_manager_get_default (); + g_signal_handler_disconnect (recent_manager, + window->priv->recents_handler_id); + window->priv->recents_handler_id = 0; + } + + if (window->priv->manager != NULL) + { + g_object_unref (window->priv->manager); + window->priv->manager = NULL; + } + + if (window->priv->message_bus != NULL) + { + g_object_unref (window->priv->message_bus); + window->priv->message_bus = NULL; + } + + if (window->priv->window_group != NULL) + { + g_object_unref (window->priv->window_group); + window->priv->window_group = NULL; + } + + /* Now that there have broken some reference loops, + * force collection again. + */ + gedit_plugins_engine_garbage_collect (gedit_plugins_engine_get_default ()); + +#ifdef OS_OSX + remove_mac_root_menu (window); +#endif + + G_OBJECT_CLASS (gedit_window_parent_class)->dispose (object); +} + +static void +gedit_window_finalize (GObject *object) +{ + GeditWindow *window; + + gedit_debug (DEBUG_WINDOW); + + window = GEDIT_WINDOW (object); + + if (window->priv->default_location != NULL) + g_object_unref (window->priv->default_location); + + G_OBJECT_CLASS (gedit_window_parent_class)->finalize (object); +} + +static gboolean +gedit_window_window_state_event (GtkWidget *widget, + GdkEventWindowState *event) +{ + GeditWindow *window = GEDIT_WINDOW (widget); + + window->priv->window_state = event->new_window_state; + + if (event->changed_mask & + (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) + { + gboolean show; + + show = !(event->new_window_state & + (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)); + + _gedit_statusbar_set_has_resize_grip (GEDIT_STATUSBAR (window->priv->statusbar), + show); + } + + return FALSE; +} + +static gboolean +gedit_window_configure_event (GtkWidget *widget, + GdkEventConfigure *event) +{ + GeditWindow *window = GEDIT_WINDOW (widget); + + window->priv->width = event->width; + window->priv->height = event->height; + + 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 */ + if (!handled) + handled = GTK_WIDGET_CLASS (grand_parent_class)->key_press_event (widget, event); + + return handled; +} + +static void +gedit_window_tab_removed (GeditWindow *window, + GeditTab *tab) +{ + gedit_plugins_engine_garbage_collect (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; + +#ifdef OS_OSX + widget_class->focus_in_event = gedit_window_focus_in_event; + widget_class->focus_out_event = gedit_window_focus_out_event; +#endif + + 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, + g_cclosure_marshal_VOID__OBJECT, + 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, + g_cclosure_marshal_VOID__OBJECT, + 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, + g_cclosure_marshal_VOID__VOID, + 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, + g_cclosure_marshal_VOID__OBJECT, + 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, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + + g_object_class_install_property (object_class, + 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_type_class_add_private (object_class, sizeof(GeditWindowPrivate)); +} + +static void +menu_item_select_cb (GtkMenuItem *proxy, + GeditWindow *window) +{ + GtkAction *action; + char *message; + + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy)); + g_return_if_fail (action != NULL); + + g_object_get (G_OBJECT (action), "tooltip", &message, NULL); + if (message) + { + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->tip_message_cid, message); + g_free (message); + } +} + +static void +menu_item_deselect_cb (GtkMenuItem *proxy, + GeditWindow *window) +{ + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->tip_message_cid); +} + +static void +connect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + GeditWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) + { + g_signal_connect (proxy, "select", + G_CALLBACK (menu_item_select_cb), window); + g_signal_connect (proxy, "deselect", + G_CALLBACK (menu_item_deselect_cb), window); + } +} + +static void +disconnect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + GeditWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) + { + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_select_cb), window); + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_deselect_cb), window); + } +} + +static void +apply_toolbar_style (GeditWindow *window, + GtkWidget *toolbar) +{ + switch (window->priv->toolbar_style) + { + case GEDIT_TOOLBAR_SYSTEM: + gedit_debug_message (DEBUG_WINDOW, "GEDIT: SYSTEM"); + gtk_toolbar_unset_style ( + GTK_TOOLBAR (toolbar)); + break; + + case GEDIT_TOOLBAR_ICONS: + gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS"); + gtk_toolbar_set_style ( + GTK_TOOLBAR (toolbar), + GTK_TOOLBAR_ICONS); + break; + + case GEDIT_TOOLBAR_ICONS_AND_TEXT: + gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS_AND_TEXT"); + gtk_toolbar_set_style ( + GTK_TOOLBAR (toolbar), + GTK_TOOLBAR_BOTH); + break; + + case GEDIT_TOOLBAR_ICONS_BOTH_HORIZ: + gedit_debug_message (DEBUG_WINDOW, "GEDIT: ICONS_BOTH_HORIZ"); + gtk_toolbar_set_style ( + GTK_TOOLBAR (toolbar), + GTK_TOOLBAR_BOTH_HORIZ); + break; + } +} + +/* Returns TRUE if toolbar is visible */ +static gboolean +set_toolbar_style (GeditWindow *window, + GeditWindow *origin) +{ + gboolean visible; + GeditToolbarSetting style; + GtkAction *action; + + if (origin == NULL) + visible = gedit_prefs_manager_get_toolbar_visible (); + else + visible = GTK_WIDGET_VISIBLE (origin->priv->toolbar); + + /* Set visibility */ + if (visible) + gtk_widget_show (window->priv->toolbar); + else + gtk_widget_hide (window->priv->toolbar); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewToolbar"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + + /* Set style */ + if (origin == NULL) + style = gedit_prefs_manager_get_toolbar_buttons_style (); + else + style = origin->priv->toolbar_style; + + window->priv->toolbar_style = style; + + apply_toolbar_style (window, window->priv->toolbar); + + return visible; +} + +static void +update_next_prev_doc_sensitivity (GeditWindow *window, + GeditTab *tab) +{ + gint tab_number; + GtkNotebook *notebook; + GtkAction *action; + + gedit_debug (DEBUG_WINDOW); + + notebook = GTK_NOTEBOOK (_gedit_window_get_notebook (window)); + + tab_number = gtk_notebook_page_num (notebook, GTK_WIDGET (tab)); + g_return_if_fail (tab_number >= 0); + + action = gtk_action_group_get_action (window->priv->action_group, + "DocumentsPreviousDocument"); + gtk_action_set_sensitive (action, tab_number != 0); + + action = gtk_action_group_get_action (window->priv->action_group, + "DocumentsNextDocument"); + gtk_action_set_sensitive (action, + tab_number < gtk_notebook_get_n_pages (notebook) - 1); +} + +static void +update_next_prev_doc_sensitivity_per_window (GeditWindow *window) +{ + GeditTab *tab; + GtkAction *action; + + gedit_debug (DEBUG_WINDOW); + + tab = gedit_window_get_active_tab (window); + if (tab != NULL) + { + update_next_prev_doc_sensitivity (window, tab); + + return; + } + + action = gtk_action_group_get_action (window->priv->action_group, + "DocumentsPreviousDocument"); + gtk_action_set_sensitive (action, FALSE); + + action = gtk_action_group_get_action (window->priv->action_group, + "DocumentsNextDocument"); + gtk_action_set_sensitive (action, FALSE); + +} + +static void +received_clipboard_contents (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + GeditWindow *window) +{ + gboolean sens; + GtkAction *action; + + /* getting clipboard contents is async, so we need to + * get the current tab and its state */ + + if (window->priv->active_tab != NULL) + { + GeditTabState state; + gboolean state_normal; + + state = gedit_tab_get_state (window->priv->active_tab); + state_normal = (state == GEDIT_TAB_STATE_NORMAL); + + sens = state_normal && + gtk_selection_data_targets_include_text (selection_data); + } + else + { + sens = FALSE; + } + + action = gtk_action_group_get_action (window->priv->action_group, + "EditPaste"); + + gtk_action_set_sensitive (action, sens); + + 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 + { + GtkAction *action; + + action = gtk_action_group_get_action (window->priv->action_group, + "EditPaste"); + + /* XFIXES extension not availbale, make + * Paste always sensitive */ + gtk_action_set_sensitive (action, TRUE); + } +} + +static void +set_sensitivity_according_to_tab (GeditWindow *window, + GeditTab *tab) +{ + GeditDocument *doc; + GeditView *view; + GtkAction *action; + gboolean b; + gboolean state_normal; + gboolean editable; + GeditTabState state; + GtkClipboard *clipboard; + GeditLockdownMask lockdown; + + g_return_if_fail (GEDIT_TAB (tab)); + + gedit_debug (DEBUG_WINDOW); + + lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); + + state = gedit_tab_get_state (tab); + state_normal = (state == GEDIT_TAB_STATE_NORMAL); + + view = gedit_tab_get_view (tab); + editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view)); + + doc = GEDIT_DOCUMENT (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))); + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), + GDK_SELECTION_CLIPBOARD); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileSave"); + gtk_action_set_sensitive (action, + (state_normal || + (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || + (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && + !gedit_document_get_readonly (doc) && + !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileSaveAs"); + gtk_action_set_sensitive (action, + (state_normal || + (state == GEDIT_TAB_STATE_SAVING_ERROR) || + (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) || + (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && + !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileRevert"); + gtk_action_set_sensitive (action, + (state_normal || + (state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)) && + !gedit_document_is_untitled (doc)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FilePrintPreview"); + gtk_action_set_sensitive (action, + state_normal && + !(lockdown & GEDIT_LOCKDOWN_PRINTING)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FilePrint"); + gtk_action_set_sensitive (action, + (state_normal || + (state == GEDIT_TAB_STATE_SHOWING_PRINT_PREVIEW)) && + !(lockdown & GEDIT_LOCKDOWN_PRINTING)); + + action = gtk_action_group_get_action (window->priv->close_action_group, + "FileClose"); + + gtk_action_set_sensitive (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_PRINT_PREVIEWING) && + (state != GEDIT_TAB_STATE_SAVING_ERROR)); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditUndo"); + gtk_action_set_sensitive (action, + state_normal && + gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditRedo"); + gtk_action_set_sensitive (action, + state_normal && + gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditCut"); + gtk_action_set_sensitive (action, + state_normal && + editable && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditCopy"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditPaste"); + if (state_normal && editable) + { + set_paste_sensitivity_according_to_clipboard (window, + clipboard); + } + else + { + gtk_action_set_sensitive (action, FALSE); + } + + action = gtk_action_group_get_action (window->priv->action_group, + "EditDelete"); + gtk_action_set_sensitive (action, + state_normal && + editable && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchFind"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchIncrementalSearch"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchReplace"); + gtk_action_set_sensitive (action, + state_normal && + editable); + + b = gedit_document_get_can_search_again (doc); + action = gtk_action_group_get_action (window->priv->action_group, + "SearchFindNext"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchFindPrevious"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchClearHighlight"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && b); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchGoToLine"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION)); + + action = gtk_action_group_get_action (window->priv->action_group, + "ViewHighlightMode"); + gtk_action_set_sensitive (action, + (state != GEDIT_TAB_STATE_CLOSING) && + gedit_prefs_manager_get_enable_syntax_highlighting ()); + + update_next_prev_doc_sensitivity (window, tab); + + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); +} + +static void +language_toggled (GtkToggleAction *action, + GeditWindow *window) +{ + GeditDocument *doc; + GtkSourceLanguage *lang; + const gchar *lang_id; + + if (gtk_toggle_action_get_active (action) == FALSE) + return; + + doc = gedit_window_get_active_document (window); + if (doc == NULL) + return; + + lang_id = gtk_action_get_name (GTK_ACTION (action)); + + if (strcmp (lang_id, LANGUAGE_NONE) == 0) + { + /* Normal (no highlighting) */ + lang = NULL; + } + else + { + lang = gtk_source_language_manager_get_language ( + gedit_get_language_manager (), + lang_id); + if (lang == NULL) + { + g_warning ("Could not get language %s\n", lang_id); + } + } + + gedit_document_set_language (doc, lang); +} + +static gchar * +escape_section_name (const gchar *name) +{ + gchar *ret; + + ret = g_markup_escape_text (name, -1); + + /* Replace '/' with '-' to avoid problems in xml paths */ + g_strdelimit (ret, "/", '-'); + + return ret; +} + +static void +create_language_menu_item (GtkSourceLanguage *lang, + gint index, + guint ui_id, + GeditWindow *window) +{ + GtkAction *section_action; + GtkRadioAction *action; + GtkAction *normal_action; + GSList *group; + const gchar *section; + gchar *escaped_section; + const gchar *lang_id; + const gchar *lang_name; + gchar *escaped_lang_name; + gchar *tip; + gchar *path; + + section = gtk_source_language_get_section (lang); + escaped_section = escape_section_name (section); + + /* check if the section submenu exists or create it */ + section_action = gtk_action_group_get_action (window->priv->languages_action_group, + escaped_section); + + if (section_action == NULL) + { + gchar *section_name; + + section_name = gedit_utils_escape_underscores (section, -1); + + section_action = gtk_action_new (escaped_section, + section_name, + NULL, + NULL); + + g_free (section_name); + + gtk_action_group_add_action (window->priv->languages_action_group, + section_action); + g_object_unref (section_action); + + gtk_ui_manager_add_ui (window->priv->manager, + ui_id, + "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder", + escaped_section, + escaped_section, + GTK_UI_MANAGER_MENU, + FALSE); + } + + /* now add the language item to the section */ + lang_name = gtk_source_language_get_name (lang); + lang_id = gtk_source_language_get_id (lang); + + escaped_lang_name = gedit_utils_escape_underscores (lang_name, -1); + + tip = g_strdup_printf (_("Use %s highlight mode"), lang_name); + path = g_strdup_printf ("/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder/%s", + escaped_section); + + action = gtk_radio_action_new (lang_id, + escaped_lang_name, + tip, + NULL, + index); + + g_free (escaped_lang_name); + + /* Action is added with a NULL accel to make the accel overridable */ + gtk_action_group_add_action_with_accel (window->priv->languages_action_group, + GTK_ACTION (action), + NULL); + g_object_unref (action); + + /* add the action to the same radio group of the "Normal" action */ + normal_action = gtk_action_group_get_action (window->priv->languages_action_group, + LANGUAGE_NONE); + group = gtk_radio_action_get_group (GTK_RADIO_ACTION (normal_action)); + gtk_radio_action_set_group (action, group); + + g_signal_connect (action, + "activate", + G_CALLBACK (language_toggled), + window); + + gtk_ui_manager_add_ui (window->priv->manager, + ui_id, + path, + lang_id, + lang_id, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (path); + g_free (tip); + g_free (escaped_section); +} + +static void +create_languages_menu (GeditWindow *window) +{ + GtkRadioAction *action_none; + GSList *languages; + GSList *l; + guint id; + gint i; + + gedit_debug (DEBUG_WINDOW); + + /* add the "Plain Text" item before all the others */ + + /* Translators: "Plain Text" means that no highlight mode is selected in the + * "View->Highlight Mode" submenu and so syntax highlighting is disabled */ + action_none = gtk_radio_action_new (LANGUAGE_NONE, _("Plain Text"), + _("Disable syntax highlighting"), + NULL, + -1); + + gtk_action_group_add_action (window->priv->languages_action_group, + GTK_ACTION (action_none)); + g_object_unref (action_none); + + g_signal_connect (action_none, + "activate", + G_CALLBACK (language_toggled), + window); + + id = gtk_ui_manager_new_merge_id (window->priv->manager); + + gtk_ui_manager_add_ui (window->priv->manager, + id, + "/MenuBar/ViewMenu/ViewHighlightModeMenu/LanguagesMenuPlaceholder", + LANGUAGE_NONE, + LANGUAGE_NONE, + GTK_UI_MANAGER_MENUITEM, + TRUE); + + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_none), TRUE); + + /* now add all the known languages */ + languages = gedit_language_manager_list_languages_sorted ( + gedit_get_language_manager (), + FALSE); + + for (l = languages, i = 0; l != NULL; l = l->next, ++i) + { + create_language_menu_item (l->data, + i, + id, + window); + } + + g_slist_free (languages); +} + +static void +update_languages_menu (GeditWindow *window) +{ + GeditDocument *doc; + GList *actions; + GList *l; + GtkAction *action; + GtkSourceLanguage *lang; + const gchar *lang_id; + + doc = gedit_window_get_active_document (window); + if (doc == NULL) + return; + + lang = gedit_document_get_language (doc); + if (lang != NULL) + lang_id = gtk_source_language_get_id (lang); + else + lang_id = LANGUAGE_NONE; + + actions = gtk_action_group_list_actions (window->priv->languages_action_group); + + /* prevent recursion */ + for (l = actions; l != NULL; l = l->next) + { + g_signal_handlers_block_by_func (GTK_ACTION (l->data), + G_CALLBACK (language_toggled), + window); + } + + action = gtk_action_group_get_action (window->priv->languages_action_group, + lang_id); + + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + + for (l = actions; l != NULL; l = l->next) + { + g_signal_handlers_unblock_by_func (GTK_ACTION (l->data), + G_CALLBACK (language_toggled), + window); + } + + g_list_free (actions); +} + +void +_gedit_recent_add (GeditWindow *window, + const gchar *uri, + const gchar *mime) +{ + GtkRecentManager *recent_manager; + GtkRecentData *recent_data; + + static gchar *groups[2] = { + "gedit", + NULL + }; + + recent_manager = gtk_recent_manager_get_default (); + + recent_data = g_slice_new (GtkRecentData); + + recent_data->display_name = NULL; + recent_data->description = NULL; + recent_data->mime_type = (gchar *) mime; + recent_data->app_name = (gchar *) g_get_application_name (); + recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); + recent_data->groups = groups; + recent_data->is_private = FALSE; + + gtk_recent_manager_add_full (recent_manager, + uri, + recent_data); + + g_free (recent_data->app_exec); + + g_slice_free (GtkRecentData, recent_data); +} + +void +_gedit_recent_remove (GeditWindow *window, + const gchar *uri) +{ + GtkRecentManager *recent_manager; + + recent_manager = gtk_recent_manager_get_default (); + + gtk_recent_manager_remove_item (recent_manager, uri, NULL); +} + +static void +open_recent_file (const gchar *uri, + GeditWindow *window) +{ + GSList *uris = NULL; + + uris = g_slist_prepend (uris, (gpointer) uri); + + if (gedit_commands_load_uris (window, uris, NULL, 0) != 1) + { + _gedit_recent_remove (window, uri); + } + + g_slist_free (uris); +} + +static void +recent_chooser_item_activated (GtkRecentChooser *chooser, + GeditWindow *window) +{ + gchar *uri; + + uri = gtk_recent_chooser_get_current_uri (chooser); + + open_recent_file (uri, window); + + g_free (uri); +} + +static void +recents_menu_activate (GtkAction *action, + GeditWindow *window) +{ + GtkRecentInfo *info; + const gchar *uri; + + info = g_object_get_data (G_OBJECT (action), "gtk-recent-info"); + g_return_if_fail (info != NULL); + + uri = gtk_recent_info_get_uri (info); + + open_recent_file (uri, window); +} + +static gint +sort_recents_mru (GtkRecentInfo *a, GtkRecentInfo *b) +{ + return (gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a)); +} + +static void update_recent_files_menu (GeditWindow *window); + +static void +recent_manager_changed (GtkRecentManager *manager, + GeditWindow *window) +{ + /* regenerate the menu when the model changes */ + update_recent_files_menu (window); +} + +/* + * Manually construct the inline recents list in the File menu. + * Hopefully gtk 2.12 will add support for it. + */ +static void +update_recent_files_menu (GeditWindow *window) +{ + GeditWindowPrivate *p = window->priv; + GtkRecentManager *recent_manager; + gint max_recents; + GList *actions, *l, *items; + GList *filtered_items = NULL; + gint i; + + gedit_debug (DEBUG_WINDOW); + + max_recents = gedit_prefs_manager_get_max_recents (); + + g_return_if_fail (p->recents_action_group != NULL); + + if (p->recents_menu_ui_id != 0) + gtk_ui_manager_remove_ui (p->manager, + p->recents_menu_ui_id); + + actions = gtk_action_group_list_actions (p->recents_action_group); + for (l = actions; l != NULL; l = l->next) + { + g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data), + G_CALLBACK (recents_menu_activate), + window); + gtk_action_group_remove_action (p->recents_action_group, + GTK_ACTION (l->data)); + } + g_list_free (actions); + + p->recents_menu_ui_id = gtk_ui_manager_new_merge_id (p->manager); + + recent_manager = gtk_recent_manager_get_default (); + items = gtk_recent_manager_get_items (recent_manager); + + /* filter */ + for (l = items; l != NULL; l = l->next) + { + GtkRecentInfo *info = l->data; + + if (!gtk_recent_info_has_group (info, "gedit")) + continue; + + filtered_items = g_list_prepend (filtered_items, info); + } + + /* sort */ + filtered_items = g_list_sort (filtered_items, + (GCompareFunc) sort_recents_mru); + + i = 0; + for (l = filtered_items; l != NULL; l = l->next) + { + gchar *action_name; + const gchar *display_name; + gchar *escaped; + gchar *label; + gchar *uri; + gchar *ruri; + gchar *tip; + GtkAction *action; + GtkRecentInfo *info = l->data; + + /* clamp */ + if (i >= max_recents) + break; + + i++; + + action_name = g_strdup_printf ("recent-info-%d", i); + + display_name = gtk_recent_info_get_display_name (info); + escaped = gedit_utils_escape_underscores (display_name, -1); + if (i >= 10) + label = g_strdup_printf ("%d. %s", + i, + escaped); + else + label = g_strdup_printf ("_%d. %s", + i, + escaped); + g_free (escaped); + + /* gtk_recent_info_get_uri_display (info) is buggy and + * works only for local files */ + uri = gedit_utils_uri_for_display (gtk_recent_info_get_uri (info)); + ruri = gedit_utils_replace_home_dir_with_tilde (uri); + g_free (uri); + + /* Translators: %s is a URI */ + tip = g_strdup_printf (_("Open '%s'"), ruri); + g_free (ruri); + + action = gtk_action_new (action_name, + label, + tip, + NULL); + + g_object_set_data_full (G_OBJECT (action), + "gtk-recent-info", + gtk_recent_info_ref (info), + (GDestroyNotify) gtk_recent_info_unref); + + g_signal_connect (action, + "activate", + G_CALLBACK (recents_menu_activate), + window); + + gtk_action_group_add_action (p->recents_action_group, + action); + g_object_unref (action); + + gtk_ui_manager_add_ui (p->manager, + p->recents_menu_ui_id, + "/MenuBar/FileMenu/FileRecentsPlaceholder", + action_name, + action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + g_free (action_name); + g_free (label); + g_free (tip); + } + + g_list_free (filtered_items); + + g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL); + g_list_free (items); +} + +static void +set_non_homogeneus (GtkWidget *widget, gpointer data) +{ + gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (widget), FALSE); +} + +static void +toolbar_visibility_changed (GtkWidget *toolbar, + GeditWindow *window) +{ + gboolean visible; + GtkAction *action; + + visible = GTK_WIDGET_VISIBLE (toolbar); + + if (gedit_prefs_manager_toolbar_visible_can_set ()) + gedit_prefs_manager_set_toolbar_visible (visible); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewToolbar"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); +} + +static GtkWidget * +setup_toolbar_open_button (GeditWindow *window, + GtkWidget *toolbar) +{ + GtkRecentManager *recent_manager; + GtkRecentFilter *filter; + GtkWidget *toolbar_recent_menu; + GtkToolItem *open_button; + GtkAction *action; + + recent_manager = gtk_recent_manager_get_default (); + + /* recent files menu tool button */ + toolbar_recent_menu = gtk_recent_chooser_menu_new_for_manager (recent_manager); + + gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (toolbar_recent_menu), + FALSE); + gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (toolbar_recent_menu), + GTK_RECENT_SORT_MRU); + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (toolbar_recent_menu), + gedit_prefs_manager_get_max_recents ()); + + filter = gtk_recent_filter_new (); + gtk_recent_filter_add_group (filter, "gedit"); + gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (toolbar_recent_menu), + filter); + + g_signal_connect (toolbar_recent_menu, + "item_activated", + G_CALLBACK (recent_chooser_item_activated), + window); + + /* add the custom Open button to the toolbar */ + open_button = gtk_menu_tool_button_new_from_stock (GTK_STOCK_OPEN); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_button), + toolbar_recent_menu); + + gtk_tool_item_set_tooltip_text (open_button, _("Open a file")); + gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_button), + _("Open a recently used file")); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "FileOpen"); + g_object_set (action, + "is_important", TRUE, + "short_label", _("Open"), + NULL); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_button), + action); + + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), + open_button, + 1); + + return toolbar_recent_menu; +} + +static void +create_menu_bar_and_toolbar (GeditWindow *window, + GtkWidget *main_box) +{ + GtkActionGroup *action_group; + GtkAction *action; + GtkUIManager *manager; + GtkRecentManager *recent_manager; + GError *error = NULL; + gchar *ui_file; + + gedit_debug (DEBUG_WINDOW); + + manager = gtk_ui_manager_new (); + window->priv->manager = manager; + + gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_ui_manager_get_accel_group (manager)); + + action_group = gtk_action_group_new ("GeditWindowAlwaysSensitiveActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, + gedit_always_sensitive_menu_entries, + G_N_ELEMENTS (gedit_always_sensitive_menu_entries), + window); + gtk_action_group_add_toggle_actions (action_group, + gedit_always_sensitive_toggle_menu_entries, + G_N_ELEMENTS (gedit_always_sensitive_toggle_menu_entries), + window); + + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + window->priv->always_sensitive_action_group = action_group; + + action_group = gtk_action_group_new ("GeditWindowActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, + gedit_menu_entries, + G_N_ELEMENTS (gedit_menu_entries), + window); + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + window->priv->action_group = action_group; + + /* set short labels to use in the toolbar */ + action = gtk_action_group_get_action (action_group, "FileSave"); + g_object_set (action, "short_label", _("Save"), NULL); + action = gtk_action_group_get_action (action_group, "FilePrint"); + g_object_set (action, "short_label", _("Print"), NULL); + action = gtk_action_group_get_action (action_group, "SearchFind"); + g_object_set (action, "short_label", _("Find"), NULL); + action = gtk_action_group_get_action (action_group, "SearchReplace"); + g_object_set (action, "short_label", _("Replace"), NULL); + + /* set which actions should have priority on the toolbar */ + action = gtk_action_group_get_action (action_group, "FileSave"); + g_object_set (action, "is_important", TRUE, NULL); + action = gtk_action_group_get_action (action_group, "EditUndo"); + g_object_set (action, "is_important", TRUE, NULL); + + action_group = gtk_action_group_new ("GeditQuitWindowActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, + gedit_quit_menu_entries, + G_N_ELEMENTS (gedit_quit_menu_entries), + window); + + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + window->priv->quit_action_group = action_group; + + action_group = gtk_action_group_new ("GeditCloseWindowActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_actions (action_group, + gedit_close_menu_entries, + G_N_ELEMENTS (gedit_close_menu_entries), + window); + + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + window->priv->close_action_group = action_group; + + action_group = gtk_action_group_new ("GeditWindowPanesActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + gtk_action_group_add_toggle_actions (action_group, + gedit_panes_toggle_menu_entries, + G_N_ELEMENTS (gedit_panes_toggle_menu_entries), + window); + + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + window->priv->panes_action_group = action_group; + + /* now load the UI definition */ + ui_file = gedit_dirs_get_ui_file (GEDIT_UIFILE); + gtk_ui_manager_add_ui_from_file (manager, ui_file, &error); + if (error != NULL) + { + g_warning ("Could not merge %s: %s", ui_file, error->message); + g_error_free (error); + } + g_free (ui_file); + +#if !GTK_CHECK_VERSION (2, 17, 4) + /* merge page setup menu manually since we cannot have conditional + * sections in gedit-ui.xml */ + { + guint merge_id; + GeditLockdownMask lockdown; + + merge_id = gtk_ui_manager_new_merge_id (manager); + gtk_ui_manager_add_ui (manager, + merge_id, + "/MenuBar/FileMenu/FileOps_5", + "FilePageSetupMenu", + "FilePageSetup", + GTK_UI_MANAGER_MENUITEM, + FALSE); + + lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); + action = gtk_action_group_get_action (window->priv->action_group, + "FilePageSetup"); + gtk_action_set_sensitive (action, + !(lockdown & GEDIT_LOCKDOWN_PRINT_SETUP)); + } +#endif + + /* show tooltips in the statusbar */ + g_signal_connect (manager, + "connect_proxy", + G_CALLBACK (connect_proxy_cb), + window); + g_signal_connect (manager, + "disconnect_proxy", + G_CALLBACK (disconnect_proxy_cb), + window); + + /* recent files menu */ + action_group = gtk_action_group_new ("RecentFilesActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + window->priv->recents_action_group = action_group; + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + + recent_manager = gtk_recent_manager_get_default (); + window->priv->recents_handler_id = g_signal_connect (recent_manager, + "changed", + G_CALLBACK (recent_manager_changed), + window); + update_recent_files_menu (window); + + /* languages menu */ + action_group = gtk_action_group_new ("LanguagesActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + window->priv->languages_action_group = action_group; + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + create_languages_menu (window); + + /* list of open documents menu */ + action_group = gtk_action_group_new ("DocumentsListActions"); + gtk_action_group_set_translation_domain (action_group, NULL); + window->priv->documents_list_action_group = action_group; + gtk_ui_manager_insert_action_group (manager, action_group, 0); + g_object_unref (action_group); + + window->priv->menubar = gtk_ui_manager_get_widget (manager, "/MenuBar"); + gtk_box_pack_start (GTK_BOX (main_box), + window->priv->menubar, + FALSE, + FALSE, + 0); + + window->priv->toolbar = gtk_ui_manager_get_widget (manager, "/ToolBar"); + gtk_box_pack_start (GTK_BOX (main_box), + window->priv->toolbar, + FALSE, + FALSE, + 0); + + set_toolbar_style (window, NULL); + + window->priv->toolbar_recent_menu = setup_toolbar_open_button (window, + window->priv->toolbar); + + gtk_container_foreach (GTK_CONTAINER (window->priv->toolbar), + (GtkCallback)set_non_homogeneus, + NULL); + + g_signal_connect_after (G_OBJECT (window->priv->toolbar), + "show", + G_CALLBACK (toolbar_visibility_changed), + window); + g_signal_connect_after (G_OBJECT (window->priv->toolbar), + "hide", + G_CALLBACK (toolbar_visibility_changed), + window); +} + +static void +documents_list_menu_activate (GtkToggleAction *action, + GeditWindow *window) +{ + gint n; + + if (gtk_toggle_action_get_active (action) == FALSE) + return; + + n = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (action)); + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), n); +} + +static gchar * +get_menu_tip_for_tab (GeditTab *tab) +{ + GeditDocument *doc; + gchar *uri; + gchar *ruri; + gchar *tip; + + doc = gedit_tab_get_document (tab); + + uri = gedit_document_get_uri_for_display (doc); + ruri = gedit_utils_replace_home_dir_with_tilde (uri); + g_free (uri); + + /* Translators: %s is a URI */ + tip = g_strdup_printf (_("Activate '%s'"), ruri); + g_free (ruri); + + return tip; +} + +static void +update_documents_list_menu (GeditWindow *window) +{ + GeditWindowPrivate *p = window->priv; + GList *actions, *l; + gint n, i; + guint id; + GSList *group = NULL; + + gedit_debug (DEBUG_WINDOW); + + g_return_if_fail (p->documents_list_action_group != NULL); + + if (p->documents_list_menu_ui_id != 0) + gtk_ui_manager_remove_ui (p->manager, + p->documents_list_menu_ui_id); + + actions = gtk_action_group_list_actions (p->documents_list_action_group); + for (l = actions; l != NULL; l = l->next) + { + g_signal_handlers_disconnect_by_func (GTK_ACTION (l->data), + G_CALLBACK (documents_list_menu_activate), + window); + gtk_action_group_remove_action (p->documents_list_action_group, + GTK_ACTION (l->data)); + } + g_list_free (actions); + + n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (p->notebook)); + + id = (n > 0) ? gtk_ui_manager_new_merge_id (p->manager) : 0; + + for (i = 0; i < n; i++) + { + GtkWidget *tab; + GtkRadioAction *action; + gchar *action_name; + gchar *tab_name; + gchar *name; + gchar *tip; + gchar *accel; + + tab = gtk_notebook_get_nth_page (GTK_NOTEBOOK (p->notebook), i); + + /* NOTE: the action is associated to the position of the tab in + * the notebook not to the tab itself! This is needed to work + * around the gtk+ bug #170727: gtk leaves around the accels + * of the action. Since the accel depends on the tab position + * the problem is worked around, action with the same name always + * get the same accel. + */ + action_name = g_strdup_printf ("Tab_%d", i); + tab_name = _gedit_tab_get_name (GEDIT_TAB (tab)); + name = gedit_utils_escape_underscores (tab_name, -1); + tip = get_menu_tip_for_tab (GEDIT_TAB (tab)); + + /* alt + 1, 2, 3... 0 to switch to the first ten tabs */ + accel = (i < 10) ? g_strdup_printf ("<alt>%d", (i + 1) % 10) : NULL; + + action = gtk_radio_action_new (action_name, + name, + tip, + NULL, + i); + + if (group != NULL) + gtk_radio_action_set_group (action, group); + + /* note that group changes each time we add an action, so it must be updated */ + group = gtk_radio_action_get_group (action); + + gtk_action_group_add_action_with_accel (p->documents_list_action_group, + GTK_ACTION (action), + accel); + + g_signal_connect (action, + "activate", + G_CALLBACK (documents_list_menu_activate), + window); + + gtk_ui_manager_add_ui (p->manager, + id, + "/MenuBar/DocumentsMenu/DocumentsListPlaceholder", + action_name, action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + if (GEDIT_TAB (tab) == p->active_tab) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + + g_object_unref (action); + + g_free (action_name); + g_free (tab_name); + g_free (name); + g_free (tip); + g_free (accel); + } + + p->documents_list_menu_ui_id = id; +} + +/* Returns TRUE if status bar is visible */ +static gboolean +set_statusbar_style (GeditWindow *window, + GeditWindow *origin) +{ + GtkAction *action; + + gboolean visible; + + if (origin == NULL) + visible = gedit_prefs_manager_get_statusbar_visible (); + else + visible = GTK_WIDGET_VISIBLE (origin->priv->statusbar); + + if (visible) + gtk_widget_show (window->priv->statusbar); + else + gtk_widget_hide (window->priv->statusbar); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewStatusbar"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + + return visible; +} + +static void +statusbar_visibility_changed (GtkWidget *statusbar, + GeditWindow *window) +{ + gboolean visible; + GtkAction *action; + + visible = GTK_WIDGET_VISIBLE (statusbar); + + if (gedit_prefs_manager_statusbar_visible_can_set ()) + gedit_prefs_manager_set_statusbar_visible (visible); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewStatusbar"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); +} + +static void +tab_width_combo_changed (GeditStatusComboBox *combo, + GtkMenuItem *item, + GeditWindow *window) +{ + GeditView *view; + guint width_data = 0; + + view = gedit_window_get_active_view (window); + + if (!view) + return; + + width_data = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), TAB_WIDTH_DATA)); + + if (width_data == 0) + return; + + g_signal_handler_block (view, window->priv->tab_width_id); + gtk_source_view_set_tab_width (GTK_SOURCE_VIEW (view), width_data); + g_signal_handler_unblock (view, window->priv->tab_width_id); +} + +static void +use_spaces_toggled (GtkCheckMenuItem *item, + GeditWindow *window) +{ + GeditView *view; + + view = gedit_window_get_active_view (window); + + g_signal_handler_block (view, window->priv->spaces_instead_of_tabs_id); + gtk_source_view_set_insert_spaces_instead_of_tabs ( + GTK_SOURCE_VIEW (view), + gtk_check_menu_item_get_active (item)); + g_signal_handler_unblock (view, window->priv->spaces_instead_of_tabs_id); +} + +static void +language_combo_changed (GeditStatusComboBox *combo, + GtkMenuItem *item, + GeditWindow *window) +{ + GeditDocument *doc; + GtkSourceLanguage *language; + + doc = gedit_window_get_active_document (window); + + if (!doc) + return; + + language = GTK_SOURCE_LANGUAGE (g_object_get_data (G_OBJECT (item), LANGUAGE_DATA)); + + g_signal_handler_block (doc, window->priv->language_changed_id); + gedit_document_set_language (doc, language); + g_signal_handler_unblock (doc, window->priv->language_changed_id); +} + +typedef struct +{ + const gchar *label; + guint width; +} TabWidthDefinition; + +static void +fill_tab_width_combo (GeditWindow *window) +{ + static TabWidthDefinition defs[] = { + {"2", 2}, + {"4", 4}, + {"8", 8}, + {"", 0}, /* custom size */ + {NULL, 0} + }; + + GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo); + guint i = 0; + GtkWidget *item; + + while (defs[i].label != NULL) + { + item = gtk_menu_item_new_with_label (defs[i].label); + g_object_set_data (G_OBJECT (item), TAB_WIDTH_DATA, GINT_TO_POINTER (defs[i].width)); + + gedit_status_combo_box_add_item (combo, + GTK_MENU_ITEM (item), + defs[i].label); + + if (defs[i].width != 0) + gtk_widget_show (item); + + ++i; + } + + item = gtk_separator_menu_item_new (); + gedit_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL); + gtk_widget_show (item); + + item = gtk_check_menu_item_new_with_label (_("Use Spaces")); + gedit_status_combo_box_add_item (combo, GTK_MENU_ITEM (item), NULL); + gtk_widget_show (item); + + g_signal_connect (item, + "toggled", + G_CALLBACK (use_spaces_toggled), + window); +} + +static void +fill_language_combo (GeditWindow *window) +{ + GtkSourceLanguageManager *manager; + GSList *languages; + GSList *item; + GtkWidget *menu_item; + const gchar *name; + + manager = gedit_get_language_manager (); + languages = gedit_language_manager_list_languages_sorted (manager, FALSE); + + name = _("Plain Text"); + menu_item = gtk_menu_item_new_with_label (name); + gtk_widget_show (menu_item); + + g_object_set_data (G_OBJECT (menu_item), LANGUAGE_DATA, NULL); + gedit_status_combo_box_add_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), + GTK_MENU_ITEM (menu_item), + name); + + for (item = languages; item; item = item->next) + { + GtkSourceLanguage *lang = GTK_SOURCE_LANGUAGE (item->data); + + name = gtk_source_language_get_name (lang); + menu_item = gtk_menu_item_new_with_label (name); + gtk_widget_show (menu_item); + + g_object_set_data_full (G_OBJECT (menu_item), + LANGUAGE_DATA, + g_object_ref (lang), + (GDestroyNotify)g_object_unref); + + gedit_status_combo_box_add_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), + GTK_MENU_ITEM (menu_item), + name); + } + + g_slist_free (languages); +} + +static void +create_statusbar (GeditWindow *window, + GtkWidget *main_box) +{ + gedit_debug (DEBUG_WINDOW); + + window->priv->statusbar = gedit_statusbar_new (); + + window->priv->generic_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (window->priv->statusbar), "generic_message"); + window->priv->tip_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (window->priv->statusbar), "tip_message"); + + gtk_box_pack_end (GTK_BOX (main_box), + window->priv->statusbar, + FALSE, + TRUE, + 0); + + window->priv->tab_width_combo = gedit_status_combo_box_new (_("Tab Width")); + gtk_widget_show (window->priv->tab_width_combo); + gtk_box_pack_end (GTK_BOX (window->priv->statusbar), + window->priv->tab_width_combo, + FALSE, + TRUE, + 0); + + fill_tab_width_combo (window); + + g_signal_connect (G_OBJECT (window->priv->tab_width_combo), + "changed", + G_CALLBACK (tab_width_combo_changed), + window); + + window->priv->language_combo = gedit_status_combo_box_new (NULL); + gtk_widget_show (window->priv->language_combo); + gtk_box_pack_end (GTK_BOX (window->priv->statusbar), + window->priv->language_combo, + FALSE, + TRUE, + 0); + + fill_language_combo (window); + + g_signal_connect (G_OBJECT (window->priv->language_combo), + "changed", + G_CALLBACK (language_combo_changed), + window); + + g_signal_connect_after (G_OBJECT (window->priv->statusbar), + "show", + G_CALLBACK (statusbar_visibility_changed), + window); + g_signal_connect_after (G_OBJECT (window->priv->statusbar), + "hide", + G_CALLBACK (statusbar_visibility_changed), + window); + + set_statusbar_style (window, NULL); +} + +static GeditWindow * +clone_window (GeditWindow *origin) +{ + GeditWindow *window; + GdkScreen *screen; + GeditApp *app; + gint panel_page; + + gedit_debug (DEBUG_WINDOW); + + app = gedit_app_get_default (); + + screen = gtk_window_get_screen (GTK_WINDOW (origin)); + window = gedit_app_create_window (app, screen); + + if ((origin->priv->window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0) + { + gint w, h; + + gedit_prefs_manager_get_default_window_size (&w, &h); + gtk_window_set_default_size (GTK_WINDOW (window), w, h); + gtk_window_maximize (GTK_WINDOW (window)); + } + else + { + gtk_window_set_default_size (GTK_WINDOW (window), + origin->priv->width, + origin->priv->height); + + 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 panes 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 = _gedit_panel_get_active_item_id (GEDIT_PANEL (origin->priv->side_panel)); + _gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->side_panel), + panel_page); + + panel_page = _gedit_panel_get_active_item_id (GEDIT_PANEL (origin->priv->bottom_panel)); + _gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->bottom_panel), + panel_page); + + if (GTK_WIDGET_VISIBLE (origin->priv->side_panel)) + gtk_widget_show (window->priv->side_panel); + else + gtk_widget_hide (window->priv->side_panel); + + if (GTK_WIDGET_VISIBLE (origin->priv->bottom_panel)) + gtk_widget_show (window->priv->bottom_panel); + else + gtk_widget_hide (window->priv->bottom_panel); + + set_statusbar_style (window, origin); + set_toolbar_style (window, origin); + + return window; +} + +static void +update_cursor_position_statusbar (GtkTextBuffer *buffer, + GeditWindow *window) +{ + gint row, col; + GtkTextIter iter; + GtkTextIter start; + guint tab_size; + GeditView *view; + + gedit_debug (DEBUG_WINDOW); + + if (buffer != GTK_TEXT_BUFFER (gedit_window_get_active_document (window))) + return; + + view = gedit_window_get_active_view (window); + + gtk_text_buffer_get_iter_at_mark (buffer, + &iter, + gtk_text_buffer_get_insert (buffer)); + + row = gtk_text_iter_get_line (&iter); + + start = iter; + gtk_text_iter_set_line_offset (&start, 0); + col = 0; + + tab_size = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (view)); + + while (!gtk_text_iter_equal (&start, &iter)) + { + /* FIXME: Are we Unicode compliant here? */ + if (gtk_text_iter_get_char (&start) == '\t') + + col += (tab_size - (col % tab_size)); + else + ++col; + + gtk_text_iter_forward_char (&start); + } + + gedit_statusbar_set_cursor_position ( + GEDIT_STATUSBAR (window->priv->statusbar), + row + 1, + col + 1); +} + +static void +update_overwrite_mode_statusbar (GtkTextView *view, + GeditWindow *window) +{ + if (view != GTK_TEXT_VIEW (gedit_window_get_active_view (window))) + return; + + /* Note that we have to use !gtk_text_view_get_overwrite since we + are in the in the signal handler of "toggle overwrite" that is + G_SIGNAL_RUN_LAST + */ + gedit_statusbar_set_overwrite ( + GEDIT_STATUSBAR (window->priv->statusbar), + !gtk_text_view_get_overwrite (view)); +} + +#define MAX_TITLE_LENGTH 100 + +static void +set_title (GeditWindow *window) +{ + GeditDocument *doc = NULL; + gchar *name; + gchar *dirname = NULL; + gchar *title = NULL; + gint len; + + if (window->priv->active_tab == NULL) + { +#ifdef OS_OSX + gedit_osx_set_window_title (window, "gedit", NULL); +#else + gtk_window_set_title (GTK_WINDOW (window), "gedit"); +#endif + return; + } + + doc = gedit_tab_get_document (window->priv->active_tab); + g_return_if_fail (doc != NULL); + + name = gedit_document_get_short_name_for_display (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 = gedit_utils_str_middle_truncate (name, + MAX_TITLE_LENGTH); + g_free (name); + name = tmp; + } + else + { + GFile *file; + + file = gedit_document_get_location (doc); + if (file != NULL) + { + gchar *str; + + str = gedit_utils_location_get_dirname_for_display (file); + g_object_unref (file); + + /* 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 = gedit_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 (gedit_document_get_readonly (doc)) + { + if (dirname != NULL) + title = g_strdup_printf ("%s [%s] (%s) - gedit", + name, + _("Read-Only"), + dirname); + else + title = g_strdup_printf ("%s [%s] - gedit", + name, + _("Read-Only")); + } + else + { + if (dirname != NULL) + title = g_strdup_printf ("%s (%s) - gedit", + name, + dirname); + else + title = g_strdup_printf ("%s - gedit", + name); + } + +#ifdef OS_OSX + gedit_osx_set_window_title (window, title, doc); +#else + gtk_window_set_title (GTK_WINDOW (window), title); +#endif + + g_free (dirname); + g_free (name); + g_free (title); +} + +#undef MAX_TITLE_LENGTH + +static void +set_tab_width_item_blocked (GeditWindow *window, + GtkMenuItem *item) +{ + g_signal_handlers_block_by_func (window->priv->tab_width_combo, + tab_width_combo_changed, + window); + + gedit_status_combo_box_set_item (GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo), + item); + + g_signal_handlers_unblock_by_func (window->priv->tab_width_combo, + tab_width_combo_changed, + window); +} + +static void +spaces_instead_of_tabs_changed (GObject *object, + GParamSpec *pspec, + GeditWindow *window) +{ + GeditView *view = GEDIT_VIEW (object); + gboolean active = gtk_source_view_get_insert_spaces_instead_of_tabs ( + GTK_SOURCE_VIEW (view)); + GList *children = gedit_status_combo_box_get_items ( + GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo)); + GtkCheckMenuItem *item; + + item = GTK_CHECK_MENU_ITEM (g_list_last (children)->data); + + gtk_check_menu_item_set_active (item, active); + + g_list_free (children); +} + +static void +tab_width_changed (GObject *object, + GParamSpec *pspec, + GeditWindow *window) +{ + GList *items; + GList *item; + GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->tab_width_combo); + guint new_tab_width; + gboolean found = FALSE; + + items = gedit_status_combo_box_get_items (combo); + + new_tab_width = gtk_source_view_get_tab_width (GTK_SOURCE_VIEW (object)); + + for (item = items; item; item = item->next) + { + guint tab_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item->data), TAB_WIDTH_DATA)); + + if (tab_width == new_tab_width) + { + set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data)); + found = TRUE; + } + + if (GTK_IS_SEPARATOR_MENU_ITEM (item->next->data)) + { + if (!found) + { + /* Set for the last item the custom thing */ + gchar *text; + + text = g_strdup_printf ("%u", new_tab_width); + gedit_status_combo_box_set_item_text (combo, + GTK_MENU_ITEM (item->data), + text); + + gtk_label_set_text (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item->data))), + text); + + set_tab_width_item_blocked (window, GTK_MENU_ITEM (item->data)); + gtk_widget_show (GTK_WIDGET (item->data)); + } + else + { + gtk_widget_hide (GTK_WIDGET (item->data)); + } + + break; + } + } + + g_list_free (items); +} + +static void +language_changed (GObject *object, + GParamSpec *pspec, + GeditWindow *window) +{ + GList *items; + GList *item; + GeditStatusComboBox *combo = GEDIT_STATUS_COMBO_BOX (window->priv->language_combo); + GtkSourceLanguage *new_language; + const gchar *new_id; + + items = gedit_status_combo_box_get_items (combo); + + new_language = gtk_source_buffer_get_language (GTK_SOURCE_BUFFER (object)); + + if (new_language) + new_id = gtk_source_language_get_id (new_language); + else + new_id = NULL; + + for (item = items; item; item = item->next) + { + GtkSourceLanguage *lang = g_object_get_data (G_OBJECT (item->data), LANGUAGE_DATA); + + if ((new_id == NULL && lang == NULL) || + (new_id != NULL && lang != NULL && strcmp (gtk_source_language_get_id (lang), + new_id) == 0)) + { + g_signal_handlers_block_by_func (window->priv->language_combo, + language_combo_changed, + window); + + gedit_status_combo_box_set_item (GEDIT_STATUS_COMBO_BOX (window->priv->language_combo), + GTK_MENU_ITEM (item->data)); + + g_signal_handlers_unblock_by_func (window->priv->language_combo, + language_combo_changed, + window); + } + } + + g_list_free (items); +} + +static void +notebook_switch_page (GtkNotebook *book, + GtkNotebookPage *pg, + gint page_num, + GeditWindow *window) +{ + GeditView *view; + GeditTab *tab; + GtkAction *action; + gchar *action_name; + + /* CHECK: I don't know why but it seems notebook_switch_page is called + two times every time the user change the active tab */ + + tab = GEDIT_TAB (gtk_notebook_get_nth_page (book, page_num)); + if (tab == window->priv->active_tab) + return; + + if (window->priv->active_tab) + { + if (window->priv->tab_width_id) + { + g_signal_handler_disconnect (gedit_tab_get_view (window->priv->active_tab), + window->priv->tab_width_id); + + window->priv->tab_width_id = 0; + } + + if (window->priv->spaces_instead_of_tabs_id) + { + g_signal_handler_disconnect (gedit_tab_get_view (window->priv->active_tab), + window->priv->spaces_instead_of_tabs_id); + + window->priv->spaces_instead_of_tabs_id = 0; + } + } + + /* set the active tab */ + window->priv->active_tab = tab; + + set_title (window); + set_sensitivity_according_to_tab (window, tab); + + /* activate the right item in the documents menu */ + action_name = g_strdup_printf ("Tab_%d", page_num); + action = gtk_action_group_get_action (window->priv->documents_list_action_group, + action_name); + + /* sometimes the action doesn't exist yet, and the proper action + * is set active during the documents list menu creation + * CHECK: would it be nicer if active_tab was a property and we monitored the notify signal? + */ + if (action != NULL) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + + g_free (action_name); + + /* update the syntax menu */ + update_languages_menu (window); + + view = gedit_tab_get_view (tab); + + /* sync the statusbar */ + update_cursor_position_statusbar (GTK_TEXT_BUFFER (gedit_tab_get_document (tab)), + window); + gedit_statusbar_set_overwrite (GEDIT_STATUSBAR (window->priv->statusbar), + gtk_text_view_get_overwrite (GTK_TEXT_VIEW (view))); + + gtk_widget_show (window->priv->tab_width_combo); + gtk_widget_show (window->priv->language_combo); + + window->priv->tab_width_id = g_signal_connect (view, + "notify::tab-width", + G_CALLBACK (tab_width_changed), + window); + window->priv->spaces_instead_of_tabs_id = g_signal_connect (view, + "notify::insert-spaces-instead-of-tabs", + G_CALLBACK (spaces_instead_of_tabs_changed), + window); + + window->priv->language_changed_id = g_signal_connect (gedit_tab_get_document (tab), + "notify::language", + G_CALLBACK (language_changed), + window); + + /* call it for the first time */ + tab_width_changed (G_OBJECT (view), NULL, window); + spaces_instead_of_tabs_changed (G_OBJECT (view), NULL, window); + language_changed (G_OBJECT (gedit_tab_get_document (tab)), NULL, window); + + g_signal_emit (G_OBJECT (window), + signals[ACTIVE_TAB_CHANGED], + 0, + window->priv->active_tab); +} + +static void +set_sensitivity_according_to_window_state (GeditWindow *window) +{ + GtkAction *action; + GeditLockdownMask lockdown; + + lockdown = gedit_app_get_lockdown (gedit_app_get_default ()); + + /* 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) */ + gtk_action_group_set_sensitive (window->priv->quit_action_group, + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileCloseAll"); + gtk_action_set_sensitive (action, + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING) && + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING)); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileSaveAll"); + gtk_action_set_sensitive (action, + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && + !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "FileNew"); + gtk_action_set_sensitive (action, + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "FileOpen"); + gtk_action_set_sensitive (action, + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + gtk_action_group_set_sensitive (window->priv->recents_action_group, + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + gedit_notebook_set_close_buttons_sensitive (GEDIT_NOTEBOOK (window->priv->notebook), + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + gedit_notebook_set_tab_drag_and_drop_enabled (GEDIT_NOTEBOOK (window->priv->notebook), + !(window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + if ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) != 0) + { + /* TODO: If we really care, Find could be active + * when in SAVING_SESSION state */ + + if (gtk_action_group_get_sensitive (window->priv->action_group)) + gtk_action_group_set_sensitive (window->priv->action_group, + FALSE); + if (gtk_action_group_get_sensitive (window->priv->quit_action_group)) + gtk_action_group_set_sensitive (window->priv->quit_action_group, + FALSE); + if (gtk_action_group_get_sensitive (window->priv->close_action_group)) + gtk_action_group_set_sensitive (window->priv->close_action_group, + FALSE); + } + else + { + if (!gtk_action_group_get_sensitive (window->priv->action_group)) + gtk_action_group_set_sensitive (window->priv->action_group, + window->priv->num_tabs > 0); + if (!gtk_action_group_get_sensitive (window->priv->quit_action_group)) + gtk_action_group_set_sensitive (window->priv->quit_action_group, + window->priv->num_tabs > 0); + if (!gtk_action_group_get_sensitive (window->priv->close_action_group)) + { +#ifdef OS_OSX + /* On OS X, File Close is always sensitive */ + gtk_action_group_set_sensitive (window->priv->close_action_group, + TRUE); +#else + gtk_action_group_set_sensitive (window->priv->close_action_group, + window->priv->num_tabs > 0); +#endif + } + } +} + +static void +update_tab_autosave (GtkWidget *widget, + gpointer data) +{ + GeditTab *tab = GEDIT_TAB (widget); + gboolean *enabled = (gboolean *) data; + + gedit_tab_set_auto_save_enabled (tab, *enabled); +} + +void +_gedit_window_set_lockdown (GeditWindow *window, + GeditLockdownMask lockdown) +{ + GeditTab *tab; + GtkAction *action; + gboolean autosave; + + /* start/stop autosave in each existing tab */ + autosave = gedit_prefs_manager_get_auto_save (); + gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), + update_tab_autosave, + &autosave); + + /* update menues wrt the current active tab */ + tab = gedit_window_get_active_tab (window); + + set_sensitivity_according_to_tab (window, tab); + + action = gtk_action_group_get_action (window->priv->action_group, + "FileSaveAll"); + gtk_action_set_sensitive (action, + !(window->priv->state & GEDIT_WINDOW_STATE_PRINTING) && + !(lockdown & GEDIT_LOCKDOWN_SAVE_TO_DISK)); + +#if !GTK_CHECK_VERSION (2, 17, 4) + action = gtk_action_group_get_action (window->priv->action_group, + "FilePageSetup"); + gtk_action_set_sensitive (action, + !(lockdown & GEDIT_LOCKDOWN_PRINT_SETUP)); +#endif +} + +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: + case GEDIT_TAB_STATE_PRINT_PREVIEWING: + 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 = old_ws & GEDIT_WINDOW_STATE_SAVING_SESSION; + + window->priv->num_tabs_with_error = 0; + + gtk_container_foreach (GTK_CONTAINER (window->priv->notebook), + (GtkCallback)analyze_tab_state, + window); + + gedit_debug_message (DEBUG_WINDOW, "New state: %x", window->priv->state); + + if (old_ws != window->priv->state) + { + set_sensitivity_according_to_window_state (window); + + gedit_statusbar_set_window_state (GEDIT_STATUSBAR (window->priv->statusbar), + window->priv->state, + window->priv->num_tabs_with_error); + + g_object_notify (G_OBJECT (window), "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 +sync_state (GeditTab *tab, + GParamSpec *pspec, + GeditWindow *window) +{ + gedit_debug (DEBUG_WINDOW); + + update_window_state (window); + + if (tab != window->priv->active_tab) + return; + + set_sensitivity_according_to_tab (window, tab); + + g_signal_emit (G_OBJECT (window), signals[ACTIVE_TAB_STATE_CHANGED], 0); +} + +static void +sync_name (GeditTab *tab, + GParamSpec *pspec, + GeditWindow *window) +{ + GtkAction *action; + gchar *action_name; + gchar *tab_name; + gchar *escaped_name; + gchar *tip; + gint n; + GeditDocument *doc; + + if (tab == window->priv->active_tab) + { + set_title (window); + + doc = gedit_tab_get_document (tab); + action = gtk_action_group_get_action (window->priv->action_group, + "FileRevert"); + gtk_action_set_sensitive (action, + !gedit_document_is_untitled (doc)); + } + + /* sync the item in the documents list menu */ + n = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), + GTK_WIDGET (tab)); + action_name = g_strdup_printf ("Tab_%d", n); + action = gtk_action_group_get_action (window->priv->documents_list_action_group, + action_name); + g_return_if_fail (action != NULL); + + tab_name = _gedit_tab_get_name (tab); + escaped_name = gedit_utils_escape_underscores (tab_name, -1); + tip = get_menu_tip_for_tab (tab); + + g_object_set (action, "label", escaped_name, NULL); + g_object_set (action, "tooltip", tip, NULL); + + g_free (action_name); + g_free (tab_name); + g_free (escaped_name); + g_free (tip); + + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + 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); + + if ((GEDIT_WINDOW(target_window)->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) != 0) + return NULL; + + return GEDIT_WINDOW (target_window); +} + +static void +load_uris_from_drop (GeditWindow *window, + gchar **uri_list) +{ + GSList *uris = NULL; + gint i; + + if (uri_list == NULL) + return; + + for (i = 0; uri_list[i] != NULL; ++i) + { + uris = g_slist_prepend (uris, uri_list[i]); + } + + uris = g_slist_reverse (uris); + gedit_commands_load_uris (window, + uris, + NULL, + 0); + + g_slist_free (uris); +} + +/* 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; + + if (info == TARGET_URI_LIST) + { + uri_list = gedit_utils_drop_get_uris(selection_data); + load_uris_from_drop (window, uri_list); + g_strfreev (uri_list); + } +} + +/* Handle drops on the GeditView */ +static void +drop_uris_cb (GtkWidget *widget, + gchar **uri_list) +{ + GeditWindow *window; + + window = get_drop_window (widget); + + if (window == NULL) + return; + + load_uris_from_drop (window, uri_list); +} + +static void +fullscreen_controls_show (GeditWindow *window) +{ + GdkScreen *screen; + GdkRectangle fs_rect; + gint w, h; + + screen = gtk_window_get_screen (GTK_WINDOW (window)); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (GTK_WIDGET (window))), + &fs_rect); + + gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h); + + gtk_window_resize (GTK_WINDOW (window->priv->fullscreen_controls), + fs_rect.width, h); + + gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), + fs_rect.x, fs_rect.y - h + 1); + + gtk_widget_show_all (window->priv->fullscreen_controls); +} + +static gboolean +run_fullscreen_animation (gpointer data) +{ + GeditWindow *window = GEDIT_WINDOW (data); + GdkScreen *screen; + GdkRectangle fs_rect; + gint x, y; + + screen = gtk_window_get_screen (GTK_WINDOW (window)); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (GTK_WIDGET (window))), + &fs_rect); + + gtk_window_get_position (GTK_WINDOW (window->priv->fullscreen_controls), + &x, &y); + + if (window->priv->fullscreen_animation_enter) + { + if (y == fs_rect.y) + { + window->priv->fullscreen_animation_timeout_id = 0; + return FALSE; + } + else + { + gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), + x, y + 1); + return TRUE; + } + } + else + { + gint w, h; + + gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), + &w, &h); + + if (y == fs_rect.y - h + 1) + { + window->priv->fullscreen_animation_timeout_id = 0; + return FALSE; + } + else + { + gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), + x, y - 1); + return TRUE; + } + } +} + +static void +show_hide_fullscreen_toolbar (GeditWindow *window, + gboolean show, + gint height) +{ + GtkSettings *settings; + gboolean enable_animations; + + settings = gtk_widget_get_settings (GTK_WIDGET (window)); + g_object_get (G_OBJECT (settings), + "gtk-enable-animations", + &enable_animations, + NULL); + + if (enable_animations) + { + window->priv->fullscreen_animation_enter = show; + + if (window->priv->fullscreen_animation_timeout_id == 0) + { + window->priv->fullscreen_animation_timeout_id = + g_timeout_add (FULLSCREEN_ANIMATION_SPEED, + (GSourceFunc) run_fullscreen_animation, + window); + } + } + else + { + GdkRectangle fs_rect; + GdkScreen *screen; + + screen = gtk_window_get_screen (GTK_WINDOW (window)); + gdk_screen_get_monitor_geometry (screen, + gdk_screen_get_monitor_at_window (screen, + gtk_widget_get_window (GTK_WIDGET (window))), + &fs_rect); + + if (show) + gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), + fs_rect.x, fs_rect.y); + else + gtk_window_move (GTK_WINDOW (window->priv->fullscreen_controls), + fs_rect.x, fs_rect.y - height + 1); + } + +} + +static gboolean +on_fullscreen_controls_enter_notify_event (GtkWidget *widget, + GdkEventCrossing *event, + GeditWindow *window) +{ + show_hide_fullscreen_toolbar (window, TRUE, 0); + + return FALSE; +} + +static gboolean +on_fullscreen_controls_leave_notify_event (GtkWidget *widget, + GdkEventCrossing *event, + GeditWindow *window) +{ + GdkDisplay *display; + GdkScreen *screen; + gint w, h; + gint x, y; + + display = gdk_display_get_default (); + screen = gtk_window_get_screen (GTK_WINDOW (window)); + + gtk_window_get_size (GTK_WINDOW (window->priv->fullscreen_controls), &w, &h); + gdk_display_get_pointer (display, &screen, &x, &y, NULL); + + /* gtk seems to emit leave notify when clicking on tool items, + * work around it by checking the coordinates + */ + if (y >= h) + { + show_hide_fullscreen_toolbar (window, FALSE, h); + } + + return FALSE; +} + +static void +fullscreen_controls_build (GeditWindow *window) +{ + GeditWindowPrivate *priv = window->priv; + GtkWidget *toolbar; + GtkWidget *toolbar_recent_menu; + GtkAction *action; + + if (priv->fullscreen_controls != NULL) + return; + + priv->fullscreen_controls = gtk_window_new (GTK_WINDOW_POPUP); + + gtk_window_set_transient_for (GTK_WINDOW (priv->fullscreen_controls), + &window->window); + + /* popup toolbar */ + toolbar = gtk_ui_manager_get_widget (priv->manager, "/FullscreenToolBar"); + gtk_container_add (GTK_CONTAINER (priv->fullscreen_controls), + toolbar); + + action = gtk_action_group_get_action (priv->always_sensitive_action_group, + "LeaveFullscreen"); + g_object_set (action, "is-important", TRUE, NULL); + + toolbar_recent_menu = setup_toolbar_open_button (window, toolbar); + + gtk_container_foreach (GTK_CONTAINER (toolbar), + (GtkCallback)set_non_homogeneus, + NULL); + + /* Set the toolbar style */ + gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), + GTK_TOOLBAR_BOTH_HORIZ); + + g_signal_connect (priv->fullscreen_controls, "enter-notify-event", + G_CALLBACK (on_fullscreen_controls_enter_notify_event), + window); + g_signal_connect (priv->fullscreen_controls, "leave-notify-event", + G_CALLBACK (on_fullscreen_controls_leave_notify_event), + window); +} + +static void +can_search_again (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + gboolean sensitive; + GtkAction *action; + + if (doc != gedit_window_get_active_document (window)) + return; + + sensitive = gedit_document_get_can_search_again (doc); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchFindNext"); + gtk_action_set_sensitive (action, sensitive); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchFindPrevious"); + gtk_action_set_sensitive (action, sensitive); + + action = gtk_action_group_get_action (window->priv->action_group, + "SearchClearHighlight"); + gtk_action_set_sensitive (action, sensitive); +} + +static void +can_undo (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + GtkAction *action; + gboolean sensitive; + + sensitive = gtk_source_buffer_can_undo (GTK_SOURCE_BUFFER (doc)); + + if (doc != gedit_window_get_active_document (window)) + return; + + action = gtk_action_group_get_action (window->priv->action_group, + "EditUndo"); + gtk_action_set_sensitive (action, sensitive); +} + +static void +can_redo (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + GtkAction *action; + gboolean sensitive; + + sensitive = gtk_source_buffer_can_redo (GTK_SOURCE_BUFFER (doc)); + + if (doc != gedit_window_get_active_document (window)) + return; + + action = gtk_action_group_get_action (window->priv->action_group, + "EditRedo"); + gtk_action_set_sensitive (action, sensitive); +} + +static void +selection_changed (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + GeditTab *tab; + GeditView *view; + GtkAction *action; + GeditTabState state; + gboolean state_normal; + gboolean editable; + + gedit_debug (DEBUG_WINDOW); + + if (doc != gedit_window_get_active_document (window)) + return; + + tab = gedit_tab_get_from_document (doc); + state = gedit_tab_get_state (tab); + state_normal = (state == GEDIT_TAB_STATE_NORMAL); + + view = gedit_tab_get_view (tab); + editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (view)); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditCut"); + gtk_action_set_sensitive (action, + state_normal && + editable && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditCopy"); + gtk_action_set_sensitive (action, + (state_normal || + state == GEDIT_TAB_STATE_EXTERNALLY_MODIFIED_NOTIFICATION) && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + action = gtk_action_group_get_action (window->priv->action_group, + "EditDelete"); + gtk_action_set_sensitive (action, + state_normal && + editable && + gtk_text_buffer_get_has_selection (GTK_TEXT_BUFFER (doc))); + + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); +} + +static void +sync_languages_menu (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + update_languages_menu (window); + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); +} + +static void +readonly_changed (GeditDocument *doc, + GParamSpec *pspec, + GeditWindow *window) +{ + set_sensitivity_according_to_tab (window, window->priv->active_tab); + + sync_name (window->priv->active_tab, NULL, window); + + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); +} + +static void +editable_changed (GeditView *view, + GParamSpec *arg1, + GeditWindow *window) +{ + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); +} + +static void +update_sensitivity_according_to_open_tabs (GeditWindow *window) +{ + GtkAction *action; + + /* Set sensitivity */ + gtk_action_group_set_sensitive (window->priv->action_group, + window->priv->num_tabs != 0); + + action = gtk_action_group_get_action (window->priv->action_group, + "DocumentsMoveToNewWindow"); + gtk_action_set_sensitive (action, + window->priv->num_tabs > 1); + + /* Do not set close action insensitive on OS X */ +#ifndef OS_OSX + gtk_action_group_set_sensitive (window->priv->close_action_group, + window->priv->num_tabs != 0); +#endif +} + +static void +notebook_tab_added (GeditNotebook *notebook, + GeditTab *tab, + GeditWindow *window) +{ + GeditView *view; + GeditDocument *doc; + + gedit_debug (DEBUG_WINDOW); + + g_return_if_fail ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) == 0); + + ++window->priv->num_tabs; + + update_sensitivity_according_to_open_tabs (window); + + view = gedit_tab_get_view (tab); + doc = gedit_tab_get_document (tab); + + /* 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 (doc, + "cursor-moved", + G_CALLBACK (update_cursor_position_statusbar), + window); + g_signal_connect (doc, + "notify::can-search-again", + G_CALLBACK (can_search_again), + 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 (doc, + "notify::language", + G_CALLBACK (sync_languages_menu), + window); + g_signal_connect (doc, + "notify::read-only", + G_CALLBACK (readonly_changed), + window); + g_signal_connect (view, + "toggle_overwrite", + G_CALLBACK (update_overwrite_mode_statusbar), + window); + g_signal_connect (view, + "notify::editable", + G_CALLBACK (editable_changed), + window); + + update_documents_list_menu (window); + + g_signal_connect (view, + "drop_uris", + G_CALLBACK (drop_uris_cb), + NULL); + + update_window_state (window); + + g_signal_emit (G_OBJECT (window), signals[TAB_ADDED], 0, tab); +} + +static void +notebook_tab_removed (GeditNotebook *notebook, + GeditTab *tab, + GeditWindow *window) +{ + GeditView *view; + GeditDocument *doc; + + gedit_debug (DEBUG_WINDOW); + + g_return_if_fail ((window->priv->state & GEDIT_WINDOW_STATE_SAVING_SESSION) == 0); + + --window->priv->num_tabs; + + 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 (doc, + G_CALLBACK (update_cursor_position_statusbar), + window); + g_signal_handlers_disconnect_by_func (doc, + G_CALLBACK (can_search_again), + 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 (sync_languages_menu), + window); + g_signal_handlers_disconnect_by_func (doc, + G_CALLBACK (readonly_changed), + window); + g_signal_handlers_disconnect_by_func (view, + G_CALLBACK (update_overwrite_mode_statusbar), + window); + g_signal_handlers_disconnect_by_func (view, + G_CALLBACK (editable_changed), + window); + g_signal_handlers_disconnect_by_func (view, + G_CALLBACK (drop_uris_cb), + NULL); + + if (window->priv->tab_width_id && tab == gedit_window_get_active_tab (window)) + { + g_signal_handler_disconnect (view, window->priv->tab_width_id); + window->priv->tab_width_id = 0; + } + + if (window->priv->spaces_instead_of_tabs_id && tab == gedit_window_get_active_tab (window)) + { + g_signal_handler_disconnect (view, window->priv->spaces_instead_of_tabs_id); + window->priv->spaces_instead_of_tabs_id = 0; + } + + if (window->priv->language_changed_id && tab == gedit_window_get_active_tab (window)) + { + g_signal_handler_disconnect (doc, window->priv->language_changed_id); + window->priv->language_changed_id = 0; + } + + g_return_if_fail (window->priv->num_tabs >= 0); + if (window->priv->num_tabs == 0) + { + window->priv->active_tab = NULL; + + set_title (window); + + /* Remove line and col info */ + gedit_statusbar_set_cursor_position ( + GEDIT_STATUSBAR (window->priv->statusbar), + -1, + -1); + + gedit_statusbar_clear_overwrite ( + GEDIT_STATUSBAR (window->priv->statusbar)); + + /* hide the combos */ + gtk_widget_hide (window->priv->tab_width_combo); + gtk_widget_hide (window->priv->language_combo); + } + + if (!window->priv->removing_tabs) + { + update_documents_list_menu (window); + update_next_prev_doc_sensitivity_per_window (window); + } + else + { + if (window->priv->num_tabs == 0) + { + update_documents_list_menu (window); + update_next_prev_doc_sensitivity_per_window (window); + } + } + + update_sensitivity_according_to_open_tabs (window); + + if (window->priv->num_tabs == 0) + { + gedit_plugins_engine_update_plugins_ui (gedit_plugins_engine_get_default (), + window); + } + + update_window_state (window); + + g_signal_emit (G_OBJECT (window), signals[TAB_REMOVED], 0, tab); +} + +static void +notebook_tabs_reordered (GeditNotebook *notebook, + GeditWindow *window) +{ + update_documents_list_menu (window); + update_next_prev_doc_sensitivity_per_window (window); + + g_signal_emit (G_OBJECT (window), signals[TABS_REORDERED], 0); +} + +static void +notebook_tab_detached (GeditNotebook *notebook, + GeditTab *tab, + GeditWindow *window) +{ + GeditWindow *new_window; + + new_window = clone_window (window); + + gedit_notebook_move_tab (notebook, + GEDIT_NOTEBOOK (_gedit_window_get_notebook (new_window)), + tab, 0); + + gtk_window_set_position (GTK_WINDOW (new_window), + GTK_WIN_POS_MOUSE); + + gtk_widget_show (GTK_WIDGET (new_window)); +} + +static void +notebook_tab_close_request (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 gboolean +show_notebook_popup_menu (GtkNotebook *notebook, + GeditWindow *window, + GdkEventButton *event) +{ + GtkWidget *menu; +// GtkAction *action; + + menu = gtk_ui_manager_get_widget (window->priv->manager, "/NotebookPopup"); + g_return_val_if_fail (menu != NULL, FALSE); + +// CHECK do we need this? +#if 0 + /* allow extensions to sync when showing the popup */ + action = gtk_action_group_get_action (window->priv->action_group, + "NotebookPopupAction"); + g_return_val_if_fail (action != NULL, FALSE); + gtk_action_activate (action); +#endif + if (event != NULL) + { + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + NULL, NULL, + event->button, event->time); + } + else + { + GtkWidget *tab; + GtkWidget *tab_label; + + tab = GTK_WIDGET (gedit_window_get_active_tab (window)); + g_return_val_if_fail (tab != NULL, FALSE); + + tab_label = gtk_notebook_get_tab_label (notebook, tab); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + gedit_utils_menu_position_under_widget, tab_label, + 0, gtk_get_current_event_time ()); + + gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE); + } + + return TRUE; +} + +static gboolean +notebook_button_press_event (GtkNotebook *notebook, + GdkEventButton *event, + GeditWindow *window) +{ + if (GDK_BUTTON_PRESS == event->type && 3 == event->button) + { + return show_notebook_popup_menu (notebook, window, event); + } + + return FALSE; +} + +static gboolean +notebook_popup_menu (GtkNotebook *notebook, + GeditWindow *window) +{ + /* Only respond if the notebook is the actual focus */ + if (GEDIT_IS_NOTEBOOK (gtk_window_get_focus (GTK_WINDOW (window)))) + { + return show_notebook_popup_menu (notebook, window, NULL); + } + + return FALSE; +} + +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; + + gedit_debug_message (DEBUG_WINDOW, + "Restoring vpaned position: bottom panel size %d", + window->priv->bottom_panel_size); + + pos = widget->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 *side_panel, + GeditWindow *window) +{ + gboolean visible; + GtkAction *action; + + visible = GTK_WIDGET_VISIBLE (side_panel); + + if (gedit_prefs_manager_side_pane_visible_can_set ()) + gedit_prefs_manager_set_side_pane_visible (visible); + + action = gtk_action_group_get_action (window->priv->panes_action_group, + "ViewSidePane"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + + /* focus the document */ + if (!visible && window->priv->active_tab != NULL) + gtk_widget_grab_focus (GTK_WIDGET ( + gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)))); +} + +static void +create_side_panel (GeditWindow *window) +{ + GtkWidget *documents_panel; + + gedit_debug (DEBUG_WINDOW); + + window->priv->side_panel = gedit_panel_new (GTK_ORIENTATION_VERTICAL); + + gtk_paned_pack1 (GTK_PANED (window->priv->hpaned), + window->priv->side_panel, + FALSE, + FALSE); + + g_signal_connect_after (window->priv->side_panel, + "show", + G_CALLBACK (side_panel_visibility_changed), + window); + g_signal_connect_after (window->priv->side_panel, + "hide", + G_CALLBACK (side_panel_visibility_changed), + window); + + documents_panel = gedit_documents_panel_new (window); + gedit_panel_add_item_with_stock_icon (GEDIT_PANEL (window->priv->side_panel), + documents_panel, + _("Documents"), + GTK_STOCK_FILE); +} + +static void +bottom_panel_visibility_changed (GeditPanel *bottom_panel, + GeditWindow *window) +{ + gboolean visible; + GtkAction *action; + + visible = GTK_WIDGET_VISIBLE (bottom_panel); + + if (gedit_prefs_manager_bottom_panel_visible_can_set ()) + gedit_prefs_manager_set_bottom_panel_visible (visible); + + action = gtk_action_group_get_action (window->priv->panes_action_group, + "ViewBottomPane"); + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) != visible) + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible); + + /* focus the document */ + if (!visible && window->priv->active_tab != NULL) + gtk_widget_grab_focus (GTK_WIDGET ( + gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)))); +} + +static void +bottom_panel_item_removed (GeditPanel *panel, + GtkWidget *item, + GeditWindow *window) +{ + if (gedit_panel_get_n_items (panel) == 0) + { + GtkAction *action; + + gtk_widget_hide (GTK_WIDGET (panel)); + + action = gtk_action_group_get_action (window->priv->panes_action_group, + "ViewBottomPane"); + gtk_action_set_sensitive (action, FALSE); + } +} + +static void +bottom_panel_item_added (GeditPanel *panel, + GtkWidget *item, + GeditWindow *window) +{ + /* if it's the first item added, set the menu item + * sensitive and if needed show the panel */ + if (gedit_panel_get_n_items (panel) == 1) + { + GtkAction *action; + gboolean show; + + action = gtk_action_group_get_action (window->priv->panes_action_group, + "ViewBottomPane"); + gtk_action_set_sensitive (action, TRUE); + + show = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + if (show) + gtk_widget_show (GTK_WIDGET (panel)); + } +} + +static void +create_bottom_panel (GeditWindow *window) +{ + gedit_debug (DEBUG_WINDOW); + + window->priv->bottom_panel = gedit_panel_new (GTK_ORIENTATION_HORIZONTAL); + + gtk_paned_pack2 (GTK_PANED (window->priv->vpaned), + window->priv->bottom_panel, + FALSE, + FALSE); + + g_signal_connect_after (window->priv->bottom_panel, + "show", + G_CALLBACK (bottom_panel_visibility_changed), + window); + g_signal_connect_after (window->priv->bottom_panel, + "hide", + G_CALLBACK (bottom_panel_visibility_changed), + window); +} + +static void +init_panels_visibility (GeditWindow *window) +{ + gint active_page; + + gedit_debug (DEBUG_WINDOW); + + /* side pane */ + active_page = gedit_prefs_manager_get_side_panel_active_page (); + _gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->side_panel), + active_page); + + if (gedit_prefs_manager_get_side_pane_visible ()) + { + gtk_widget_show (window->priv->side_panel); + } + + /* bottom pane, it can be empty */ + if (gedit_panel_get_n_items (GEDIT_PANEL (window->priv->bottom_panel)) > 0) + { + active_page = gedit_prefs_manager_get_bottom_panel_active_page (); + _gedit_panel_set_active_item_by_id (GEDIT_PANEL (window->priv->bottom_panel), + active_page); + + if (gedit_prefs_manager_get_bottom_panel_visible ()) + { + gtk_widget_show (window->priv->bottom_panel); + } + } + else + { + GtkAction *action; + action = gtk_action_group_get_action (window->priv->panes_action_group, + "ViewBottomPane"); + gtk_action_set_sensitive (action, FALSE); + } + + /* start track sensitivity after the initial state is set */ + window->priv->bottom_panel_item_removed_handler_id = + g_signal_connect (window->priv->bottom_panel, + "item_removed", + G_CALLBACK (bottom_panel_item_removed), + window); + + g_signal_connect (window->priv->bottom_panel, + "item_added", + 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 +check_window_is_active (GeditWindow *window, + GParamSpec *property, + gpointer useless) +{ + if (window->priv->window_state & GDK_WINDOW_STATE_FULLSCREEN) + { + if (gtk_window_is_active (GTK_WINDOW (window))) + { + gtk_widget_show (window->priv->fullscreen_controls); + } + else + { + gtk_widget_hide (window->priv->fullscreen_controls); + } + } +} + +#ifdef OS_OSX +static void +setup_mac_menu (GeditWindow *window) +{ + GtkAction *action; + + gtk_widget_hide (window->priv->menubar); + action = gtk_ui_manager_get_action (window->priv->manager, "/ui/MenuBar/HelpMenu/HelpAboutMenu"); + + gtk_action_set_label (action, _("About gedit")); + + ige_mac_menu_set_menu_bar (GTK_MENU_SHELL (window->priv->menubar)); + ige_mac_menu_set_quit_menu_item (ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/FileMenu/FileQuitMenu")); + + ige_mac_menu_set_preferences_menu_item (ui_manager_menu_item (window->priv->manager, "/ui/MenuBar/EditMenu/EditPreferencesMenu")); + + add_mac_root_menu (window); + ige_mac_menu_connect_window_key_handler (GTK_WINDOW (window)); +} +#endif + +static void +connect_notebook_signals (GeditWindow *window, + GtkWidget *notebook) +{ + g_signal_connect (notebook, + "switch-page", + G_CALLBACK (notebook_switch_page), + window); + g_signal_connect (notebook, + "tab-added", + G_CALLBACK (notebook_tab_added), + window); + g_signal_connect (notebook, + "tab-removed", + G_CALLBACK (notebook_tab_removed), + window); + g_signal_connect (notebook, + "tabs-reordered", + G_CALLBACK (notebook_tabs_reordered), + window); + g_signal_connect (notebook, + "tab-detached", + G_CALLBACK (notebook_tab_detached), + window); + g_signal_connect (notebook, + "tab-close-request", + G_CALLBACK (notebook_tab_close_request), + window); + g_signal_connect (notebook, + "button-press-event", + G_CALLBACK (notebook_button_press_event), + window); + g_signal_connect (notebook, + "popup-menu", + G_CALLBACK (notebook_popup_menu), + window); +} + +static void +add_notebook (GeditWindow *window, + GtkWidget *notebook) +{ + gtk_paned_pack1 (GTK_PANED (window->priv->vpaned), + notebook, + TRUE, + TRUE); + + gtk_widget_show (notebook); + + connect_notebook_signals (window, notebook); +} + +static void +gedit_window_init (GeditWindow *window) +{ + GtkWidget *main_box; + GtkTargetList *tl; + + gedit_debug (DEBUG_WINDOW); + + window->priv = GEDIT_WINDOW_GET_PRIVATE (window); + window->priv->active_tab = NULL; + window->priv->num_tabs = 0; + window->priv->removing_tabs = FALSE; + window->priv->state = GEDIT_WINDOW_STATE_NORMAL; + window->priv->dispose_has_run = FALSE; + window->priv->fullscreen_controls = NULL; + window->priv->fullscreen_animation_timeout_id = 0; + + window->priv->message_bus = gedit_message_bus_new (); + + window->priv->window_group = gtk_window_group_new (); + gtk_window_group_add_window (window->priv->window_group, GTK_WINDOW (window)); + + main_box = gtk_vbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (window), main_box); + gtk_widget_show (main_box); + + /* Add menu bar and toolbar bar */ + create_menu_bar_and_toolbar (window, main_box); + + /* Add status bar */ + create_statusbar (window, main_box); + + /* Add the main area */ + gedit_debug_message (DEBUG_WINDOW, "Add main area"); + window->priv->hpaned = gtk_hpaned_new (); + gtk_box_pack_start (GTK_BOX (main_box), + window->priv->hpaned, + TRUE, + TRUE, + 0); + + window->priv->vpaned = gtk_vpaned_new (); + gtk_paned_pack2 (GTK_PANED (window->priv->hpaned), + window->priv->vpaned, + TRUE, + FALSE); + + gedit_debug_message (DEBUG_WINDOW, "Create gedit notebook"); + window->priv->notebook = gedit_notebook_new (); + add_notebook (window, window->priv->notebook); + + /* side and bottom panels */ + create_side_panel (window); + create_bottom_panel (window); + + /* panes' state must be restored after panels have been mapped, + * since the bottom pane position depends on the size of the vpaned. */ + window->priv->side_panel_size = gedit_prefs_manager_get_side_panel_size (); + window->priv->bottom_panel_size = gedit_prefs_manager_get_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); + + gtk_widget_show (window->priv->hpaned); + gtk_widget_show (window->priv->vpaned); + + /* Drag and drop support, set targets to NULL because we add the + default uri_targets below */ + gtk_drag_dest_set (GTK_WIDGET (window), + GTK_DEST_DEFAULT_MOTION | + GTK_DEST_DEFAULT_HIGHLIGHT | + GTK_DEST_DEFAULT_DROP, + NULL, + 0, + GDK_ACTION_COPY); + + /* Add uri targets */ + tl = gtk_drag_dest_get_target_list (GTK_WIDGET (window)); + + if (tl == NULL) + { + tl = gtk_target_list_new (NULL, 0); + 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); + + /* 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); + + /* Check if the window is active for fullscreen */ + g_signal_connect (window, + "notify::is-active", + G_CALLBACK (check_window_is_active), + NULL); + + gedit_debug_message (DEBUG_WINDOW, "Update plugins ui"); + + gedit_plugins_engine_activate_plugins (gedit_plugins_engine_get_default (), + window); + + /* set visibility of panes. + * This needs to be done after plugins activatation */ + init_panels_visibility (window); + + update_sensitivity_according_to_open_tabs (window); + +#ifdef OS_OSX + setup_mac_menu (window); +#endif + + gedit_debug_message (DEBUG_WINDOW, "END"); +} + +/** + * gedit_window_get_active_view: + * @window: a #GeditWindow + * + * Gets the active #GeditView. + * + * Returns: the active #GeditView + */ +GeditView * +gedit_window_get_active_view (GeditWindow *window) +{ + GeditView *view; + + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + if (window->priv->active_tab == NULL) + return NULL; + + view = gedit_tab_get_view (GEDIT_TAB (window->priv->active_tab)); + + return view; +} + +/** + * gedit_window_get_active_document: + * @window: a #GeditWindow + * + * Gets the active #GeditDocument. + * + * Returns: 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_notebook (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + return window->priv->notebook; +} + +/** + * 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 #GeditNotebook. + * In case @jump_to is %TRUE the #GeditNotebook switches to that new #GeditTab. + * + * Returns: a new #GeditTab + */ +GeditTab * +gedit_window_create_tab (GeditWindow *window, + gboolean jump_to) +{ + GeditTab *tab; + + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + tab = GEDIT_TAB (_gedit_tab_new ()); + gtk_widget_show (GTK_WIDGET (tab)); + + gedit_notebook_add_tab (GEDIT_NOTEBOOK (window->priv->notebook), + tab, + -1, + jump_to); + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_window_present (GTK_WINDOW (window)); + } + + return tab; +} + +/** + * gedit_window_create_tab_from_uri: + * @window: a #GeditWindow + * @uri: the uri of the document + * @encoding: a #GeditEncoding + * @line_pos: the line 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 #GeditNotebook swithes to that new #GeditTab. + * Whether @create is %TRUE, creates a new empty document if location does + * not refer to an existing file + * + * Returns: a new #GeditTab + */ +GeditTab * +gedit_window_create_tab_from_uri (GeditWindow *window, + const gchar *uri, + const GeditEncoding *encoding, + gint line_pos, + gboolean create, + gboolean jump_to) +{ + GtkWidget *tab; + + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + g_return_val_if_fail (uri != NULL, NULL); + + tab = _gedit_tab_new_from_uri (uri, + encoding, + line_pos, + create); + if (tab == NULL) + return NULL; + + gtk_widget_show (tab); + + gedit_notebook_add_tab (GEDIT_NOTEBOOK (window->priv->notebook), + GEDIT_TAB (tab), + -1, + jump_to); + + + if (!GTK_WIDGET_VISIBLE (window)) + { + gtk_window_present (GTK_WINDOW (window)); + } + + return GEDIT_TAB (tab); +} + +/** + * gedit_window_get_active_tab: + * @window: a GeditWindow + * + * Gets the active #GeditTab in the @window. + * + * Returns: 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->active_tab == NULL) ? + NULL : GEDIT_TAB (window->priv->active_tab); +} + +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: 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); + + gtk_container_foreach (GTK_CONTAINER (window->priv->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: 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); + + gtk_container_foreach (GTK_CONTAINER (window->priv->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) +{ + 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)); + + gedit_notebook_remove_tab (GEDIT_NOTEBOOK (window->priv->notebook), + tab); +} + +/** + * 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->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + window->priv->removing_tabs = TRUE; + + gedit_notebook_remove_all_tabs (GEDIT_NOTEBOOK (window->priv->notebook)); + + window->priv->removing_tabs = FALSE; +} + +/** + * gedit_window_close_tabs: + * @window: a #GeditWindow + * @tabs: 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->state & GEDIT_WINDOW_STATE_SAVING_SESSION)); + + if (tabs == NULL) + return; + + window->priv->removing_tabs = TRUE; + + while (tabs != NULL) + { + if (tabs->next == NULL) + window->priv->removing_tabs = FALSE; + + gedit_notebook_remove_tab (GEDIT_NOTEBOOK (window->priv->notebook), + GEDIT_TAB (tabs->data)); + + tabs = g_list_next (tabs); + } + + g_return_if_fail (window->priv->removing_tabs == FALSE); +} + +GeditWindow * +_gedit_window_move_tab_to_new_window (GeditWindow *window, + GeditTab *tab) +{ + GeditWindow *new_window; + + 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 (gtk_notebook_get_n_pages ( + GTK_NOTEBOOK (window->priv->notebook)) > 1, + NULL); + + new_window = clone_window (window); + + gedit_notebook_move_tab (GEDIT_NOTEBOOK (window->priv->notebook), + GEDIT_NOTEBOOK (new_window->priv->notebook), + tab, + -1); + + gtk_widget_show (GTK_WIDGET (new_window)); + + return new_window; +} + +/** + * 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) +{ + gint page_num; + + g_return_if_fail (GEDIT_IS_WINDOW (window)); + g_return_if_fail (GEDIT_IS_TAB (tab)); + + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (window->priv->notebook), + GTK_WIDGET (tab)); + g_return_if_fail (page_num != -1); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), + page_num); +} + +/** + * gedit_window_get_group: + * @window: a #GeditWindow + * + * Gets the #GtkWindowGroup in which @window resides. + * + * Returns: 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_ui_manager: + * @window: a #GeditWindow + * + * Gets the #GtkUIManager associated with the @window. + * + * Returns: the #GtkUIManager of the @window. + */ +GtkUIManager * +gedit_window_get_ui_manager (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + return window->priv->manager; +} + +/** + * gedit_window_get_side_panel: + * @window: a #GeditWindow + * + * Gets the side #GeditPanel of the @window. + * + * Returns: the side #GeditPanel. + */ +GeditPanel * +gedit_window_get_side_panel (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + return GEDIT_PANEL (window->priv->side_panel); +} + +/** + * gedit_window_get_bottom_panel: + * @window: a #GeditWindow + * + * Gets the bottom #GeditPanel of the @window. + * + * Returns: the bottom #GeditPanel. + */ +GeditPanel * +gedit_window_get_bottom_panel (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + return GEDIT_PANEL (window->priv->bottom_panel); +} + +/** + * gedit_window_get_statusbar: + * @window: a #GeditWindow + * + * Gets the #GeditStatusbar of the @window. + * + * Returns: the #GeditStatusbar of the @window. + */ +GtkWidget * +gedit_window_get_statusbar (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), 0); + + 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; +} + +GFile * +_gedit_window_get_default_location (GeditWindow *window) +{ + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + return window->priv->default_location != NULL ? + g_object_ref (window->priv->default_location) : NULL; +} + +void +_gedit_window_set_default_location (GeditWindow *window, + GFile *location) +{ + GFile *dir; + + g_return_if_fail (GEDIT_IS_WINDOW (window)); + g_return_if_fail (G_IS_FILE (location)); + + dir = g_file_get_parent (location); + g_return_if_fail (dir != NULL); + + if (window->priv->default_location != NULL) + g_object_unref (window->priv->default_location); + + window->priv->default_location = dir; +} + +/** + * gedit_window_get_unsaved_documents: + * @window: a #GeditWindow + * + * Gets the list of documents that need to be saved before closing the window. + * + * Returns: a list of #GeditDocument that need to be saved before closing the window + */ +GList * +gedit_window_get_unsaved_documents (GeditWindow *window) +{ + GList *unsaved_docs = NULL; + GList *tabs; + GList *l; + + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + + tabs = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook)); + + l = tabs; + while (l != NULL) + { + GeditTab *tab; + + tab = GEDIT_TAB (l->data); + + if (!_gedit_tab_can_close (tab)) + { + GeditDocument *doc; + + doc = gedit_tab_get_document (tab); + unsaved_docs = g_list_prepend (unsaved_docs, doc); + } + + l = g_list_next (l); + } + + g_list_free (tabs); + + return g_list_reverse (unsaved_docs); +} + +void +_gedit_window_set_saving_session_state (GeditWindow *window, + gboolean saving_session) +{ + GeditWindowState old_state; + + g_return_if_fail (GEDIT_IS_WINDOW (window)); + + old_state = window->priv->state; + + if (saving_session) + window->priv->state |= GEDIT_WINDOW_STATE_SAVING_SESSION; + else + window->priv->state &= ~GEDIT_WINDOW_STATE_SAVING_SESSION; + + if (old_state != window->priv->state) + { + set_sensitivity_according_to_window_state (window); + + g_object_notify (G_OBJECT (window), "state"); + } +} + +static void +hide_notebook_tabs_on_fullscreen (GtkNotebook *notebook, + GParamSpec *pspec, + GeditWindow *window) +{ + gtk_notebook_set_show_tabs (notebook, FALSE); +} + +void +_gedit_window_fullscreen (GeditWindow *window) +{ + g_return_if_fail (GEDIT_IS_WINDOW (window)); + + if (_gedit_window_is_fullscreen (window)) + return; + + /* Go to fullscreen mode and hide bars */ + gtk_window_fullscreen (&window->window); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), FALSE); + g_signal_connect (window->priv->notebook, "notify::show-tabs", + G_CALLBACK (hide_notebook_tabs_on_fullscreen), window); + + gtk_widget_hide (window->priv->menubar); + + g_signal_handlers_block_by_func (window->priv->toolbar, + toolbar_visibility_changed, + window); + gtk_widget_hide (window->priv->toolbar); + + g_signal_handlers_block_by_func (window->priv->statusbar, + statusbar_visibility_changed, + window); + gtk_widget_hide (window->priv->statusbar); + + fullscreen_controls_build (window); + fullscreen_controls_show (window); +} + +void +_gedit_window_unfullscreen (GeditWindow *window) +{ + gboolean visible; + GtkAction *action; + + g_return_if_fail (GEDIT_IS_WINDOW (window)); + + if (!_gedit_window_is_fullscreen (window)) + return; + + /* Unfullscreen and show bars */ + gtk_window_unfullscreen (&window->window); + g_signal_handlers_disconnect_by_func (window->priv->notebook, + hide_notebook_tabs_on_fullscreen, + window); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (window->priv->notebook), TRUE); + gtk_widget_show (window->priv->menubar); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewToolbar"); + visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + if (visible) + gtk_widget_show (window->priv->toolbar); + g_signal_handlers_unblock_by_func (window->priv->toolbar, + toolbar_visibility_changed, + window); + + action = gtk_action_group_get_action (window->priv->always_sensitive_action_group, + "ViewStatusbar"); + visible = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + if (visible) + gtk_widget_show (window->priv->statusbar); + g_signal_handlers_unblock_by_func (window->priv->statusbar, + statusbar_visibility_changed, + window); + + gtk_widget_hide (window->priv->fullscreen_controls); +} + +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: 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 = gtk_container_get_children (GTK_CONTAINER (window->priv->notebook)); + + for (l = tabs; l != NULL; l = g_list_next (l)) + { + GeditDocument *d; + GeditTab *t; + GFile *f; + + t = GEDIT_TAB (l->data); + d = gedit_tab_get_document (t); + + f = gedit_document_get_location (d); + + if ((f != NULL)) + { + gboolean found = g_file_equal (location, f); + + g_object_unref (f); + + if (found) + { + ret = t; + 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: 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; +} + +/** + * gedit_window_get_tab_from_uri: + * @window: a #GeditWindow + * @uri: the uri to get the #GeditTab + * + * Gets the #GeditTab that matches @uri. + * + * Returns: the #GeditTab associated with @uri. + * + * Deprecated: 2.24: Use gedit_window_get_tab_from_location() instead. + */ +GeditTab * +gedit_window_get_tab_from_uri (GeditWindow *window, + const gchar *uri) +{ + GFile *f; + GeditTab *tab; + + g_return_val_if_fail (GEDIT_IS_WINDOW (window), NULL); + g_return_val_if_fail (uri != NULL, NULL); + + f = g_file_new_for_uri (uri); + tab = gedit_window_get_tab_from_location (window, f); + g_object_unref (f); + + return tab; +} |