diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 15:59:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 15:59:36 +0000 |
commit | ec52555862913a23417735f9f7f5402f5230da13 (patch) | |
tree | 5e43a30d289a3daa69dddfbb060216ff6332f197 /src/nautilus-window-slot.c | |
parent | Initial commit. (diff) | |
download | nautilus-upstream/3.38.2.tar.xz nautilus-upstream/3.38.2.zip |
Adding upstream version 3.38.2.upstream/3.38.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nautilus-window-slot.c')
-rw-r--r-- | src/nautilus-window-slot.c | 3769 |
1 files changed, 3769 insertions, 0 deletions
diff --git a/src/nautilus-window-slot.c b/src/nautilus-window-slot.c new file mode 100644 index 0000000..b5ff000 --- /dev/null +++ b/src/nautilus-window-slot.c @@ -0,0 +1,3769 @@ +/* + * nautilus-window-slot.c: Nautilus window slot + * + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: Christian Neumair <cneumair@gnome.org> + */ + +#include "config.h" + +#include "nautilus-window-slot.h" + +#include "nautilus-application.h" +#include "nautilus-bookmark.h" +#include "nautilus-bookmark-list.h" +#include "nautilus-mime-actions.h" +#include "nautilus-query-editor.h" +#include "nautilus-special-location-bar.h" +#include "nautilus-toolbar.h" +#include "nautilus-trash-bar.h" +#include "nautilus-trash-monitor.h" +#include "nautilus-view.h" +#include "nautilus-window.h" +#include "nautilus-x-content-bar.h" + +#include <glib/gi18n.h> + +#include "nautilus-file.h" +#include "nautilus-file-utilities.h" +#include "nautilus-global-preferences.h" +#include "nautilus-module.h" +#include "nautilus-monitor.h" +#include "nautilus-profile.h" +#include <nautilus-extension.h> +#include "nautilus-ui-utilities.h" +#include <eel/eel-vfs-extensions.h> + +enum +{ + PROP_ACTIVE = 1, + PROP_WINDOW, + PROP_ICON, + PROP_TOOLBAR_MENU_SECTIONS, + PROP_EXTENSIONS_BACKGROUND_MENU, + PROP_TEMPLATES_MENU, + PROP_LOADING, + PROP_SEARCHING, + PROP_SELECTION, + PROP_LOCATION, + PROP_TOOLTIP, + NUM_PROPERTIES +}; + +typedef struct +{ + NautilusWindow *window; + + gboolean active : 1; + guint loading : 1; + + /* slot contains + * 1) an vbox containing extra_location_widgets + * 2) the view + */ + GtkWidget *extra_location_widgets; + + /* Slot actions */ + GActionGroup *slot_action_group; + + /* Current location. */ + GFile *location; + gchar *title; + + /* Viewed file */ + NautilusView *content_view; + NautilusView *new_content_view; + NautilusFile *viewed_file; + gboolean viewed_file_seen; + gboolean viewed_file_in_trash; + + /* Information about bookmarks and history list */ + NautilusBookmark *current_location_bookmark; + NautilusBookmark *last_location_bookmark; + GList *back_list; + GList *forward_list; + + /* Query editor */ + NautilusQueryEditor *query_editor; + NautilusQuery *pending_search_query; + gulong qe_changed_id; + gulong qe_cancel_id; + gulong qe_activated_id; + gulong qe_focus_view_id; + + GtkLabel *search_info_label; + GtkRevealer *search_info_label_revealer; + + /* Load state */ + GCancellable *find_mount_cancellable; + /* It could be either the view is loading the files or the search didn't + * finish. Used for showing a spinner to provide feedback to the user. */ + gboolean allow_stop; + gboolean needs_reload; + + /* New location. */ + GFile *pending_location; + NautilusLocationChangeType location_change_type; + guint location_change_distance; + char *pending_scroll_to; + GList *pending_selection; + NautilusFile *pending_file_to_activate; + NautilusFile *determine_view_file; + GCancellable *mount_cancellable; + GError *mount_error; + gboolean tried_mount; + gint view_mode_before_search; + + /* Menus */ + GMenuModel *extensions_background_menu; + GMenuModel *templates_menu; + + /* View bindings */ + GBinding *searching_binding; + GBinding *selection_binding; + GBinding *extensions_background_menu_binding; + GBinding *templates_menu_binding; + gboolean searching; + GList *selection; +} NautilusWindowSlotPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (NautilusWindowSlot, nautilus_window_slot, GTK_TYPE_BOX); + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +static void nautilus_window_slot_force_reload (NautilusWindowSlot *self); +static void change_view (NautilusWindowSlot *self); +static void hide_query_editor (NautilusWindowSlot *self); +static void nautilus_window_slot_sync_actions (NautilusWindowSlot *self); +static void nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *self); +static void nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *self); +static gboolean nautilus_window_slot_content_view_matches (NautilusWindowSlot *self, + guint id); +static NautilusView *nautilus_window_slot_get_view_for_location (NautilusWindowSlot *self, + GFile *location); +static void nautilus_window_slot_set_content_view (NautilusWindowSlot *self, + guint id); +static void nautilus_window_slot_set_loading (NautilusWindowSlot *self, + gboolean loading); +char *nautilus_window_slot_get_location_uri (NautilusWindowSlot *self); +static void nautilus_window_slot_set_search_visible (NautilusWindowSlot *self, + gboolean visible); +static gboolean nautilus_window_slot_get_search_visible (NautilusWindowSlot *self); +static void nautilus_window_slot_set_location (NautilusWindowSlot *self, + GFile *location); +static void trash_state_changed_cb (NautilusTrashMonitor *monitor, + gboolean is_empty, + gpointer user_data); +static void update_search_information (NautilusWindowSlot *self); +static void real_set_extensions_background_menu (NautilusWindowSlot *self, + GMenuModel *menu); +static GMenuModel *real_get_extensions_background_menu (NautilusWindowSlot *self); +static void real_set_templates_menu (NautilusWindowSlot *self, + GMenuModel *menu); +static GMenuModel *real_get_templates_menu (NautilusWindowSlot *self); +static void nautilus_window_slot_setup_extra_location_widgets (NautilusWindowSlot *self); + +void +free_navigation_state (gpointer data) +{ + NautilusNavigationState *navigation_state = data; + + g_list_free_full (navigation_state->back_list, g_object_unref); + g_list_free_full (navigation_state->forward_list, g_object_unref); + nautilus_file_unref (navigation_state->file); + g_clear_object (&navigation_state->current_location_bookmark); + + g_free (navigation_state); +} + +void +nautilus_window_slot_restore_navigation_state (NautilusWindowSlot *self, + NautilusNavigationState *data) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + /* We are restoring saved history to newly created slot with no history. */ + g_warn_if_fail (priv->back_list == NULL && priv->forward_list == NULL); + + priv->back_list = g_steal_pointer (&data->back_list); + + priv->forward_list = g_steal_pointer (&data->forward_list); + + priv->view_mode_before_search = data->view_before_search; + + g_set_object (&priv->current_location_bookmark, data->current_location_bookmark); + + priv->location_change_type = NAUTILUS_LOCATION_CHANGE_RELOAD; +} + +NautilusNavigationState * +nautilus_window_slot_get_navigation_state (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + NautilusNavigationState *data; + GList *back_list; + GList *forward_list; + + priv = nautilus_window_slot_get_instance_private (self); + + if (priv->location == NULL) + { + return NULL; + } + + back_list = g_list_copy_deep (priv->back_list, + (GCopyFunc) g_object_ref, + NULL); + forward_list = g_list_copy_deep (priv->forward_list, + (GCopyFunc) g_object_ref, + NULL); + + /* This data is used to restore a tab after it was closed. + * In order to do that we need to keep the history, what was + * the view mode before search and a reference to the file. + * A GFile isn't enough, as the NautilusFile also keeps a + * reference to the search directory */ + data = g_new0 (NautilusNavigationState, 1); + data->back_list = back_list; + data->forward_list = forward_list; + data->file = nautilus_file_get (priv->location); + data->view_before_search = priv->view_mode_before_search; + g_set_object (&data->current_location_bookmark, priv->current_location_bookmark); + + return data; +} + +gboolean +nautilus_window_slot_handles_location (NautilusWindowSlot *self, + GFile *location) +{ + return NAUTILUS_WINDOW_SLOT_CLASS (G_OBJECT_GET_CLASS (self))->handles_location (self, location); +} + +static gboolean +real_handles_location (NautilusWindowSlot *self, + GFile *location) +{ + NautilusFile *file; + gboolean handles_location; + g_autofree char *uri = NULL; + + uri = g_file_get_uri (location); + + file = nautilus_file_get (location); + handles_location = !nautilus_file_is_other_locations (file); + nautilus_file_unref (file); + + return handles_location; +} + +static NautilusView * +nautilus_window_slot_get_view_for_location (NautilusWindowSlot *self, + GFile *location) +{ + return NAUTILUS_WINDOW_SLOT_CLASS (G_OBJECT_GET_CLASS (self))->get_view_for_location (self, location); +} + +static NautilusView * +real_get_view_for_location (NautilusWindowSlot *self, + GFile *location) +{ + NautilusWindowSlotPrivate *priv; + NautilusFile *file; + NautilusView *view; + guint view_id; + + priv = nautilus_window_slot_get_instance_private (self); + file = nautilus_file_get (location); + view = NULL; + view_id = NAUTILUS_VIEW_INVALID_ID; + + /* If we are in search, try to use by default list view. */ + if (nautilus_file_is_in_search (file)) + { + /* If it's already set, is because we already made the change to search mode, + * so the view mode of the current view will be the one search is using, + * which is not the one we are interested in */ + if (priv->view_mode_before_search == NAUTILUS_VIEW_INVALID_ID && priv->content_view) + { + priv->view_mode_before_search = nautilus_files_view_get_view_id (priv->content_view); + } + view_id = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_SEARCH_VIEW); + } + else if (priv->content_view != NULL) + { + /* If there is already a view, just use the view mode that it's currently using, or + * if we were on search before, use what we were using before entering + * search mode */ + if (priv->view_mode_before_search != NAUTILUS_VIEW_INVALID_ID) + { + view_id = priv->view_mode_before_search; + priv->view_mode_before_search = NAUTILUS_VIEW_INVALID_ID; + } + else + { + view_id = nautilus_files_view_get_view_id (priv->content_view); + } + } + + /* If there is not previous view in this slot, use the default view mode + * from preferences */ + if (view_id == NAUTILUS_VIEW_INVALID_ID) + { + view_id = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_DEFAULT_FOLDER_VIEWER); + } + + /* Try to reuse the current view */ + if (nautilus_window_slot_content_view_matches (self, view_id)) + { + view = priv->content_view; + } + else + { + view = NAUTILUS_VIEW (nautilus_files_view_new (view_id, self)); + } + + nautilus_file_unref (file); + + return view; +} + +static gboolean +nautilus_window_slot_content_view_matches (NautilusWindowSlot *self, + guint id) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view == NULL) + { + return FALSE; + } + + if (id != NAUTILUS_VIEW_INVALID_ID && NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + return nautilus_files_view_get_view_id (priv->content_view) == id; + } + else + { + return FALSE; + } +} + +static void +update_search_visible (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + NautilusQuery *query; + NautilusView *view; + + priv = nautilus_window_slot_get_instance_private (self); + + view = nautilus_window_slot_get_current_view (self); + /* If we changed location just to another search location, for example, + * when changing the query, just keep the search visible. + * Make sure the search is visible though, since we could be returning + * from a previous search location when using the history */ + if (nautilus_view_is_searching (view)) + { + nautilus_window_slot_set_search_visible (self, TRUE); + return; + } + + query = nautilus_query_editor_get_query (priv->query_editor); + if (query) + { + /* If the view is not searching, but search is visible, and the + * query is empty, we don't hide it. Some users enable the search + * and then change locations, then they search. */ + if (!nautilus_query_is_empty (query)) + { + nautilus_window_slot_set_search_visible (self, FALSE); + } + } + + if (priv->pending_search_query) + { + nautilus_window_slot_search (self, g_object_ref (priv->pending_search_query)); + } +} + +static void +nautilus_window_slot_sync_actions (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + GAction *action; + GVariant *variant; + + priv = nautilus_window_slot_get_instance_private (self); + if (!nautilus_window_slot_get_active (self)) + { + return; + } + + if (priv->content_view == NULL || priv->new_content_view != NULL) + { + return; + } + + /* Check if we need to close the search or show search after changing the location. + * Needs to be done after the change has been done, if not, a loop happens, + * because setting the search enabled or not actually opens a location */ + update_search_visible (self); + + /* Files view mode */ + action = g_action_map_lookup_action (G_ACTION_MAP (priv->slot_action_group), "files-view-mode"); + if (g_action_get_enabled (action)) + { + variant = g_variant_new_uint32 (nautilus_files_view_get_view_id (nautilus_window_slot_get_current_view (self))); + g_action_change_state (action, variant); + } +} + +static void +query_editor_cancel_callback (NautilusQueryEditor *editor, + NautilusWindowSlot *self) +{ + nautilus_window_slot_set_search_visible (self, FALSE); +} + +static void +query_editor_activated_callback (NautilusQueryEditor *editor, + NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view != NULL) + { + if (NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (priv->content_view)); + } + } +} + +static void +query_editor_focus_view_callback (NautilusQueryEditor *editor, + NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view != NULL) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->content_view)); + } +} + +static void +query_editor_changed_callback (NautilusQueryEditor *editor, + NautilusQuery *query, + gboolean reload, + NautilusWindowSlot *self) +{ + NautilusView *view; + + view = nautilus_window_slot_get_current_view (self); + + nautilus_view_set_search_query (view, query); + nautilus_window_slot_open_location_full (self, nautilus_view_get_location (view), 0, NULL); +} + +static void +hide_query_editor (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + NautilusView *view; + + priv = nautilus_window_slot_get_instance_private (self); + view = nautilus_window_slot_get_current_view (self); + + g_clear_signal_handler (&priv->qe_changed_id, priv->query_editor); + g_clear_signal_handler (&priv->qe_cancel_id, priv->query_editor); + g_clear_signal_handler (&priv->qe_activated_id, priv->query_editor); + g_clear_signal_handler (&priv->qe_focus_view_id, priv->query_editor); + + nautilus_query_editor_set_query (priv->query_editor, NULL); + + if (nautilus_view_is_searching (view)) + { + g_autolist (NautilusFile) selection = NULL; + + selection = nautilus_view_get_selection (view); + + nautilus_view_set_search_query (view, NULL); + nautilus_window_slot_open_location_full (self, + nautilus_view_get_location (view), + 0, + selection); + } + + if (nautilus_window_slot_get_active (self)) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->window)); + } +} + +static GFile * +nautilus_window_slot_get_current_location (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->pending_location != NULL) + { + return priv->pending_location; + } + + if (priv->location != NULL) + { + return priv->location; + } + + return NULL; +} + +static void +show_query_editor (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + NautilusView *view; + + priv = nautilus_window_slot_get_instance_private (self); + view = nautilus_window_slot_get_current_view (self); + if (view == NULL) + { + return; + } + + if (nautilus_view_is_searching (view)) + { + NautilusQuery *query; + + query = nautilus_view_get_search_query (view); + + if (query != NULL) + { + nautilus_query_editor_set_query (priv->query_editor, query); + } + } + + gtk_widget_grab_focus (GTK_WIDGET (priv->query_editor)); + + if (priv->qe_changed_id == 0) + { + priv->qe_changed_id = + g_signal_connect (priv->query_editor, "changed", + G_CALLBACK (query_editor_changed_callback), self); + } + if (priv->qe_cancel_id == 0) + { + priv->qe_cancel_id = + g_signal_connect (priv->query_editor, "cancel", + G_CALLBACK (query_editor_cancel_callback), self); + } + if (priv->qe_activated_id == 0) + { + priv->qe_activated_id = + g_signal_connect (priv->query_editor, "activated", + G_CALLBACK (query_editor_activated_callback), self); + } + if (priv->qe_focus_view_id == 0) + { + priv->qe_focus_view_id = + g_signal_connect (priv->query_editor, "focus-view", + G_CALLBACK (query_editor_focus_view_callback), self); + } +} + +static void +nautilus_window_slot_set_search_visible (NautilusWindowSlot *self, + gboolean visible) +{ + NautilusWindowSlotPrivate *priv; + GAction *action; + + priv = nautilus_window_slot_get_instance_private (self); + + action = g_action_map_lookup_action (G_ACTION_MAP (priv->slot_action_group), + "search-visible"); + g_action_change_state (action, g_variant_new_boolean (visible)); +} + +static gboolean +nautilus_window_slot_get_search_visible (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + GAction *action; + GVariant *state; + gboolean searching; + + priv = nautilus_window_slot_get_instance_private (self); + action = g_action_map_lookup_action (G_ACTION_MAP (priv->slot_action_group), + "search-visible"); + state = g_action_get_state (action); + searching = g_variant_get_boolean (state); + + g_variant_unref (state); + + return searching; +} + +void +nautilus_window_slot_search (NautilusWindowSlot *self, + NautilusQuery *query) +{ + NautilusWindowSlotPrivate *priv; + NautilusView *view; + + priv = nautilus_window_slot_get_instance_private (self); + g_clear_object (&priv->pending_search_query); + + view = nautilus_window_slot_get_current_view (self); + /* We could call this when the location is still being checked in the + * window slot. For that, save the search we want to do for once we have + * a view set up */ + if (view) + { + nautilus_window_slot_set_search_visible (self, TRUE); + nautilus_query_editor_set_query (priv->query_editor, query); + } + else + { + priv->pending_search_query = g_object_ref (query); + } +} + +gboolean +nautilus_window_slot_handle_event (NautilusWindowSlot *self, + GdkEvent *event) +{ + NautilusWindowSlotPrivate *priv; + gboolean retval; + GAction *action; + guint keyval; + + priv = nautilus_window_slot_get_instance_private (self); + retval = FALSE; + action = g_action_map_lookup_action (G_ACTION_MAP (priv->slot_action_group), + "search-visible"); + + if (gdk_event_get_event_type (event) != GDK_KEY_PRESS) + { + return GDK_EVENT_PROPAGATE; + } + + if (G_UNLIKELY (!gdk_event_get_keyval (event, &keyval))) + { + g_return_val_if_reached (GDK_EVENT_PROPAGATE); + } + + if (keyval == GDK_KEY_Escape) + { + g_autoptr (GVariant) state = NULL; + + state = g_action_get_state (action); + + if (g_variant_get_boolean (state)) + { + nautilus_window_slot_set_search_visible (self, FALSE); + } + } + + /* If the action is not enabled, don't try to handle search */ + if (g_action_get_enabled (action)) + { + retval = nautilus_query_editor_handle_event (priv->query_editor, event); + } + + if (retval) + { + nautilus_window_slot_set_search_visible (self, TRUE); + } + + return retval; +} + +static void +remove_all_extra_location_widgets (GtkWidget *widget, + gpointer data) +{ + NautilusWindowSlotPrivate *priv; + NautilusWindowSlot *self = data; + NautilusDirectory *directory; + + priv = nautilus_window_slot_get_instance_private (self); + directory = nautilus_directory_get (priv->location); + if (widget != GTK_WIDGET (priv->query_editor)) + { + gtk_container_remove (GTK_CONTAINER (priv->extra_location_widgets), widget); + } + + nautilus_directory_unref (directory); +} + +static void +nautilus_window_slot_remove_extra_location_widgets (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + gtk_container_foreach (GTK_CONTAINER (priv->extra_location_widgets), + remove_all_extra_location_widgets, + self); +} + +static void +nautilus_window_slot_add_extra_location_widget (NautilusWindowSlot *self, + GtkWidget *widget) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + gtk_box_pack_start (GTK_BOX (priv->extra_location_widgets), + widget, FALSE, TRUE, 0); + gtk_widget_show (priv->extra_location_widgets); +} + +static void +nautilus_window_slot_set_searching (NautilusWindowSlot *self, + gboolean searching) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + priv->searching = searching; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCHING]); +} + +static void +nautilus_window_slot_set_selection (NautilusWindowSlot *self, + GList *selection) +{ + NautilusWindowSlotPrivate *priv; + priv = nautilus_window_slot_get_instance_private (self); + + priv->selection = selection; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTION]); +} + +static void +real_set_extensions_background_menu (NautilusWindowSlot *self, + GMenuModel *menu) +{ + NautilusWindowSlotPrivate *priv; + priv = nautilus_window_slot_get_instance_private (self); + + g_set_object (&priv->extensions_background_menu, menu); +} + +static void +real_set_templates_menu (NautilusWindowSlot *self, + GMenuModel *menu) +{ + NautilusWindowSlotPrivate *priv; + priv = nautilus_window_slot_get_instance_private (self); + + g_set_object (&priv->templates_menu, menu); +} + +static void +nautilus_window_slot_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusWindowSlot *self = NAUTILUS_WINDOW_SLOT (object); + + switch (property_id) + { + case PROP_ACTIVE: + { + nautilus_window_slot_set_active (self, g_value_get_boolean (value)); + } + break; + + case PROP_WINDOW: + { + nautilus_window_slot_set_window (self, g_value_get_object (value)); + } + break; + + case PROP_LOCATION: + { + nautilus_window_slot_set_location (self, g_value_get_object (value)); + } + break; + + case PROP_SEARCHING: + { + nautilus_window_slot_set_searching (self, g_value_get_boolean (value)); + } + break; + + case PROP_EXTENSIONS_BACKGROUND_MENU: + { + real_set_extensions_background_menu (self, g_value_get_object (value)); + } + break; + + case PROP_TEMPLATES_MENU: + { + real_set_templates_menu (self, g_value_get_object (value)); + } + break; + + case PROP_SELECTION: + { + nautilus_window_slot_set_selection (self, g_value_get_pointer (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + break; + } +} + +static GMenuModel * +real_get_extensions_background_menu (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + return priv->extensions_background_menu; +} + +GMenuModel * +nautilus_window_slot_get_extensions_background_menu (NautilusWindowSlot *self) +{ + GMenuModel *menu = NULL; + + g_object_get (self, "extensions-background-menu", &menu, NULL); + + return menu; +} + +static GMenuModel * +real_get_templates_menu (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + return priv->templates_menu; +} + +GMenuModel * +nautilus_window_slot_get_templates_menu (NautilusWindowSlot *self) +{ + GMenuModel *menu = NULL; + + g_object_get (self, "templates-menu", &menu, NULL); + + return menu; +} + +static void +nautilus_window_slot_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusWindowSlot *self = NAUTILUS_WINDOW_SLOT (object); + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + switch (property_id) + { + case PROP_ACTIVE: + { + g_value_set_boolean (value, nautilus_window_slot_get_active (self)); + } + break; + + case PROP_WINDOW: + { + g_value_set_object (value, priv->window); + } + break; + + case PROP_ICON: + { + g_value_set_object (value, nautilus_window_slot_get_icon (self)); + } + break; + + case PROP_TOOLBAR_MENU_SECTIONS: + { + g_value_set_object (value, nautilus_window_slot_get_toolbar_menu_sections (self)); + } + break; + + case PROP_EXTENSIONS_BACKGROUND_MENU: + { + g_value_set_object (value, real_get_extensions_background_menu (self)); + } + break; + + case PROP_TEMPLATES_MENU: + { + g_value_set_object (value, real_get_templates_menu (self)); + } + break; + + case PROP_LOADING: + { + g_value_set_boolean (value, nautilus_window_slot_get_loading (self)); + } + break; + + case PROP_LOCATION: + { + g_value_set_object (value, nautilus_window_slot_get_current_location (self)); + } + break; + + case PROP_SEARCHING: + { + g_value_set_boolean (value, nautilus_window_slot_get_searching (self)); + } + break; + + case PROP_TOOLTIP: + { + g_value_set_static_string (value, nautilus_window_slot_get_tooltip (self)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + break; + } +} + +gboolean +nautilus_window_slot_get_searching (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->searching; +} + +GList * +nautilus_window_slot_get_selection (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->selection; +} + +static void +nautilus_window_slot_constructed (GObject *object) +{ + NautilusWindowSlotPrivate *priv; + NautilusWindowSlot *self = NAUTILUS_WINDOW_SLOT (object); + GtkWidget *extras_vbox; + GtkStyleContext *style_context; + + priv = nautilus_window_slot_get_instance_private (self); + G_OBJECT_CLASS (nautilus_window_slot_parent_class)->constructed (object); + + gtk_orientable_set_orientation (GTK_ORIENTABLE (self), + GTK_ORIENTATION_VERTICAL); + gtk_widget_show (GTK_WIDGET (self)); + + extras_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + priv->extra_location_widgets = extras_vbox; + gtk_box_pack_start (GTK_BOX (self), extras_vbox, FALSE, FALSE, 0); + gtk_widget_show (extras_vbox); + + priv->query_editor = NAUTILUS_QUERY_EDITOR (nautilus_query_editor_new ()); + /* We want to keep alive the query editor betwen additions and removals on the + * UI, specifically when the toolbar adds or removes it */ + g_object_ref_sink (priv->query_editor); + gtk_widget_show (GTK_WIDGET (priv->query_editor)); + + priv->search_info_label = GTK_LABEL (gtk_label_new (NULL)); + priv->search_info_label_revealer = GTK_REVEALER (gtk_revealer_new ()); + + gtk_container_add (GTK_CONTAINER (priv->search_info_label_revealer), + GTK_WIDGET (priv->search_info_label)); + gtk_container_add (GTK_CONTAINER (self), + GTK_WIDGET (priv->search_info_label_revealer)); + + gtk_widget_show (GTK_WIDGET (priv->search_info_label)); + gtk_widget_show (GTK_WIDGET (priv->search_info_label_revealer)); + + style_context = gtk_widget_get_style_context (GTK_WIDGET (priv->search_info_label)); + gtk_style_context_add_class (style_context, "search-information"); + + g_object_bind_property (self, "location", + priv->query_editor, "location", + G_BINDING_DEFAULT); + + priv->title = g_strdup (_("Loading…")); +} + +static void +action_search_visible (GSimpleAction *action, + GVariant *state, + gpointer user_data) +{ + NautilusWindowSlot *self; + GVariant *current_state; + + self = NAUTILUS_WINDOW_SLOT (user_data); + current_state = g_action_get_state (G_ACTION (action)); + if (g_variant_get_boolean (current_state) != g_variant_get_boolean (state)) + { + g_simple_action_set_state (action, state); + + if (g_variant_get_boolean (state)) + { + show_query_editor (self); + nautilus_window_slot_set_searching (self, TRUE); + } + else + { + hide_query_editor (self); + nautilus_window_slot_set_searching (self, FALSE); + } + + update_search_information (self); + } + + g_variant_unref (current_state); +} + +static void +change_files_view_mode (NautilusWindowSlot *self, + guint view_id) +{ + const gchar *preferences_key; + + if (!nautilus_window_slot_content_view_matches (self, view_id)) + { + nautilus_window_slot_set_content_view (self, view_id); + } + preferences_key = nautilus_view_is_searching (nautilus_window_slot_get_current_view (self)) ? + NAUTILUS_PREFERENCES_SEARCH_VIEW : + NAUTILUS_PREFERENCES_DEFAULT_FOLDER_VIEWER; + + g_settings_set_enum (nautilus_preferences, preferences_key, view_id); +} + +static void +action_files_view_mode_toggle (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusWindowSlot *self; + NautilusWindowSlotPrivate *priv; + guint current_view_id; + + self = NAUTILUS_WINDOW_SLOT (user_data); + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view == NULL) + { + return; + } + + current_view_id = nautilus_files_view_get_view_id (priv->content_view); + if (current_view_id == NAUTILUS_VIEW_LIST_ID) + { + change_files_view_mode (self, NAUTILUS_VIEW_GRID_ID); + } + else + { + change_files_view_mode (self, NAUTILUS_VIEW_LIST_ID); + } +} + +static void +action_files_view_mode (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + NautilusWindowSlot *self; + guint view_id; + + view_id = g_variant_get_uint32 (value); + self = NAUTILUS_WINDOW_SLOT (user_data); + + if (!NAUTILUS_IS_FILES_VIEW (nautilus_window_slot_get_current_view (self))) + { + return; + } + + change_files_view_mode (self, view_id); + + g_simple_action_set_state (action, value); +} + +const GActionEntry slot_entries[] = +{ + /* 4 is NAUTILUS_VIEW_INVALID_ID */ + { "files-view-mode", NULL, "u", "uint32 4", action_files_view_mode }, + { "files-view-mode-toggle", action_files_view_mode_toggle }, + { "search-visible", NULL, NULL, "false", action_search_visible }, +}; + +static void +update_search_information (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + GFile *location; + + priv = nautilus_window_slot_get_instance_private (self); + + if (!nautilus_window_slot_get_searching (self)) + { + gtk_revealer_set_reveal_child (priv->search_info_label_revealer, FALSE); + + return; + } + + location = nautilus_window_slot_get_current_location (self); + + if (location) + { + g_autoptr (NautilusFile) file = NULL; + gchar *label; + g_autofree gchar *uri = NULL; + + file = nautilus_file_get (location); + label = NULL; + uri = g_file_get_uri (location); + + if (nautilus_file_is_other_locations (file)) + { + label = _("Searching locations only"); + } + else if (g_str_has_prefix (uri, "network://")) + { + label = _("Searching network locations only"); + } + else if (nautilus_file_is_remote (file) && + location_settings_search_get_recursive_for_location (location) == NAUTILUS_QUERY_RECURSIVE_NEVER) + { + label = _("Remote location — only searching the current folder"); + } + else if (location_settings_search_get_recursive_for_location (location) == NAUTILUS_QUERY_RECURSIVE_NEVER) + { + label = _("Only searching the current folder"); + } + + gtk_label_set_label (priv->search_info_label, label); + gtk_revealer_set_reveal_child (priv->search_info_label_revealer, + label != NULL); + } +} + +static void +recursive_search_preferences_changed (GSettings *settings, + gchar *key, + gpointer callback_data) +{ + NautilusWindowSlot *self; + + self = callback_data; + + update_search_information (self); +} + +static void +use_experimental_views_changed_callback (GSettings *settings, + gchar *key, + gpointer callback_data) +{ + NautilusWindowSlot *self; + + self = callback_data; + + if (nautilus_window_slot_content_view_matches (self, NAUTILUS_VIEW_GRID_ID)) + { + /* Note that although this call does not change the view id, + * it changes the canvas view between new and old. + */ + nautilus_window_slot_set_content_view (self, NAUTILUS_VIEW_GRID_ID); + } +} + +static void +nautilus_window_slot_init (NautilusWindowSlot *self) +{ + GApplication *app; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + app = g_application_get_default (); + + g_signal_connect (nautilus_trash_monitor_get (), + "trash-state-changed", + G_CALLBACK (trash_state_changed_cb), self); + g_signal_connect_object (nautilus_preferences, + "changed::" NAUTILUS_PREFERENCES_USE_EXPERIMENTAL_VIEWS, + G_CALLBACK (use_experimental_views_changed_callback), self, 0); + + g_signal_connect_object (nautilus_preferences, + "changed::recursive-search", + G_CALLBACK (recursive_search_preferences_changed), + self, 0); + + priv->slot_action_group = G_ACTION_GROUP (g_simple_action_group_new ()); + g_action_map_add_action_entries (G_ACTION_MAP (priv->slot_action_group), + slot_entries, + G_N_ELEMENTS (slot_entries), + self); + gtk_widget_insert_action_group (GTK_WIDGET (self), + "slot", + G_ACTION_GROUP (priv->slot_action_group)); + nautilus_application_set_accelerator (app, "slot.files-view-mode(uint32 1)", "<control>1"); + nautilus_application_set_accelerator (app, "slot.files-view-mode(uint32 0)", "<control>2"); + nautilus_application_set_accelerator (app, "slot.search-visible", "<control>f"); + + priv->view_mode_before_search = NAUTILUS_VIEW_INVALID_ID; +} + +#define DEBUG_FLAG NAUTILUS_DEBUG_WINDOW +#include "nautilus-debug.h" + +static void begin_location_change (NautilusWindowSlot *slot, + GFile *location, + GFile *previous_location, + GList *new_selection, + NautilusLocationChangeType type, + guint distance, + const char *scroll_pos); +static void free_location_change (NautilusWindowSlot *self); +static void end_location_change (NautilusWindowSlot *self); +static void got_file_info_for_view_selection_callback (NautilusFile *file, + gpointer callback_data); +static gboolean setup_view (NautilusWindowSlot *self, + NautilusView *view); +static void load_new_location (NautilusWindowSlot *slot, + GFile *location, + GList *selection, + NautilusFile *file_to_activate, + gboolean tell_current_content_view, + gboolean tell_new_content_view); + +void +nautilus_window_slot_open_location_full (NautilusWindowSlot *self, + GFile *location, + NautilusWindowOpenFlags flags, + GList *new_selection) +{ + NautilusWindowSlotPrivate *priv; + GFile *old_location; + g_autolist (NautilusFile) old_selection = NULL; + + priv = nautilus_window_slot_get_instance_private (self); + old_selection = NULL; + old_location = nautilus_window_slot_get_location (self); + + if (priv->content_view) + { + old_selection = nautilus_view_get_selection (priv->content_view); + } + if (old_location && g_file_equal (old_location, location) && + nautilus_file_selection_equal (old_selection, new_selection)) + { + goto done; + } + + begin_location_change (self, location, old_location, new_selection, + NAUTILUS_LOCATION_CHANGE_STANDARD, 0, NULL); + +done: + nautilus_profile_end (NULL); +} + +static GList * +check_select_old_location_containing_folder (GList *new_selection, + GFile *location, + GFile *previous_location) +{ + GFile *from_folder, *parent; + + /* If there is no new selection and the new location is + * a (grand)parent of the old location then we automatically + * select the folder the previous location was in */ + if (new_selection == NULL && previous_location != NULL && + g_file_has_prefix (previous_location, location)) + { + from_folder = g_object_ref (previous_location); + parent = g_file_get_parent (from_folder); + while (parent != NULL && !g_file_equal (parent, location)) + { + g_object_unref (from_folder); + from_folder = parent; + parent = g_file_get_parent (from_folder); + } + + if (parent != NULL) + { + new_selection = g_list_prepend (NULL, nautilus_file_get (from_folder)); + g_object_unref (parent); + } + + g_object_unref (from_folder); + } + + return new_selection; +} + +static void +check_force_reload (GFile *location, + NautilusLocationChangeType type) +{ + NautilusDirectory *directory; + NautilusFile *file; + gboolean force_reload; + + /* The code to force a reload is here because if we do it + * after determining an initial view (in the components), then + * we end up fetching things twice. + */ + directory = nautilus_directory_get (location); + file = nautilus_file_get (location); + + if (type == NAUTILUS_LOCATION_CHANGE_RELOAD) + { + force_reload = TRUE; + } + else + { + force_reload = !nautilus_directory_is_local (directory); + } + + /* We need to invalidate file attributes as well due to how mounting works + * in the window slot and to avoid other caching issues. + * Read handle_mount_if_needed for one example */ + if (force_reload) + { + nautilus_file_invalidate_all_attributes (file); + nautilus_directory_force_reload (directory); + } + + nautilus_directory_unref (directory); + nautilus_file_unref (file); +} + +static void +save_scroll_position_for_history (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + /* Set current_bookmark scroll pos */ + if (priv->current_location_bookmark != NULL && + priv->content_view != NULL && + NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + char *current_pos; + + current_pos = nautilus_files_view_get_first_visible_file (NAUTILUS_FILES_VIEW (priv->content_view)); + nautilus_bookmark_set_scroll_pos (priv->current_location_bookmark, current_pos); + g_free (current_pos); + } +} + +/* + * begin_location_change + * + * Change a window slot's location. + * @window: The NautilusWindow whose location should be changed. + * @location: A url specifying the location to load + * @previous_location: The url that was previously shown in the window that initialized the change, if any + * @new_selection: The initial selection to present after loading the location + * @type: Which type of location change is this? Standard, back, forward, or reload? + * @distance: If type is back or forward, the index into the back or forward chain. If + * type is standard or reload, this is ignored, and must be 0. + * @scroll_pos: The file to scroll to when the location is loaded. + * + * This is the core function for changing the location of a window. Every change to the + * location begins here. + */ +static void +begin_location_change (NautilusWindowSlot *self, + GFile *location, + GFile *previous_location, + GList *new_selection, + NautilusLocationChangeType type, + guint distance, + const char *scroll_pos) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (self != NULL); + g_assert (location != NULL); + g_assert (type == NAUTILUS_LOCATION_CHANGE_BACK + || type == NAUTILUS_LOCATION_CHANGE_FORWARD + || distance == 0); + + nautilus_profile_start (NULL); + + priv = nautilus_window_slot_get_instance_private (self); + /* Avoid to update status from the current view in our async calls */ + nautilus_window_slot_disconnect_content_view (self); + /* We are going to change the location, so make sure we stop any loading + * or searching of the previous view, so we avoid to be slow */ + nautilus_window_slot_stop_loading (self); + + nautilus_window_slot_set_allow_stop (self, TRUE); + + new_selection = check_select_old_location_containing_folder (new_selection, location, previous_location); + + g_assert (priv->pending_location == NULL); + + priv->pending_location = g_object_ref (location); + priv->location_change_type = type; + priv->location_change_distance = distance; + priv->tried_mount = FALSE; + priv->pending_selection = nautilus_file_list_copy (new_selection); + + priv->pending_scroll_to = g_strdup (scroll_pos); + + check_force_reload (location, type); + + save_scroll_position_for_history (self); + + /* Get the info needed to make decisions about how to open the new location */ + priv->determine_view_file = nautilus_file_get (location); + g_assert (priv->determine_view_file != NULL); + + nautilus_file_call_when_ready (priv->determine_view_file, + NAUTILUS_FILE_ATTRIBUTE_INFO | + NAUTILUS_FILE_ATTRIBUTE_MOUNT, + got_file_info_for_view_selection_callback, + self); + + nautilus_profile_end (NULL); +} + +static void +nautilus_window_slot_set_location (NautilusWindowSlot *self, + GFile *location) +{ + NautilusWindowSlotPrivate *priv; + GFile *old_location; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->location && + g_file_equal (location, priv->location)) + { + /* The location name could be updated even if the location + * wasn't changed. This is the case for a search. + */ + nautilus_window_slot_update_title (self); + return; + } + + old_location = priv->location; + priv->location = g_object_ref (location); + + if (nautilus_window_slot_get_active (self)) + { + nautilus_window_sync_location_widgets (priv->window); + } + + nautilus_window_slot_update_title (self); + + if (old_location) + { + g_object_unref (old_location); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOCATION]); +} + +static void +viewed_file_changed_callback (NautilusFile *file, + NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + GFile *new_location; + gboolean is_in_trash, was_in_trash; + + g_assert (NAUTILUS_IS_FILE (file)); + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + g_assert (file == priv->viewed_file); + + if (!nautilus_file_is_not_yet_confirmed (file)) + { + priv->viewed_file_seen = TRUE; + } + + was_in_trash = priv->viewed_file_in_trash; + + priv->viewed_file_in_trash = is_in_trash = nautilus_file_is_in_trash (file); + + if (nautilus_file_is_gone (file) || (is_in_trash && !was_in_trash)) + { + if (priv->viewed_file_seen) + { + GFile *go_to_file; + GFile *parent; + GFile *location; + GMount *mount; + + parent = NULL; + location = nautilus_file_get_location (file); + + if (g_file_is_native (location)) + { + mount = nautilus_get_mounted_mount_for_root (location); + + if (mount == NULL) + { + parent = g_file_get_parent (location); + } + + g_clear_object (&mount); + } + + if (parent != NULL) + { + /* auto-show existing parent */ + go_to_file = nautilus_find_existing_uri_in_hierarchy (parent); + } + else + { + go_to_file = g_file_new_for_path (g_get_home_dir ()); + } + + nautilus_window_slot_open_location_full (self, go_to_file, 0, NULL); + + g_clear_object (&parent); + g_object_unref (go_to_file); + g_object_unref (location); + } + } + else + { + new_location = nautilus_file_get_location (file); + nautilus_window_slot_set_location (self, new_location); + g_object_unref (new_location); + } +} + +static void +nautilus_window_slot_go_home (NautilusWindowSlot *self, + NautilusWindowOpenFlags flags) +{ + GFile *home; + + g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (self)); + + home = g_file_new_for_path (g_get_home_dir ()); + nautilus_window_slot_open_location_full (self, home, flags, NULL); + g_object_unref (home); +} + +static void +nautilus_window_slot_set_viewed_file (NautilusWindowSlot *self, + NautilusFile *file) +{ + NautilusWindowSlotPrivate *priv; + NautilusFileAttributes attributes; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->viewed_file == file) + { + return; + } + + nautilus_file_ref (file); + + if (priv->viewed_file != NULL) + { + g_signal_handlers_disconnect_by_func (priv->viewed_file, + G_CALLBACK (viewed_file_changed_callback), + self); + nautilus_file_monitor_remove (priv->viewed_file, + self); + } + + if (file != NULL) + { + attributes = NAUTILUS_FILE_ATTRIBUTE_INFO; + nautilus_file_monitor_add (file, self, attributes); + + g_signal_connect_object (file, "changed", + G_CALLBACK (viewed_file_changed_callback), self, 0); + } + + nautilus_file_unref (priv->viewed_file); + priv->viewed_file = file; +} + +typedef struct +{ + GCancellable *cancellable; + NautilusWindowSlot *slot; +} MountNotMountedData; + +static void +mount_not_mounted_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusWindowSlotPrivate *priv; + MountNotMountedData *data; + NautilusWindowSlot *self; + GError *error; + GCancellable *cancellable; + + data = user_data; + self = data->slot; + priv = nautilus_window_slot_get_instance_private (self); + cancellable = data->cancellable; + g_free (data); + + if (g_cancellable_is_cancelled (cancellable)) + { + /* Cancelled, don't call back */ + g_object_unref (cancellable); + return; + } + + priv->mount_cancellable = NULL; + + priv->determine_view_file = nautilus_file_get (priv->pending_location); + + error = NULL; + if (!g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error)) + { + priv->mount_error = error; + got_file_info_for_view_selection_callback (priv->determine_view_file, self); + priv->mount_error = NULL; + g_error_free (error); + } + else + { + nautilus_file_invalidate_all_attributes (priv->determine_view_file); + nautilus_file_call_when_ready (priv->determine_view_file, + NAUTILUS_FILE_ATTRIBUTE_INFO | + NAUTILUS_FILE_ATTRIBUTE_MOUNT, + got_file_info_for_view_selection_callback, + self); + } + + g_object_unref (cancellable); +} + +static void +nautilus_window_slot_display_view_selection_failure (NautilusWindow *window, + NautilusFile *file, + GFile *location, + GError *error) +{ + char *error_message; + char *detail_message; + char *scheme_string; + + /* Some sort of failure occurred. How 'bout we tell the user? */ + + error_message = g_strdup (_("Oops! Something went wrong.")); + detail_message = NULL; + if (error == NULL) + { + if (nautilus_file_is_directory (file)) + { + detail_message = g_strdup (_("Unable to display the contents of this folder.")); + } + else + { + detail_message = g_strdup (_("This location doesn’t appear to be a folder.")); + } + } + else if (error->domain == G_IO_ERROR) + { + switch (error->code) + { + case G_IO_ERROR_NOT_FOUND: + { + detail_message = g_strdup (_("Unable to find the requested file. Please check the spelling and try again.")); + } + break; + + case G_IO_ERROR_NOT_SUPPORTED: + { + scheme_string = g_file_get_uri_scheme (location); + if (scheme_string != NULL) + { + detail_message = g_strdup_printf (_("“%s” locations are not supported."), + scheme_string); + } + else + { + detail_message = g_strdup (_("Unable to handle this kind of location.")); + } + g_free (scheme_string); + } + break; + + case G_IO_ERROR_NOT_MOUNTED: + { + detail_message = g_strdup (_("Unable to access the requested location.")); + } + break; + + case G_IO_ERROR_PERMISSION_DENIED: + { + detail_message = g_strdup (_("Don’t have permission to access the requested location.")); + } + break; + + case G_IO_ERROR_HOST_NOT_FOUND: + { + /* This case can be hit for user-typed strings like "foo" due to + * the code that guesses web addresses when there's no initial "/". + * But this case is also hit for legitimate web addresses when + * the proxy is set up wrong. + */ + detail_message = g_strdup (_("Unable to find the requested location. Please check the spelling or the network settings.")); + } + break; + + case G_IO_ERROR_CONNECTION_REFUSED: + { + /* This case can be hit when server application is not installed + * or is inactive in the system user is trying to connect to. + */ + detail_message = g_strdup (_("The server has refused the connection. Typically this means that the firewall is blocking access or that the remote service is not running.")); + } + break; + + case G_IO_ERROR_CANCELLED: + case G_IO_ERROR_FAILED_HANDLED: + { + goto done; + } + + default: + { + } + break; + } + } + + if (detail_message == NULL) + { + detail_message = g_strdup_printf (_("Unhandled error message: %s"), error->message); + } + + show_dialog (error_message, detail_message, GTK_WINDOW (window), GTK_MESSAGE_ERROR); + +done: + g_free (error_message); + g_free (detail_message); +} + +/* FIXME: This works in the folowwing way. begin_location_change tries to get the + * information of the file directly. + * If the nautilus file finds that there is an error trying to get its + * information and the error match that the file is not mounted, it sets an + * internal attribute with the error then we try to mount it here. + * + * However, files are cached, and if the file doesn't get finalized in a location + * change, because needs to be in the navigation history or is a bookmark, and the + * file is not the root of the mount point, which is tracked by a volume monitor, + * and it gets unmounted aftwerwards, the file doesn't realize it's unmounted, and + * therefore this trick to open an unmounted file will fail the next time the user + * tries to open. + * For that, we need to always invalidate the file attributes when a location is + * changed, which is done in check_force_reload. + * A better way would be to make sure any children of the mounted root gets + * akwnoledge by it either by adding a reference to its parent volume monitor + * or with another solution. */ +static gboolean +handle_mount_if_needed (NautilusWindowSlot *self, + NautilusFile *file) +{ + NautilusWindowSlotPrivate *priv; + NautilusWindow *window; + GMountOperation *mount_op; + MountNotMountedData *data; + GFile *location; + GError *error = NULL; + gboolean needs_mount_handling = FALSE; + + priv = nautilus_window_slot_get_instance_private (self); + window = nautilus_window_slot_get_window (self); + if (priv->mount_error) + { + error = g_error_copy (priv->mount_error); + } + else if (nautilus_file_get_file_info_error (file) != NULL) + { + error = g_error_copy (nautilus_file_get_file_info_error (file)); + } + + if (error && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED && + !priv->tried_mount) + { + priv->tried_mount = TRUE; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (window)); + g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION); + location = nautilus_file_get_location (file); + data = g_new0 (MountNotMountedData, 1); + data->cancellable = g_cancellable_new (); + data->slot = self; + priv->mount_cancellable = data->cancellable; + g_file_mount_enclosing_volume (location, 0, mount_op, priv->mount_cancellable, + mount_not_mounted_callback, data); + g_object_unref (location); + g_object_unref (mount_op); + + needs_mount_handling = TRUE; + } + + g_clear_error (&error); + + return needs_mount_handling; +} + +static gboolean +handle_regular_file_if_needed (NautilusWindowSlot *self, + NautilusFile *file) +{ + NautilusFile *parent_file; + gboolean needs_regular_file_handling = FALSE; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + parent_file = nautilus_file_get_parent (file); + if ((parent_file != NULL) && + nautilus_file_get_file_type (file) == G_FILE_TYPE_REGULAR) + { + g_clear_pointer (&priv->pending_selection, nautilus_file_list_free); + g_clear_object (&priv->pending_location); + g_clear_object (&priv->pending_file_to_activate); + g_free (priv->pending_scroll_to); + + priv->pending_location = nautilus_file_get_parent_location (file); + if (nautilus_file_is_archive (file)) + { + priv->pending_file_to_activate = nautilus_file_ref (file); + } + else + { + priv->pending_selection = g_list_prepend (NULL, nautilus_file_ref (file)); + } + priv->determine_view_file = nautilus_file_ref (parent_file); + priv->pending_scroll_to = nautilus_file_get_uri (file); + + nautilus_file_invalidate_all_attributes (priv->determine_view_file); + nautilus_file_call_when_ready (priv->determine_view_file, + NAUTILUS_FILE_ATTRIBUTE_INFO | + NAUTILUS_FILE_ATTRIBUTE_MOUNT, + got_file_info_for_view_selection_callback, + self); + + needs_regular_file_handling = TRUE; + } + + nautilus_file_unref (parent_file); + + return needs_regular_file_handling; +} + +static void +got_file_info_for_view_selection_callback (NautilusFile *file, + gpointer callback_data) +{ + NautilusWindowSlotPrivate *priv; + GError *error = NULL; + NautilusWindow *window; + NautilusWindowSlot *self; + NautilusFile *viewed_file; + NautilusView *view; + GFile *location; + NautilusApplication *app; + + self = callback_data; + priv = nautilus_window_slot_get_instance_private (self); + window = nautilus_window_slot_get_window (self); + + g_assert (priv->determine_view_file == file); + priv->determine_view_file = NULL; + + nautilus_profile_start (NULL); + + if (handle_mount_if_needed (self, file)) + { + goto done; + } + + if (handle_regular_file_if_needed (self, file)) + { + goto done; + } + + if (priv->mount_error) + { + error = g_error_copy (priv->mount_error); + } + else if (nautilus_file_get_file_info_error (file) != NULL) + { + error = g_error_copy (nautilus_file_get_file_info_error (file)); + } + + location = priv->pending_location; + + /* desktop and other-locations GFile operations report G_IO_ERROR_NOT_SUPPORTED, + * but it's not an actual error for Nautilus */ + if (!error || g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + { + view = nautilus_window_slot_get_view_for_location (self, location); + setup_view (self, view); + } + else + { + if (error == NULL) + { + error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Unable to load location")); + } + nautilus_window_slot_display_view_selection_failure (window, + file, + location, + error); + + if (!gtk_widget_get_visible (GTK_WIDGET (window))) + { + /* Destroy never-had-a-chance-to-be-seen window. This case + * happens when a new window cannot display its initial URI. + */ + /* if this is the only window, we don't want to quit, so we redirect it to home */ + + app = NAUTILUS_APPLICATION (g_application_get_default ()); + + if (g_list_length (nautilus_application_get_windows (app)) == 1) + { + /* the user could have typed in a home directory that doesn't exist, + * in which case going home would cause an infinite loop, so we + * better test for that */ + + if (!nautilus_is_root_directory (location)) + { + if (!nautilus_is_home_directory (location)) + { + nautilus_window_slot_go_home (self, FALSE); + } + else + { + GFile *root; + + root = g_file_new_for_path ("/"); + /* the last fallback is to go to a known place that can't be deleted! */ + nautilus_window_slot_open_location_full (self, location, 0, NULL); + g_object_unref (root); + } + } + else + { + gtk_widget_destroy (GTK_WIDGET (window)); + } + } + else + { + /* Since this is a window, destroying it will also unref it. */ + gtk_widget_destroy (GTK_WIDGET (window)); + } + } + else + { + GFile *slot_location; + + /* Clean up state of already-showing window */ + end_location_change (self); + slot_location = nautilus_window_slot_get_location (self); + + /* XXX FIXME VOODOO TODO: + * Context: https://gitlab.gnome.org/GNOME/nautilus/issues/562 + * (and the associated MR) + * + * This used to just close the slot, which, in combination with + * the transient error dialog, caused Mutter to have a heart attack + * and die when the slot happened to be the only one remaining. + * The following condition can hold true in (at least) two cases: + * + * 1. We are inside the “Other Locations” view and are opening + * a broken bookmark, which causes the window slot to get replaced + * with one that handles the location, and is, understandably, + * empty. + * 2. We open a broken bookmark in a new window, which works almost + * the same, in that it has no open location. + * + * Ernestas: I’m leaning towards having an in-view message about the + * failure, which avoids dialogs and magically disappearing + * slots/tabs/windows (also allowing to go back to the + * previous location), but a dialog is quicker to inform + * about the failure. + * XXX + */ + if (slot_location == NULL) + { + nautilus_window_slot_go_home (self, 0); + } + else + { + /* We disconnected this, so we need to re-connect it */ + viewed_file = nautilus_file_get (slot_location); + nautilus_window_slot_set_viewed_file (self, viewed_file); + nautilus_file_unref (viewed_file); + + /* Leave the location bar showing the bad location that the user + * typed (or maybe achieved by dragging or something). Many times + * the mistake will just be an easily-correctable typo. The user + * can choose "Refresh" to get the original URI back in the location bar. + */ + } + } + } + +done: + g_clear_error (&error); + + nautilus_file_unref (file); + nautilus_profile_end (NULL); +} + +/* Load a view into the window, either reusing the old one or creating + * a new one. This happens when you want to load a new location, or just + * switch to a different view. + * If pending_location is set we're loading a new location and + * pending_location/selection will be used. If not, we're just switching + * view, and the current location will be used. + */ +static gboolean +setup_view (NautilusWindowSlot *self, + NautilusView *view) +{ + gboolean ret = TRUE; + GFile *old_location; + NautilusWindowSlotPrivate *priv; + + nautilus_profile_start (NULL); + + priv = nautilus_window_slot_get_instance_private (self); + nautilus_window_slot_disconnect_content_view (self); + + priv->new_content_view = view; + + nautilus_window_slot_connect_new_content_view (self); + + /* Forward search selection and state before loading the new model */ + old_location = priv->content_view ? nautilus_view_get_location (priv->content_view) : NULL; + + /* Actually load the pending location and selection: */ + if (priv->pending_location != NULL) + { + load_new_location (self, + priv->pending_location, + priv->pending_selection, + priv->pending_file_to_activate, + FALSE, + TRUE); + + nautilus_file_list_free (priv->pending_selection); + priv->pending_selection = NULL; + } + else if (old_location != NULL) + { + g_autolist (NautilusFile) selection = NULL; + + selection = nautilus_view_get_selection (priv->content_view); + + load_new_location (self, + old_location, + selection, + NULL, + FALSE, + TRUE); + } + else + { + ret = FALSE; + goto out; + } + + change_view (self); + gtk_widget_show (GTK_WIDGET (priv->window)); + +out: + nautilus_profile_end (NULL); + + return ret; +} + +static void +load_new_location (NautilusWindowSlot *self, + GFile *location, + GList *selection, + NautilusFile *file_to_activate, + gboolean tell_current_content_view, + gboolean tell_new_content_view) +{ + NautilusView *view; + NautilusWindowSlotPrivate *priv; + + g_assert (self != NULL); + g_assert (location != NULL); + + view = NULL; + priv = nautilus_window_slot_get_instance_private (self); + + nautilus_profile_start (NULL); + /* Note, these may recurse into report_load_underway */ + if (priv->content_view != NULL && tell_current_content_view) + { + view = priv->content_view; + nautilus_view_set_location (priv->content_view, location); + } + + if (priv->new_content_view != NULL && tell_new_content_view && + (!tell_current_content_view || + priv->new_content_view != priv->content_view)) + { + view = priv->new_content_view; + nautilus_view_set_location (priv->new_content_view, location); + } + if (view) + { + nautilus_view_set_selection (view, selection); + if (file_to_activate != NULL) + { + g_autoptr (GAppInfo) app_info = NULL; + const gchar *app_id; + + g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view)); + app_info = nautilus_mime_get_default_application_for_file (file_to_activate); + app_id = g_app_info_get_id (app_info); + if (g_strcmp0 (app_id, NAUTILUS_DESKTOP_ID) == 0) + { + nautilus_files_view_activate_file (NAUTILUS_FILES_VIEW (view), + file_to_activate, 0); + } + } + } + + nautilus_profile_end (NULL); +} + +static void +end_location_change (NautilusWindowSlot *self) +{ + char *uri; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + uri = nautilus_window_slot_get_location_uri (self); + if (uri) + { + DEBUG ("Finished loading window for uri %s", uri); + g_free (uri); + } + + nautilus_window_slot_set_allow_stop (self, FALSE); + + /* Now we can free details->pending_scroll_to, since the load_complete + * callback already has been emitted. + */ + g_free (priv->pending_scroll_to); + priv->pending_scroll_to = NULL; + + free_location_change (self); +} + +static void +free_location_change (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + g_clear_object (&priv->pending_location); + g_clear_object (&priv->pending_file_to_activate); + nautilus_file_list_free (priv->pending_selection); + priv->pending_selection = NULL; + + /* Don't free details->pending_scroll_to, since thats needed until + * the load_complete callback. + */ + + if (priv->mount_cancellable != NULL) + { + g_cancellable_cancel (priv->mount_cancellable); + priv->mount_cancellable = NULL; + } + + if (priv->determine_view_file != NULL) + { + nautilus_file_cancel_call_when_ready + (priv->determine_view_file, + got_file_info_for_view_selection_callback, self); + priv->determine_view_file = NULL; + } +} + +/* This sets up a new view, for the current location, with the provided id. Used + * whenever the user changes the type of view to use. + * + * Note that the current view will be thrown away, even if it has the same id. + * Callers may first check if !nautilus_window_slot_content_view_matches(). + */ +static void +nautilus_window_slot_set_content_view (NautilusWindowSlot *self, + guint id) +{ + NautilusFilesView *view; + g_autolist (NautilusFile) selection = NULL; + char *uri; + NautilusWindowSlotPrivate *priv; + + g_assert (self != NULL); + + priv = nautilus_window_slot_get_instance_private (self); + uri = nautilus_window_slot_get_location_uri (self); + DEBUG ("Change view of window %s to %d", uri, id); + g_free (uri); + + selection = nautilus_view_get_selection (priv->content_view); + view = nautilus_files_view_new (id, self); + + nautilus_window_slot_stop_loading (self); + + nautilus_window_slot_set_allow_stop (self, TRUE); + + if (g_list_length (selection) == 0 && NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + /* If there is no selection, queue a scroll to the same icon that + * is currently visible */ + priv->pending_scroll_to = nautilus_files_view_get_first_visible_file (NAUTILUS_FILES_VIEW (priv->content_view)); + } + + priv->location_change_type = NAUTILUS_LOCATION_CHANGE_RELOAD; + + if (!setup_view (self, NAUTILUS_VIEW (view))) + { + /* Just load the homedir. */ + nautilus_window_slot_go_home (self, FALSE); + } +} + +void +nautilus_window_slot_back_or_forward (NautilusWindowSlot *self, + gboolean back, + guint distance, + NautilusWindowOpenFlags flags) +{ + GList *list; + GFile *location; + guint len; + NautilusBookmark *bookmark; + GFile *old_location; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + list = back ? priv->back_list : priv->forward_list; + + len = (guint) g_list_length (list); + + /* If we can't move in the direction at all, just return. */ + if (len == 0) + { + return; + } + + /* If the distance to move is off the end of the list, go to the end + * of the list. */ + if (distance >= len) + { + distance = len - 1; + } + + bookmark = g_list_nth_data (list, distance); + location = nautilus_bookmark_get_location (bookmark); + + if (flags != 0) + { + nautilus_window_slot_open_location_full (self, location, flags, NULL); + } + else + { + char *scroll_pos; + + old_location = nautilus_window_slot_get_location (self); + scroll_pos = nautilus_bookmark_get_scroll_pos (bookmark); + begin_location_change + (self, + location, old_location, NULL, + back ? NAUTILUS_LOCATION_CHANGE_BACK : NAUTILUS_LOCATION_CHANGE_FORWARD, + distance, + scroll_pos); + + g_free (scroll_pos); + } + + g_object_unref (location); +} + +/* reload the contents of the window */ +static void +nautilus_window_slot_force_reload (NautilusWindowSlot *self) +{ + GFile *location; + char *current_pos; + NautilusWindowSlotPrivate *priv; + g_autolist (NautilusFile) selection = NULL; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + location = nautilus_window_slot_get_location (self); + if (location == NULL) + { + return; + } + + /* peek_slot_field (window, location) can be free'd during the processing + * of begin_location_change, so make a copy + */ + g_object_ref (location); + current_pos = NULL; + + if (priv->new_content_view) + { + selection = nautilus_view_get_selection (priv->content_view); + + if (NAUTILUS_IS_FILES_VIEW (priv->new_content_view)) + { + current_pos = nautilus_files_view_get_first_visible_file (NAUTILUS_FILES_VIEW (priv->content_view)); + } + } + begin_location_change + (self, location, location, selection, + NAUTILUS_LOCATION_CHANGE_RELOAD, 0, current_pos); + g_free (current_pos); + g_object_unref (location); +} + +void +nautilus_window_slot_queue_reload (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + if (nautilus_window_slot_get_location (self) == NULL) + { + return; + } + + if (priv->pending_location != NULL + || priv->content_view == NULL + || nautilus_view_is_loading (priv->content_view)) + { + /* there is a reload in flight */ + priv->needs_reload = TRUE; + return; + } + + nautilus_window_slot_force_reload (self); +} + +static void +nautilus_window_slot_clear_forward_list (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + g_list_free_full (priv->forward_list, g_object_unref); + priv->forward_list = NULL; +} + +static void +nautilus_window_slot_clear_back_list (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + g_list_free_full (priv->back_list, g_object_unref); + priv->back_list = NULL; +} + +static void +nautilus_window_slot_update_bookmark (NautilusWindowSlot *self, + NautilusFile *file) +{ + gboolean recreate; + GFile *new_location; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + new_location = nautilus_file_get_location (file); + + if (priv->current_location_bookmark == NULL) + { + recreate = TRUE; + } + else + { + GFile *bookmark_location; + bookmark_location = nautilus_bookmark_get_location (priv->current_location_bookmark); + recreate = !g_file_equal (bookmark_location, new_location); + g_object_unref (bookmark_location); + } + + if (recreate) + { + char *display_name = NULL; + + /* We've changed locations, must recreate bookmark for current location. */ + g_clear_object (&priv->last_location_bookmark); + priv->last_location_bookmark = priv->current_location_bookmark; + + display_name = nautilus_file_get_display_name (file); + priv->current_location_bookmark = nautilus_bookmark_new (new_location, display_name); + g_free (display_name); + } + + g_object_unref (new_location); +} + +static void +check_bookmark_location_matches (NautilusBookmark *bookmark, + GFile *location) +{ + GFile *bookmark_location; + char *bookmark_uri, *uri; + + bookmark_location = nautilus_bookmark_get_location (bookmark); + if (!g_file_equal (location, bookmark_location)) + { + bookmark_uri = g_file_get_uri (bookmark_location); + uri = g_file_get_uri (location); + g_warning ("bookmark uri is %s, but expected %s", bookmark_uri, uri); + g_free (uri); + g_free (bookmark_uri); + } + g_object_unref (bookmark_location); +} + +/* Debugging function used to verify that the last_location_bookmark + * is in the state we expect when we're about to use it to update the + * Back or Forward list. + */ +static void +check_last_bookmark_location_matches_slot (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + check_bookmark_location_matches (priv->last_location_bookmark, + nautilus_window_slot_get_location (self)); +} + +static void +handle_go_direction (NautilusWindowSlot *self, + GFile *location, + gboolean forward) +{ + GList **list_ptr, **other_list_ptr; + GList *list, *other_list, *link; + NautilusBookmark *bookmark; + gint i; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + list_ptr = (forward) ? (&priv->forward_list) : (&priv->back_list); + other_list_ptr = (forward) ? (&priv->back_list) : (&priv->forward_list); + list = *list_ptr; + other_list = *other_list_ptr; + + /* Move items from the list to the other list. */ + g_assert (g_list_length (list) > priv->location_change_distance); + check_bookmark_location_matches (g_list_nth_data (list, priv->location_change_distance), + location); + g_assert (nautilus_window_slot_get_location (self) != NULL); + + /* Move current location to list */ + check_last_bookmark_location_matches_slot (self); + + /* Use the first bookmark in the history list rather than creating a new one. */ + other_list = g_list_prepend (other_list, priv->last_location_bookmark); + g_object_ref (other_list->data); + + /* Move extra links from the list to the other list */ + for (i = 0; i < priv->location_change_distance; ++i) + { + bookmark = NAUTILUS_BOOKMARK (list->data); + list = g_list_remove (list, bookmark); + other_list = g_list_prepend (other_list, bookmark); + } + + /* One bookmark falls out of back/forward lists and becomes viewed location */ + link = list; + list = g_list_remove_link (list, link); + g_object_unref (link->data); + g_list_free_1 (link); + + *list_ptr = list; + *other_list_ptr = other_list; +} + +static void +handle_go_elsewhere (NautilusWindowSlot *self, + GFile *location) +{ + GFile *slot_location; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + /* Clobber the entire forward list, and move displayed location to back list */ + nautilus_window_slot_clear_forward_list (self); + + slot_location = nautilus_window_slot_get_location (self); + + if (slot_location != NULL) + { + /* If we're returning to the same uri somehow, don't put this uri on back list. + * This also avoids a problem where set_displayed_location + * didn't update last_location_bookmark since the uri didn't change. + */ + if (!g_file_equal (slot_location, location)) + { + /* Store bookmark for current location in back list, unless there is no current location */ + check_last_bookmark_location_matches_slot (self); + /* Use the first bookmark in the history list rather than creating a new one. */ + priv->back_list = g_list_prepend (priv->back_list, + priv->last_location_bookmark); + g_object_ref (priv->back_list->data); + } + } +} + +static void +update_history (NautilusWindowSlot *self, + NautilusLocationChangeType type, + GFile *new_location) +{ + switch (type) + { + case NAUTILUS_LOCATION_CHANGE_STANDARD: + { + handle_go_elsewhere (self, new_location); + return; + } + + case NAUTILUS_LOCATION_CHANGE_RELOAD: + { + /* for reload there is no work to do */ + return; + } + + case NAUTILUS_LOCATION_CHANGE_BACK: + { + handle_go_direction (self, new_location, FALSE); + return; + } + + case NAUTILUS_LOCATION_CHANGE_FORWARD: + handle_go_direction (self, new_location, TRUE); + return; + } + g_return_if_fail (FALSE); +} + +typedef struct +{ + NautilusWindowSlot *slot; + GCancellable *cancellable; + GMount *mount; +} FindMountData; + +static void +nautilus_window_slot_show_x_content_bar (NautilusWindowSlot *self, + GMount *mount, + const char * const *x_content_types) +{ + GtkWidget *bar; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + if (!should_handle_content_types (x_content_types)) + { + return; + } + + bar = nautilus_x_content_bar_new (mount, x_content_types); + gtk_widget_show (bar); + nautilus_window_slot_add_extra_location_widget (self, bar); +} + +static void +found_content_type_cb (const char **x_content_types, + gpointer user_data) +{ + NautilusWindowSlot *self; + FindMountData *data = user_data; + NautilusWindowSlotPrivate *priv; + + self = data->slot; + priv = nautilus_window_slot_get_instance_private (self); + + if (g_cancellable_is_cancelled (data->cancellable)) + { + goto out; + } + + + if (x_content_types != NULL && x_content_types[0] != NULL) + { + nautilus_window_slot_show_x_content_bar (self, data->mount, (const char * const *) x_content_types); + } + + priv->find_mount_cancellable = NULL; + +out: + g_object_unref (data->mount); + g_object_unref (data->cancellable); + g_free (data); +} + +static void +found_mount_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + FindMountData *data = user_data; + NautilusWindowSlot *self; + GMount *mount; + NautilusWindowSlotPrivate *priv; + + self = NAUTILUS_WINDOW_SLOT (data->slot); + priv = nautilus_window_slot_get_instance_private (self); + if (g_cancellable_is_cancelled (data->cancellable)) + { + goto out; + } + + mount = g_file_find_enclosing_mount_finish (G_FILE (source_object), + res, + NULL); + if (mount != NULL) + { + data->mount = mount; + nautilus_get_x_content_types_for_mount_async (mount, + found_content_type_cb, + data->cancellable, + data); + return; + } + + priv->find_mount_cancellable = NULL; + +out: + g_object_unref (data->cancellable); + g_free (data); +} + +static void +trash_state_changed_cb (NautilusTrashMonitor *monitor, + gboolean is_empty, + gpointer user_data) +{ + GFile *location; + NautilusDirectory *directory; + NautilusView *view; + + location = nautilus_window_slot_get_current_location (user_data); + view = nautilus_window_slot_get_current_view (user_data); + + /* The signal 'trash-state-changed' could be emitted by NautilusTrashMonitor + * while a NautilusWindowSlot is still initializing the content view. + */ + if (location == NULL || view == NULL) + { + return; + } + + directory = nautilus_directory_get (location); + + if (nautilus_directory_is_in_trash (directory)) + { + if (nautilus_trash_monitor_is_empty ()) + { + nautilus_window_slot_remove_extra_location_widgets (user_data); + } + else + { + nautilus_window_slot_setup_extra_location_widgets (user_data); + } + } +} + +static void +nautilus_window_slot_show_trash_bar (NautilusWindowSlot *self) +{ + GtkWidget *bar; + NautilusView *view; + + view = nautilus_window_slot_get_current_view (self); + bar = nautilus_trash_bar_new (NAUTILUS_FILES_VIEW (view)); + gtk_widget_show (bar); + + nautilus_window_slot_add_extra_location_widget (self, bar); +} + +static void +nautilus_window_slot_show_special_location_bar (NautilusWindowSlot *self, + NautilusSpecialLocation special_location) +{ + GtkWidget *bar; + + bar = nautilus_special_location_bar_new (special_location); + gtk_widget_show (bar); + + nautilus_window_slot_add_extra_location_widget (self, bar); +} + +static void +slot_add_extension_extra_widgets (NautilusWindowSlot *self) +{ + GList *providers, *l; + GtkWidget *widget; + char *uri; + NautilusWindow *window; + + providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_LOCATION_WIDGET_PROVIDER); + window = nautilus_window_slot_get_window (self); + + uri = nautilus_window_slot_get_location_uri (self); + for (l = providers; l != NULL; l = l->next) + { + NautilusLocationWidgetProvider *provider; + + provider = NAUTILUS_LOCATION_WIDGET_PROVIDER (l->data); + widget = nautilus_location_widget_provider_get_widget (provider, uri, GTK_WIDGET (window)); + if (widget != NULL) + { + nautilus_window_slot_add_extra_location_widget (self, widget); + } + } + g_free (uri); + + nautilus_module_extension_list_free (providers); +} + +static void +nautilus_window_slot_update_for_new_location (NautilusWindowSlot *self) +{ + GFile *new_location; + NautilusFile *file; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + new_location = priv->pending_location; + priv->pending_location = NULL; + + file = nautilus_file_get (new_location); + nautilus_window_slot_update_bookmark (self, file); + + update_history (self, priv->location_change_type, new_location); + + /* Create a NautilusFile for this location, so we can catch it + * if it goes away. + */ + nautilus_window_slot_set_viewed_file (self, file); + priv->viewed_file_seen = !nautilus_file_is_not_yet_confirmed (file); + priv->viewed_file_in_trash = nautilus_file_is_in_trash (file); + nautilus_file_unref (file); + + nautilus_window_slot_set_location (self, new_location); + + /* Sync the actions for this new location. */ + nautilus_window_slot_sync_actions (self); +} + +static void +view_started_loading (NautilusWindowSlot *self, + NautilusView *view) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (view == priv->content_view) + { + nautilus_window_slot_set_allow_stop (self, TRUE); + } + + /* Only grab focus if the menu isn't showing. Otherwise the menu disappears + * e.g. when the user toggles Show Hidden Files + */ + if (!nautilus_toolbar_is_menu_visible (NAUTILUS_TOOLBAR (nautilus_window_get_toolbar (priv->window)))) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->window)); + } + + gtk_widget_show (GTK_WIDGET (priv->window)); + + nautilus_window_slot_set_loading (self, TRUE); +} + +static void +view_ended_loading (NautilusWindowSlot *self, + NautilusView *view) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (view == priv->content_view) + { + if (NAUTILUS_IS_FILES_VIEW (view) && priv->pending_scroll_to != NULL) + { + nautilus_files_view_scroll_to_file (NAUTILUS_FILES_VIEW (priv->content_view), priv->pending_scroll_to); + } + + end_location_change (self); + } + + if (priv->needs_reload) + { + nautilus_window_slot_queue_reload (self); + priv->needs_reload = FALSE; + } + + nautilus_window_slot_set_allow_stop (self, FALSE); + + nautilus_window_slot_set_loading (self, FALSE); +} + +static void +view_is_loading_changed_cb (GObject *object, + GParamSpec *pspec, + NautilusWindowSlot *self) +{ + NautilusView *view; + + view = NAUTILUS_VIEW (object); + + nautilus_profile_start (NULL); + + if (nautilus_view_is_loading (view)) + { + view_started_loading (self, view); + } + else + { + view_ended_loading (self, view); + } + + nautilus_profile_end (NULL); +} + +static void +nautilus_window_slot_setup_extra_location_widgets (NautilusWindowSlot *self) +{ + GFile *location; + FindMountData *data; + NautilusDirectory *directory; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + location = nautilus_window_slot_get_current_location (self); + + if (location == NULL) + { + return; + } + + directory = nautilus_directory_get (location); + + if (nautilus_directory_is_in_trash (directory)) + { + if (!nautilus_trash_monitor_is_empty ()) + { + nautilus_window_slot_show_trash_bar (self); + } + } + else + { + NautilusFile *file; + GFile *scripts_file; + char *scripts_path = nautilus_get_scripts_directory_path (); + + scripts_file = g_file_new_for_path (scripts_path); + g_free (scripts_path); + + file = nautilus_file_get (location); + + if (nautilus_should_use_templates_directory () && + nautilus_file_is_user_special_directory (file, G_USER_DIRECTORY_TEMPLATES)) + { + nautilus_window_slot_show_special_location_bar (self, NAUTILUS_SPECIAL_LOCATION_TEMPLATES); + } + else if (g_file_equal (location, scripts_file)) + { + nautilus_window_slot_show_special_location_bar (self, NAUTILUS_SPECIAL_LOCATION_SCRIPTS); + } + + g_object_unref (scripts_file); + nautilus_file_unref (file); + } + + /* need the mount to determine if we should put up the x-content cluebar */ + if (priv->find_mount_cancellable != NULL) + { + g_cancellable_cancel (priv->find_mount_cancellable); + priv->find_mount_cancellable = NULL; + } + + data = g_new (FindMountData, 1); + data->slot = self; + data->cancellable = g_cancellable_new (); + data->mount = NULL; + + priv->find_mount_cancellable = data->cancellable; + g_file_find_enclosing_mount_async (location, + G_PRIORITY_DEFAULT, + data->cancellable, + found_mount_cb, + data); + + nautilus_directory_unref (directory); + + slot_add_extension_extra_widgets (self); +} + +static void +nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->new_content_view) + { + g_signal_connect (priv->new_content_view, + "notify::loading", + G_CALLBACK (view_is_loading_changed_cb), + self); + } +} + +static void +nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view) + { + /* disconnect old view */ + g_signal_handlers_disconnect_by_func (priv->content_view, + G_CALLBACK (view_is_loading_changed_cb), + self); + } +} + +static void +nautilus_window_slot_switch_new_content_view (NautilusWindowSlot *self) +{ + GtkWidget *widget; + gboolean reusing_view; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + reusing_view = priv->new_content_view && + gtk_widget_get_parent (GTK_WIDGET (priv->new_content_view)) != NULL; + /* We are either reusing the view, so new_content_view and content_view + * are the same, or the new_content_view is invalid */ + if (priv->new_content_view == NULL || reusing_view) + { + goto done; + } + + if (priv->content_view != NULL) + { + g_binding_unbind (priv->searching_binding); + g_binding_unbind (priv->selection_binding); + g_binding_unbind (priv->extensions_background_menu_binding); + g_binding_unbind (priv->templates_menu_binding); + widget = GTK_WIDGET (priv->content_view); + gtk_widget_destroy (widget); + g_clear_object (&priv->content_view); + } + + if (priv->new_content_view != NULL) + { + priv->content_view = priv->new_content_view; + priv->new_content_view = NULL; + + widget = GTK_WIDGET (priv->content_view); + gtk_container_add (GTK_CONTAINER (self), widget); + gtk_widget_set_vexpand (widget, TRUE); + gtk_widget_show (widget); + priv->searching_binding = g_object_bind_property (priv->content_view, "searching", + self, "searching", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + priv->selection_binding = g_object_bind_property (priv->content_view, "selection", + self, "selection", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + priv->extensions_background_menu_binding = g_object_bind_property (priv->content_view, "extensions-background-menu", + self, "extensions-background-menu", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + priv->templates_menu_binding = g_object_bind_property (priv->content_view, "templates-menu", + self, "templates-menu", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ICON]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOOLBAR_MENU_SECTIONS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXTENSIONS_BACKGROUND_MENU]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TEMPLATES_MENU]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOOLTIP]); + } + +done: + /* Clean up, so we don't confuse having a new_content_view available or + * just that we didn't care about it here */ + priv->new_content_view = NULL; +} + +/* This is called when we have decided we can actually change to the new view/location situation. */ +static void +change_view (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + /* Switch to the new content view. + * Destroy the extra location widgets first, since they might hold + * a pointer to the old view, which will possibly be destroyed inside + * nautilus_window_slot_switch_new_content_view(). + */ + nautilus_window_slot_remove_extra_location_widgets (self); + nautilus_window_slot_switch_new_content_view (self); + + if (priv->pending_location != NULL) + { + /* Tell the window we are finished. */ + nautilus_window_slot_update_for_new_location (self); + } + + /* Now that we finished switching to the new location, + * add back the extra location widgets. + */ + nautilus_window_slot_setup_extra_location_widgets (self); +} + +static void +nautilus_window_slot_dispose (GObject *object) +{ + NautilusWindowSlot *self; + NautilusWindowSlotPrivate *priv; + + self = NAUTILUS_WINDOW_SLOT (object); + priv = nautilus_window_slot_get_instance_private (self); + + g_signal_handlers_disconnect_by_data (nautilus_trash_monitor_get (), self); + + g_signal_handlers_disconnect_by_data (nautilus_preferences, self); + + nautilus_window_slot_clear_forward_list (self); + nautilus_window_slot_clear_back_list (self); + + nautilus_window_slot_remove_extra_location_widgets (self); + + g_clear_pointer (&priv->searching_binding, g_binding_unbind); + g_clear_pointer (&priv->selection_binding, g_binding_unbind); + g_clear_pointer (&priv->extensions_background_menu_binding, g_binding_unbind); + g_clear_pointer (&priv->templates_menu_binding, g_binding_unbind); + + if (priv->content_view) + { + gtk_widget_destroy (GTK_WIDGET (priv->content_view)); + g_clear_object (&priv->content_view); + } + + if (priv->new_content_view) + { + gtk_widget_destroy (GTK_WIDGET (priv->new_content_view)); + g_clear_object (&priv->new_content_view); + } + + nautilus_window_slot_set_viewed_file (self, NULL); + + g_clear_object (&priv->location); + g_clear_object (&priv->pending_file_to_activate); + g_clear_pointer (&priv->pending_selection, nautilus_file_list_free); + + g_clear_object (&priv->current_location_bookmark); + g_clear_object (&priv->last_location_bookmark); + g_clear_object (&priv->slot_action_group); + g_clear_object (&priv->pending_search_query); + + g_clear_pointer (&priv->find_mount_cancellable, g_cancellable_cancel); + + if (priv->query_editor) + { + gtk_widget_destroy (GTK_WIDGET (priv->query_editor)); + g_clear_object (&priv->query_editor); + } + + free_location_change (self); + + G_OBJECT_CLASS (nautilus_window_slot_parent_class)->dispose (object); +} + +static void +nautilus_window_slot_finalize (GObject *object) +{ + NautilusWindowSlot *self; + NautilusWindowSlotPrivate *priv; + + self = NAUTILUS_WINDOW_SLOT (object); + priv = nautilus_window_slot_get_instance_private (self); + + g_clear_pointer (&priv->title, g_free); + + G_OBJECT_CLASS (nautilus_window_slot_parent_class)->finalize (object); +} + +static void +nautilus_window_slot_grab_focus (GtkWidget *widget) +{ + NautilusWindowSlot *self; + NautilusWindowSlotPrivate *priv; + + self = NAUTILUS_WINDOW_SLOT (widget); + priv = nautilus_window_slot_get_instance_private (self); + + GTK_WIDGET_CLASS (nautilus_window_slot_parent_class)->grab_focus (widget); + + if (nautilus_window_slot_get_search_visible (self)) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->query_editor)); + } + else if (priv->content_view) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->content_view)); + } + else if (priv->new_content_view) + { + gtk_widget_grab_focus (GTK_WIDGET (priv->new_content_view)); + } +} + +static void +nautilus_window_slot_class_init (NautilusWindowSlotClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + klass->get_view_for_location = real_get_view_for_location; + klass->handles_location = real_handles_location; + + oclass->dispose = nautilus_window_slot_dispose; + oclass->finalize = nautilus_window_slot_finalize; + oclass->constructed = nautilus_window_slot_constructed; + oclass->set_property = nautilus_window_slot_set_property; + oclass->get_property = nautilus_window_slot_get_property; + + widget_class->grab_focus = nautilus_window_slot_grab_focus; + + properties[PROP_ACTIVE] = + g_param_spec_boolean ("active", + "Whether the slot is active", + "Whether the slot is the active slot of the window", + FALSE, + G_PARAM_READWRITE); + + properties[PROP_LOADING] = + g_param_spec_boolean ("loading", + "Whether the slot loading", + "Whether the slot is loading a new location", + FALSE, + G_PARAM_READABLE); + + properties[PROP_SEARCHING] = + g_param_spec_boolean ("searching", + "Whether the current view of the slot is searching", + "Whether the current view of the slot is searching. Proxy property from the view", + FALSE, + G_PARAM_READWRITE); + + properties[PROP_SELECTION] = + g_param_spec_pointer ("selection", + "Selection of the current view of the slot", + "The selection of the current view of the slot. Proxy property from the view", + G_PARAM_READWRITE); + + properties[PROP_WINDOW] = + g_param_spec_object ("window", + "The NautilusWindow", + "The NautilusWindow this slot is part of", + NAUTILUS_TYPE_WINDOW, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT); + + properties[PROP_ICON] = + g_param_spec_object ("icon", + "Icon that represents the slot", + "The icon that represents the slot", + G_TYPE_ICON, + G_PARAM_READABLE); + + properties[PROP_TOOLBAR_MENU_SECTIONS] = + g_param_spec_pointer ("toolbar-menu-sections", + "Menu sections for the toolbar menu", + "The menu sections to add to the toolbar menu for this slot", + G_PARAM_READABLE); + + properties[PROP_EXTENSIONS_BACKGROUND_MENU] = + g_param_spec_object ("extensions-background-menu", + "Background menu of extensions", + "Proxy property from the view for the background menu for extensions", + G_TYPE_MENU_MODEL, + G_PARAM_READWRITE); + + properties[PROP_TEMPLATES_MENU] = + g_param_spec_object ("templates-menu", + "Templates menu", + "Proxy property from the view for the templates menu", + G_TYPE_MENU_MODEL, + G_PARAM_READWRITE); + + properties[PROP_LOCATION] = + g_param_spec_object ("location", + "Current location visible on the slot", + "Either the location that is used currently, or the pending location. Clients will see the same value they set, and therefore it will be cosistent from clients point of view.", + G_TYPE_FILE, + G_PARAM_READWRITE); + + properties[PROP_TOOLTIP] = + g_param_spec_string ("tooltip", + "Tooltip that represents the slot", + "The tooltip that represents the slot", + NULL, + G_PARAM_READWRITE); + + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +GFile * +nautilus_window_slot_get_location (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->location; +} + +GFile * +nautilus_window_slot_get_pending_location (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->pending_location; +} + +const gchar * +nautilus_window_slot_get_title (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->title; +} + +char * +nautilus_window_slot_get_location_uri (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->location) + { + return g_file_get_uri (priv->location); + } + return NULL; +} + +NautilusWindow * +nautilus_window_slot_get_window (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->window; +} + +void +nautilus_window_slot_set_window (NautilusWindowSlot *self, + NautilusWindow *window) +{ + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + g_assert (NAUTILUS_IS_WINDOW (window)); + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->window != window) + { + priv->window = window; + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WINDOW]); + } +} + +/* nautilus_window_slot_update_title: + * + * Re-calculate the slot title. + * Called when the location or view has changed. + * @slot: The NautilusWindowSlot in question. + * + */ +void +nautilus_window_slot_update_title (NautilusWindowSlot *self) +{ + NautilusWindow *window; + char *title; + gboolean do_sync = FALSE; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + title = nautilus_compute_title_for_location (priv->location); + window = nautilus_window_slot_get_window (self); + + if (g_strcmp0 (title, priv->title) != 0) + { + do_sync = TRUE; + + g_free (priv->title); + priv->title = title; + title = NULL; + } + + if (strlen (priv->title) > 0) + { + do_sync = TRUE; + } + + if (do_sync) + { + nautilus_window_sync_title (window, self); + } + + if (title != NULL) + { + g_free (title); + } +} + +gboolean +nautilus_window_slot_get_allow_stop (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->allow_stop; +} + +void +nautilus_window_slot_set_allow_stop (NautilusWindowSlot *self, + gboolean allow) +{ + NautilusWindow *window; + NautilusWindowSlotPrivate *priv; + + g_assert (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + + priv->allow_stop = allow; + + window = nautilus_window_slot_get_window (self); + nautilus_window_sync_allow_stop (window, self); +} + +void +nautilus_window_slot_stop_loading (NautilusWindowSlot *self) +{ + GFile *location; + NautilusDirectory *directory; + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + location = nautilus_window_slot_get_location (self); + directory = nautilus_directory_get (priv->location); + + if (NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + nautilus_files_view_stop_loading (NAUTILUS_FILES_VIEW (priv->content_view)); + } + + nautilus_directory_unref (directory); + + if (priv->pending_location != NULL && + location != NULL && + priv->content_view != NULL && + NAUTILUS_IS_FILES_VIEW (priv->content_view)) + { + /* No need to tell the new view - either it is the + * same as the old view, in which case it will already + * be told, or it is the very pending change we wish + * to cancel. + */ + g_autolist (NautilusFile) selection = NULL; + + selection = nautilus_view_get_selection (priv->content_view); + load_new_location (self, + location, + selection, + NULL, + TRUE, + FALSE); + } + + end_location_change (self); + + if (priv->new_content_view) + { + g_object_unref (priv->new_content_view); + priv->new_content_view = NULL; + } +} + +NautilusView * +nautilus_window_slot_get_current_view (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view != NULL) + { + return priv->content_view; + } + else if (priv->new_content_view) + { + return priv->new_content_view; + } + + return NULL; +} + +NautilusBookmark * +nautilus_window_slot_get_bookmark (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->current_location_bookmark; +} + +GList * +nautilus_window_slot_get_back_history (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->back_list; +} + +GList * +nautilus_window_slot_get_forward_history (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->forward_list; +} + +NautilusWindowSlot * +nautilus_window_slot_new (NautilusWindow *window) +{ + return g_object_new (NAUTILUS_TYPE_WINDOW_SLOT, + "window", window, + NULL); +} + +GIcon * +nautilus_window_slot_get_icon (NautilusWindowSlot *self) +{ + guint current_view_id; + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view == NULL) + { + return NULL; + } + + current_view_id = nautilus_view_get_view_id (NAUTILUS_VIEW (priv->content_view)); + switch (current_view_id) + { + case NAUTILUS_VIEW_LIST_ID: + { + return nautilus_view_get_icon (NAUTILUS_VIEW_GRID_ID); + } + break; + + case NAUTILUS_VIEW_GRID_ID: + { + return nautilus_view_get_icon (NAUTILUS_VIEW_LIST_ID); + } + break; + + case NAUTILUS_VIEW_OTHER_LOCATIONS_ID: + { + return nautilus_view_get_icon (NAUTILUS_VIEW_OTHER_LOCATIONS_ID); + } + break; + + default: + { + return NULL; + } + } +} + +const gchar * +nautilus_window_slot_get_tooltip (NautilusWindowSlot *self) +{ + guint current_view_id; + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->content_view == NULL) + { + return NULL; + } + + current_view_id = nautilus_view_get_view_id (NAUTILUS_VIEW (priv->content_view)); + switch (current_view_id) + { + case NAUTILUS_VIEW_LIST_ID: + { + return nautilus_view_get_tooltip (NAUTILUS_VIEW_GRID_ID); + } + break; + + case NAUTILUS_VIEW_GRID_ID: + { + return nautilus_view_get_tooltip (NAUTILUS_VIEW_LIST_ID); + } + break; + + case NAUTILUS_VIEW_OTHER_LOCATIONS_ID: + { + return nautilus_view_get_tooltip (NAUTILUS_VIEW_OTHER_LOCATIONS_ID); + } + break; + + default: + { + return NULL; + } + } +} + +NautilusToolbarMenuSections * +nautilus_window_slot_get_toolbar_menu_sections (NautilusWindowSlot *self) +{ + NautilusView *view; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + view = nautilus_window_slot_get_current_view (self); + + return view ? nautilus_view_get_toolbar_menu_sections (view) : NULL; +} + +gboolean +nautilus_window_slot_get_active (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), FALSE); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->active; +} + +void +nautilus_window_slot_set_active (NautilusWindowSlot *self, + gboolean active) +{ + NautilusWindowSlotPrivate *priv; + NautilusWindow *window; + + g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + if (priv->active != active) + { + priv->active = active; + + if (active) + { + int page_num; + + priv = nautilus_window_slot_get_instance_private (self); + window = priv->window; + page_num = gtk_notebook_page_num (GTK_NOTEBOOK (nautilus_window_get_notebook (window)), + GTK_WIDGET (self)); + g_assert (page_num >= 0); + + gtk_notebook_set_current_page (GTK_NOTEBOOK (nautilus_window_get_notebook (window)), page_num); + + /* sync window to new slot */ + nautilus_window_sync_allow_stop (window, self); + nautilus_window_sync_title (window, self); + nautilus_window_sync_location_widgets (window); + nautilus_window_slot_sync_actions (self); + + gtk_widget_insert_action_group (GTK_WIDGET (window), "slot", priv->slot_action_group); + } + else + { + window = nautilus_window_slot_get_window (self); + g_assert (self == nautilus_window_get_active_slot (window)); + + gtk_widget_insert_action_group (GTK_WIDGET (window), "slot", NULL); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE]); + } +} + +static void +nautilus_window_slot_set_loading (NautilusWindowSlot *self, + gboolean loading) +{ + NautilusWindowSlotPrivate *priv; + + g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (self)); + + priv = nautilus_window_slot_get_instance_private (self); + priv->loading = loading; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADING]); +} + +gboolean +nautilus_window_slot_get_loading (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), FALSE); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->loading; +} + +NautilusQueryEditor * +nautilus_window_slot_get_query_editor (NautilusWindowSlot *self) +{ + NautilusWindowSlotPrivate *priv; + + g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (self), NULL); + + priv = nautilus_window_slot_get_instance_private (self); + + return priv->query_editor; +} + +/* + * Open the specified location and set up the navigation history including the + * back and forward lists. This function is intended to be called when switching + * between NautilusWindowSlot and NautilusOtherLocationsWindowSlot. It allows + * the navigation history accumulated in the slot being replaced to be loaded + * into the replacing slot. + * + * The 'location' member variable is set to the new location before calling + * begin_location_change() to ensure that it matches the + * 'current_location_bookmark' member as expected by the location change + * pipeline. + */ +void +nautilus_window_slot_open_location_set_navigation_state (NautilusWindowSlot *self, + GFile *location, + NautilusWindowOpenFlags flags, + GList *new_selection, + NautilusLocationChangeType change_type, + NautilusNavigationState *navigation_state, + guint distance) +{ + NautilusWindowSlotPrivate *priv; + + priv = nautilus_window_slot_get_instance_private (self); + + nautilus_window_slot_restore_navigation_state (self, navigation_state); + + g_clear_object (&priv->location); + + priv->location = nautilus_file_get_location (navigation_state->file); + + begin_location_change (self, location, NULL, new_selection, + change_type, distance, NULL); +} |