/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- 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 . Author: Christian Neumair */ #include "config.h" #include "nautilus-window-slot.h" #include "nautilus-application.h" #include "nautilus-canvas-view.h" #include "nautilus-desktop-window.h" #include "nautilus-floating-bar.h" #include "nautilus-list-view.h" #include "nautilus-special-location-bar.h" #include "nautilus-trash-bar.h" #include "nautilus-window.h" #include "nautilus-x-content-bar.h" #include #include #include #include #include #include #include #include #include G_DEFINE_TYPE (NautilusWindowSlot, nautilus_window_slot, GTK_TYPE_BOX); enum { ACTIVE, INACTIVE, LOCATION_CHANGED, LAST_SIGNAL }; enum { PROP_WINDOW = 1, NUM_PROPERTIES }; struct NautilusWindowSlotDetails { NautilusWindow *window; /* floating bar */ guint set_status_timeout_id; guint loading_timeout_id; GtkWidget *floating_bar; GtkWidget *view_overlay; /* no search results widget */ GtkWidget *no_search_results_widget; /* slot contains * 1) an vbox containing extra_location_widgets * 2) the view */ GtkWidget *extra_location_widgets; /* 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; GtkWidget *query_editor_revealer; gulong qe_changed_id; gulong qe_cancel_id; gulong qe_activated_id; gboolean search_visible; /* 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; gboolean load_with_search; /* New location. */ GFile *pending_location; NautilusLocationChangeType location_change_type; guint location_change_distance; char *pending_scroll_to; GList *pending_selection; gboolean pending_use_default_location; NautilusFile *determine_view_file; GCancellable *mount_cancellable; GError *mount_error; gboolean tried_mount; NautilusWindowGoToCallback open_callback; gpointer open_callback_user_data; gchar *view_mode_before_search; /*Folder is empty */ GtkWidget *folder_is_empty_widget; }; static guint signals[LAST_SIGNAL] = { 0 }; static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; static void nautilus_window_slot_force_reload (NautilusWindowSlot *slot); static void location_has_really_changed (NautilusWindowSlot *slot); static void nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *slot); static void nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *slot); static void nautilus_window_slot_emit_location_change (NautilusWindowSlot *slot, GFile *from, GFile *to); static void setup_loading_floating_bar (NautilusWindowSlot *slot); static void nautilus_window_slot_sync_search_widgets (NautilusWindowSlot *slot) { NautilusDirectory *directory; gboolean toggle; if (slot != nautilus_window_get_active_slot (slot->details->window)) { return; } toggle = slot->details->search_visible; if (slot->details->content_view != NULL) { directory = nautilus_view_get_model (slot->details->content_view); if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { toggle = TRUE; } } nautilus_window_slot_set_search_visible (slot, toggle); } static gboolean nautilus_window_slot_content_view_matches_iid (NautilusWindowSlot *slot, const char *iid) { if (slot->details->content_view == NULL) { return FALSE; } return g_strcmp0 (nautilus_view_get_view_id (slot->details->content_view), iid) == 0; } void nautilus_window_slot_sync_view_mode (NautilusWindowSlot *slot) { GAction *action; if (slot != nautilus_window_get_active_slot (slot->details->window)) { return; } if (slot->details->content_view == NULL || slot->details->new_content_view != NULL) { return; } action = g_action_map_lookup_action (G_ACTION_MAP (slot->details->window), "view-mode"); if (nautilus_window_slot_content_view_matches_iid (slot, NAUTILUS_LIST_VIEW_ID)) { g_action_change_state (action, g_variant_new_string ("list")); } else if (nautilus_window_slot_content_view_matches_iid (slot, NAUTILUS_CANVAS_VIEW_ID)) { g_action_change_state (action, g_variant_new_string ("grid")); } else { g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE); } } static void remove_loading_floating_bar (NautilusWindowSlot *slot) { if (slot->details->loading_timeout_id != 0) { g_source_remove (slot->details->loading_timeout_id); slot->details->loading_timeout_id = 0; } gtk_widget_hide (slot->details->floating_bar); nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (slot->details->floating_bar)); } static void check_empty_states (NautilusWindowSlot *slot) { GList *files; GList *filtered; NautilusDirectory *directory; gboolean show_hidden_files; gtk_widget_hide (slot->details->no_search_results_widget); gtk_widget_hide (slot->details->folder_is_empty_widget); directory = nautilus_view_get_model (slot->details->content_view); if (!slot->details->allow_stop && directory != NULL) { files = nautilus_directory_get_file_list (directory); show_hidden_files = g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES); filtered = nautilus_file_list_filter_hidden (files, show_hidden_files); if (g_list_length (filtered) == 0) { if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { gtk_widget_show (slot->details->no_search_results_widget); } else { gtk_widget_show (slot->details->folder_is_empty_widget); } } nautilus_file_list_unref (filtered); nautilus_file_list_unref (files); } } static void nautilus_window_slot_on_done_loading (NautilusDirectory *directory, NautilusWindowSlot *slot) { remove_loading_floating_bar (slot); nautilus_window_slot_set_allow_stop (slot, FALSE); /* For this pourpose, we could check directly to see if the view is empty, * instead of avoiding races disconnecting the model when appropiate. * But I think we are doing better disconnecting when we know the data * of the directory is not valid */ check_empty_states (slot); } static void connect_directory_signals (NautilusWindowSlot *slot, NautilusDirectory *directory) { if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { g_signal_connect_object (directory, "done-loading", G_CALLBACK (nautilus_window_slot_on_done_loading), slot, 0); } } static void disconnect_directory_signals (NautilusWindowSlot *slot, NautilusDirectory *directory) { if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { g_signal_handlers_disconnect_by_func (directory, G_CALLBACK (nautilus_window_slot_on_done_loading), slot); } } static void sync_search_directory (NautilusWindowSlot *slot) { NautilusDirectory *directory; NautilusQuery *query; gchar *text; GFile *location; g_assert (NAUTILUS_IS_FILE (slot->details->viewed_file)); directory = nautilus_directory_get_for_file (slot->details->viewed_file); g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory)); query = nautilus_query_editor_get_query (slot->details->query_editor); text = nautilus_query_get_text (query); if (!strlen (text)) { /* Prevent the location change from hiding the query editor in this case */ slot->details->load_with_search = TRUE; location = nautilus_query_editor_get_location (slot->details->query_editor); nautilus_window_slot_open_location (slot, location, 0); g_object_unref (location); } else { nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), query); nautilus_window_slot_force_reload (slot); } g_free (text); g_object_unref (query); nautilus_directory_unref (directory); } static void create_new_search (NautilusWindowSlot *slot) { char *uri; NautilusDirectory *directory; GFile *location; NautilusQuery *query; uri = nautilus_search_directory_generate_new_uri (); location = g_file_new_for_uri (uri); directory = nautilus_directory_get (location); g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory)); query = nautilus_query_editor_get_query (slot->details->query_editor); nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), query); nautilus_window_slot_open_location (slot, location, 0); nautilus_directory_unref (directory); g_object_unref (query); g_object_unref (location); g_free (uri); } static void query_editor_cancel_callback (NautilusQueryEditor *editor, NautilusWindowSlot *slot) { nautilus_window_slot_set_search_visible (slot, FALSE); } static void query_editor_activated_callback (NautilusQueryEditor *editor, NautilusWindowSlot *slot) { if (slot->details->content_view != NULL) { nautilus_view_activate_selection (slot->details->content_view); } } static void query_editor_changed_callback (NautilusQueryEditor *editor, NautilusQuery *query, gboolean reload, NautilusWindowSlot *slot) { NautilusDirectory *directory; g_assert (NAUTILUS_IS_FILE (slot->details->viewed_file)); directory = nautilus_directory_get_for_file (slot->details->viewed_file); if (!NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { /* this is the first change from the query editor. we ask for a location change to the search directory, indicate the directory needs to be sync'd with the current query. */ create_new_search (slot); } else { sync_search_directory (slot); } nautilus_directory_unref (directory); } static void hide_query_editor (NautilusWindowSlot *slot) { gtk_revealer_set_reveal_child (GTK_REVEALER (slot->details->query_editor_revealer), FALSE); if (slot->details->qe_changed_id > 0) { g_signal_handler_disconnect (slot->details->query_editor, slot->details->qe_changed_id); slot->details->qe_changed_id = 0; } if (slot->details->qe_cancel_id > 0) { g_signal_handler_disconnect (slot->details->query_editor, slot->details->qe_cancel_id); slot->details->qe_cancel_id = 0; } if (slot->details->qe_activated_id > 0) { g_signal_handler_disconnect (slot->details->query_editor, slot->details->qe_activated_id); slot->details->qe_activated_id = 0; } nautilus_query_editor_set_query (slot->details->query_editor, NULL); } static GFile * nautilus_window_slot_get_current_location (NautilusWindowSlot *slot) { if (slot->details->pending_location != NULL) { return slot->details->pending_location; } if (slot->details->location != NULL) { return slot->details->location; } return NULL; } static void show_query_editor (NautilusWindowSlot *slot) { NautilusDirectory *directory; NautilusSearchDirectory *search_directory; GFile *location; location = nautilus_window_slot_get_current_location (slot); directory = nautilus_directory_get (location); if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { NautilusQuery *query; search_directory = NAUTILUS_SEARCH_DIRECTORY (directory); query = nautilus_search_directory_get_query (search_directory); if (query != NULL) { nautilus_query_editor_set_query (slot->details->query_editor, query); g_object_unref (query); } } else { nautilus_query_editor_set_location (slot->details->query_editor, location); } nautilus_directory_unref (directory); gtk_revealer_set_reveal_child (GTK_REVEALER (slot->details->query_editor_revealer), TRUE); gtk_widget_grab_focus (GTK_WIDGET (slot->details->query_editor)); if (slot->details->qe_changed_id == 0) { slot->details->qe_changed_id = g_signal_connect (slot->details->query_editor, "changed", G_CALLBACK (query_editor_changed_callback), slot); } if (slot->details->qe_cancel_id == 0) { slot->details->qe_cancel_id = g_signal_connect (slot->details->query_editor, "cancel", G_CALLBACK (query_editor_cancel_callback), slot); } if (slot->details->qe_activated_id == 0) { slot->details->qe_activated_id = g_signal_connect (slot->details->query_editor, "activated", G_CALLBACK (query_editor_activated_callback), slot); } } void nautilus_window_slot_set_search_visible (NautilusWindowSlot *slot, gboolean visible) { GAction *action; gboolean old_visible; GFile *return_location; gboolean active_slot; /* set search active state for the slot */ old_visible = slot->details->search_visible; slot->details->search_visible = visible; return_location = NULL; active_slot = (slot == nautilus_window_get_active_slot (slot->details->window)); if (visible) { show_query_editor (slot); } else { /* If search was active on this slot and became inactive, change * the slot location to the real directory. */ if (old_visible && active_slot) { /* Use the query editor search root if possible */ return_location = nautilus_window_slot_get_query_editor_location (slot); /* Use the home directory as a fallback */ if (return_location == NULL) { return_location = g_file_new_for_path (g_get_home_dir ()); } } if (active_slot) { nautilus_window_grab_focus (slot->details->window); } /* Now hide the editor and clear its state */ hide_query_editor (slot); } if (!active_slot) { return; } /* also synchronize the window action state */ action = g_action_map_lookup_action (G_ACTION_MAP (slot->details->window), "toggle-search"); g_simple_action_set_state (G_SIMPLE_ACTION (action), g_variant_new_boolean (visible)); /* If search was active on this slot and became inactive, change * the slot location to the real directory. */ if (return_location != NULL) { nautilus_window_slot_open_location (slot, return_location, 0); g_object_unref (return_location); } } GFile * nautilus_window_slot_get_query_editor_location (NautilusWindowSlot *slot) { return nautilus_query_editor_get_location (slot->details->query_editor); } gboolean nautilus_window_slot_handle_event (NautilusWindowSlot *slot, GdkEventKey *event) { NautilusWindow *window; gboolean retval; retval = FALSE; window = nautilus_window_slot_get_window (slot); if (!NAUTILUS_IS_DESKTOP_WINDOW (window)) { retval = nautilus_query_editor_handle_event (slot->details->query_editor, event); } if (retval) { nautilus_window_slot_set_search_visible (slot, TRUE); } return retval; } static void real_active (NautilusWindowSlot *slot) { NautilusWindow *window; int page_num; window = slot->details->window; page_num = gtk_notebook_page_num (GTK_NOTEBOOK (nautilus_window_get_notebook (window)), GTK_WIDGET (slot)); 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, slot); nautilus_window_sync_title (window, slot); nautilus_window_sync_location_widgets (window); nautilus_window_slot_sync_search_widgets (slot); nautilus_window_slot_sync_view_mode (slot); } static void real_inactive (NautilusWindowSlot *slot) { NautilusWindow *window; window = nautilus_window_slot_get_window (slot); g_assert (slot == nautilus_window_get_active_slot (window)); } static void floating_bar_action_cb (NautilusFloatingBar *floating_bar, gint action, NautilusWindowSlot *slot) { if (action == NAUTILUS_FLOATING_BAR_ACTION_ID_STOP) { nautilus_window_slot_stop_loading (slot); } } static void remove_all_extra_location_widgets (GtkWidget *widget, gpointer data) { NautilusWindowSlot *slot = data; NautilusDirectory *directory; directory = nautilus_directory_get (slot->details->location); if (widget != GTK_WIDGET (slot->details->query_editor_revealer)) { gtk_container_remove (GTK_CONTAINER (slot->details->extra_location_widgets), widget); } nautilus_directory_unref (directory); } static void nautilus_window_slot_remove_extra_location_widgets (NautilusWindowSlot *slot) { gtk_container_foreach (GTK_CONTAINER (slot->details->extra_location_widgets), remove_all_extra_location_widgets, slot); } static void nautilus_window_slot_add_extra_location_widget (NautilusWindowSlot *slot, GtkWidget *widget) { gtk_box_pack_start (GTK_BOX (slot->details->extra_location_widgets), widget, FALSE, TRUE, 0); gtk_widget_show (slot->details->extra_location_widgets); } static void nautilus_window_slot_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (object); switch (property_id) { case PROP_WINDOW: nautilus_window_slot_set_window (slot, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void nautilus_window_slot_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (object); switch (property_id) { case PROP_WINDOW: g_value_set_object (value, slot->details->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void nautilus_window_slot_constructed (GObject *object) { NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (object); GtkBuilder *builder; GtkWidget *extras_vbox; G_OBJECT_CLASS (nautilus_window_slot_parent_class)->constructed (object); gtk_orientable_set_orientation (GTK_ORIENTABLE (slot), GTK_ORIENTATION_VERTICAL); gtk_widget_show (GTK_WIDGET (slot)); extras_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); slot->details->extra_location_widgets = extras_vbox; gtk_box_pack_start (GTK_BOX (slot), extras_vbox, FALSE, FALSE, 0); gtk_widget_show (extras_vbox); slot->details->query_editor = NAUTILUS_QUERY_EDITOR (nautilus_query_editor_new ()); slot->details->query_editor_revealer = gtk_revealer_new (); gtk_container_add (GTK_CONTAINER (slot->details->query_editor_revealer), GTK_WIDGET (slot->details->query_editor)); gtk_widget_show_all (slot->details->query_editor_revealer); nautilus_window_slot_add_extra_location_widget (slot, slot->details->query_editor_revealer); slot->details->view_overlay = gtk_overlay_new (); gtk_widget_add_events (slot->details->view_overlay, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); gtk_box_pack_start (GTK_BOX (slot), slot->details->view_overlay, TRUE, TRUE, 0); gtk_widget_show (slot->details->view_overlay); builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-no-search-results.ui"); slot->details->no_search_results_widget = GTK_WIDGET (gtk_builder_get_object (builder, "no_search_results")); gtk_overlay_add_overlay (GTK_OVERLAY (slot->details->view_overlay), slot->details->no_search_results_widget); builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-folder-is-empty.ui"); slot->details->folder_is_empty_widget = GTK_WIDGET (gtk_builder_get_object (builder, "folder_is_empty")); gtk_overlay_add_overlay (GTK_OVERLAY (slot->details->view_overlay), slot->details->folder_is_empty_widget); g_object_unref (builder); slot->details->floating_bar = nautilus_floating_bar_new (NULL, NULL, FALSE); gtk_widget_set_halign (slot->details->floating_bar, GTK_ALIGN_END); gtk_widget_set_valign (slot->details->floating_bar, GTK_ALIGN_END); gtk_overlay_add_overlay (GTK_OVERLAY (slot->details->view_overlay), slot->details->floating_bar); g_signal_connect (slot->details->floating_bar, "action", G_CALLBACK (floating_bar_action_cb), slot); slot->details->title = g_strdup (_("Loading…")); } static void nautilus_window_slot_init (NautilusWindowSlot *slot) { slot->details = G_TYPE_INSTANCE_GET_PRIVATE (slot, NAUTILUS_TYPE_WINDOW_SLOT, NautilusWindowSlotDetails); } #define DEBUG_FLAG NAUTILUS_DEBUG_WINDOW #include /* FIXME bugzilla.gnome.org 41243: * We should use inheritance instead of these special cases * for the desktop window. */ #include "nautilus-desktop-window.h" /* This number controls a maximum character count for a URL that is * displayed as part of a dialog. It's fairly arbitrary -- big enough * to allow most "normal" URIs to display in full, but small enough to * prevent the dialog from getting insanely wide. */ #define MAX_URI_IN_DIALOG_LENGTH 60 static void begin_location_change (NautilusWindowSlot *slot, GFile *location, GFile *previous_location, GList *new_selection, NautilusLocationChangeType type, guint distance, const char *scroll_pos, NautilusWindowGoToCallback callback, gpointer user_data); static void free_location_change (NautilusWindowSlot *slot); static void end_location_change (NautilusWindowSlot *slot); static void cancel_location_change (NautilusWindowSlot *slot); static void got_file_info_for_view_selection_callback (NautilusFile *file, gpointer callback_data); static gboolean create_content_view (NautilusWindowSlot *slot, const char *view_id, GError **error); static void load_new_location (NautilusWindowSlot *slot, GFile *location, GList *selection, gboolean tell_current_content_view, gboolean tell_new_content_view); static void new_window_show_callback (GtkWidget *widget, gpointer user_data){ NautilusWindow *window; window = NAUTILUS_WINDOW (user_data); nautilus_window_close (window); g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (new_window_show_callback), user_data); } void nautilus_window_slot_open_location_full (NautilusWindowSlot *slot, GFile *location, NautilusWindowOpenFlags flags, GList *new_selection, NautilusWindowGoToCallback callback, gpointer user_data) { NautilusWindow *window; NautilusWindow *target_window; NautilusWindowSlot *target_slot; NautilusWindowOpenFlags slot_flags; GFile *old_location; GList *old_selection; char *old_uri, *new_uri; int new_slot_position; gboolean use_same; gboolean is_desktop; window = nautilus_window_slot_get_window (slot); target_window = NULL; target_slot = NULL; use_same = TRUE; /* this happens at startup */ old_uri = nautilus_window_slot_get_location_uri (slot); if (old_uri == NULL) { old_uri = g_strdup ("(none)"); use_same = TRUE; } new_uri = g_file_get_uri (location); DEBUG ("Opening location, old: %s, new: %s", old_uri, new_uri); nautilus_profile_start ("Opening location, old: %s, new: %s", old_uri, new_uri); g_free (old_uri); g_free (new_uri); is_desktop = NAUTILUS_IS_DESKTOP_WINDOW (window); if (is_desktop) { use_same = !nautilus_desktop_window_loaded (NAUTILUS_DESKTOP_WINDOW (window)); /* if we're requested to open a new tab on the desktop, open a window * instead. */ if (flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) { flags ^= NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB; flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW; } } g_assert (!((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0 && (flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0)); /* and if the flags specify so, this is overridden */ if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0) { use_same = FALSE; } old_location = nautilus_window_slot_get_location (slot); /* now get/create the window */ if (use_same) { target_window = window; } else { target_window = nautilus_application_create_window (NAUTILUS_APPLICATION (g_application_get_default ()), gtk_window_get_screen (GTK_WINDOW (window))); } g_assert (target_window != NULL); /* if the flags say we want a new tab, open a slot in the current window */ if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0) { g_assert (target_window == window); slot_flags = 0; new_slot_position = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_NEW_TAB_POSITION); if (new_slot_position == NAUTILUS_NEW_TAB_POSITION_END) { slot_flags = NAUTILUS_WINDOW_OPEN_SLOT_APPEND; } target_slot = nautilus_window_open_slot (window, slot_flags); } /* close the current window if the flags say so */ if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0) { if (!is_desktop) { if (gtk_widget_get_visible (GTK_WIDGET (target_window))) { nautilus_window_close (window); } else { g_signal_connect_object (target_window, "show", G_CALLBACK (new_window_show_callback), window, G_CONNECT_AFTER); } } } if (target_slot == NULL) { if (target_window == window) { target_slot = slot; } else { target_slot = nautilus_window_get_active_slot (target_window); } } old_selection = NULL; if (slot->details->content_view != NULL) { old_selection = nautilus_view_get_selection (slot->details->content_view); } if (target_window == window && target_slot == slot && !is_desktop && old_location && g_file_equal (old_location, location) && nautilus_file_selection_equal (old_selection, new_selection)) { if (callback != NULL) { callback (window, location, NULL, user_data); } goto done; } slot->details->pending_use_default_location = ((flags & NAUTILUS_WINDOW_OPEN_FLAG_USE_DEFAULT_LOCATION) != 0); begin_location_change (target_slot, location, old_location, new_selection, NAUTILUS_LOCATION_CHANGE_STANDARD, 0, NULL, callback, user_data); done: nautilus_file_list_free (old_selection); nautilus_profile_end (NULL); } static gboolean report_callback (NautilusWindowSlot *slot, GError *error) { if (slot->details->open_callback != NULL) { gboolean res; res = slot->details->open_callback (nautilus_window_slot_get_window (slot), slot->details->pending_location, error, slot->details->open_callback_user_data); slot->details->open_callback = NULL; slot->details->open_callback_user_data = NULL; return res; } return FALSE; } /* * 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. * @callback: function to be called when the location is changed. * @user_data: data for @callback. * * 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 *slot, GFile *location, GFile *previous_location, GList *new_selection, NautilusLocationChangeType type, guint distance, const char *scroll_pos, NautilusWindowGoToCallback callback, gpointer user_data) { NautilusDirectory *previous_directory; NautilusDirectory *directory; NautilusFile *file; gboolean force_reload; char *current_pos; GFile *from_folder, *parent; GList *parent_selection = NULL; g_assert (slot != NULL); g_assert (location != NULL); g_assert (type == NAUTILUS_LOCATION_CHANGE_BACK || type == NAUTILUS_LOCATION_CHANGE_FORWARD || distance == 0); nautilus_profile_start (NULL); previous_directory = nautilus_directory_get (previous_location); directory = nautilus_directory_get (location); /* Disconnect search signals from the old directory if it was a search directory */ disconnect_directory_signals (slot, previous_directory); nautilus_directory_unref (previous_directory); /* Avoid to update status from the current view in our async calls */ nautilus_window_slot_disconnect_content_view (slot); /* 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 */ if (slot->details->content_view) { nautilus_view_stop_loading (slot->details->content_view); } /* 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 = parent_selection = g_list_prepend (NULL, nautilus_file_get (from_folder)); g_object_unref (parent); } g_object_unref (from_folder); } end_location_change (slot); nautilus_window_slot_set_allow_stop (slot, TRUE); nautilus_window_slot_set_status (slot, NULL, NULL); g_assert (slot->details->pending_location == NULL); g_assert (slot->details->pending_selection == NULL); slot->details->pending_location = g_object_ref (location); slot->details->location_change_type = type; slot->details->location_change_distance = distance; slot->details->tried_mount = FALSE; slot->details->pending_selection = g_list_copy_deep (new_selection, (GCopyFunc) g_object_ref, NULL); slot->details->pending_scroll_to = g_strdup (scroll_pos); slot->details->open_callback = callback; slot->details->open_callback_user_data = user_data; /* 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. */ if (type == NAUTILUS_LOCATION_CHANGE_RELOAD) { force_reload = TRUE; } else if (!nautilus_monitor_active ()) { force_reload = TRUE; } else { force_reload = !nautilus_directory_is_local (directory); } if (force_reload) { nautilus_directory_force_reload (directory); file = nautilus_directory_get_corresponding_file (directory); nautilus_file_invalidate_all_attributes (file); nautilus_file_unref (file); } nautilus_directory_unref (directory); if (parent_selection != NULL) { g_list_free_full (parent_selection, g_object_unref); } /* Set current_bookmark scroll pos */ if (slot->details->current_location_bookmark != NULL && slot->details->content_view != NULL) { current_pos = nautilus_view_get_first_visible_file (slot->details->content_view); nautilus_bookmark_set_scroll_pos (slot->details->current_location_bookmark, current_pos); g_free (current_pos); } /* Get the info needed for view selection */ slot->details->determine_view_file = nautilus_file_get (location); g_assert (slot->details->determine_view_file != NULL); nautilus_file_call_when_ready (slot->details->determine_view_file, NAUTILUS_FILE_ATTRIBUTE_INFO | NAUTILUS_FILE_ATTRIBUTE_MOUNT, got_file_info_for_view_selection_callback, slot); nautilus_profile_end (NULL); } static void nautilus_window_slot_set_location (NautilusWindowSlot *slot, GFile *location) { GFile *old_location; if (slot->details->location && g_file_equal (location, slot->details->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 (slot); return; } old_location = slot->details->location; slot->details->location = g_object_ref (location); if (slot == nautilus_window_get_active_slot (slot->details->window)) { nautilus_window_sync_location_widgets (slot->details->window); } nautilus_window_slot_update_title (slot); nautilus_window_slot_emit_location_change (slot, old_location, location); if (old_location) { g_object_unref (old_location); } } static void viewed_file_changed_callback (NautilusFile *file, NautilusWindowSlot *slot) { GFile *new_location; gboolean is_in_trash, was_in_trash; g_assert (NAUTILUS_IS_FILE (file)); g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); g_assert (file == slot->details->viewed_file); if (!nautilus_file_is_not_yet_confirmed (file)) { slot->details->viewed_file_seen = TRUE; } was_in_trash = slot->details->viewed_file_in_trash; slot->details->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 (slot->details->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 (slot, go_to_file, 0); 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 (slot, new_location); g_object_unref (new_location); } } static void nautilus_window_slot_set_viewed_file (NautilusWindowSlot *slot, NautilusFile *file) { NautilusFileAttributes attributes; if (slot->details->viewed_file == file) { return; } nautilus_file_ref (file); if (slot->details->viewed_file != NULL) { g_signal_handlers_disconnect_by_func (slot->details->viewed_file, G_CALLBACK (viewed_file_changed_callback), slot); nautilus_file_monitor_remove (slot->details->viewed_file, slot); } if (file != NULL) { attributes = NAUTILUS_FILE_ATTRIBUTE_INFO | NAUTILUS_FILE_ATTRIBUTE_LINK_INFO; nautilus_file_monitor_add (file, slot, attributes); g_signal_connect_object (file, "changed", G_CALLBACK (viewed_file_changed_callback), slot, 0); } nautilus_file_unref (slot->details->viewed_file); slot->details->viewed_file = file; } typedef struct { GCancellable *cancellable; NautilusWindowSlot *slot; } MountNotMountedData; static void mount_not_mounted_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { MountNotMountedData *data; NautilusWindowSlot *slot; GError *error; GCancellable *cancellable; data = user_data; slot = data->slot; cancellable = data->cancellable; g_free (data); if (g_cancellable_is_cancelled (cancellable)) { /* Cancelled, don't call back */ g_object_unref (cancellable); return; } slot->details->mount_cancellable = NULL; slot->details->determine_view_file = nautilus_file_get (slot->details->pending_location); error = NULL; if (!g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error)) { slot->details->mount_error = error; got_file_info_for_view_selection_callback (slot->details->determine_view_file, slot); slot->details->mount_error = NULL; g_error_free (error); } else { nautilus_file_invalidate_all_attributes (slot->details->determine_view_file); nautilus_file_call_when_ready (slot->details->determine_view_file, NAUTILUS_FILE_ATTRIBUTE_INFO | NAUTILUS_FILE_ATTRIBUTE_MOUNT, got_file_info_for_view_selection_callback, slot); } g_object_unref (cancellable); } static void got_file_info_for_view_selection_callback (NautilusFile *file, gpointer callback_data) { GError *error = NULL; char *view_id; NautilusWindow *window; NautilusWindowSlot *slot; NautilusFile *viewed_file, *parent_file; GFile *location, *default_location; GMountOperation *mount_op; MountNotMountedData *data; NautilusApplication *app; GMount *mount; slot = callback_data; window = nautilus_window_slot_get_window (slot); g_assert (slot->details->determine_view_file == file); slot->details->determine_view_file = NULL; nautilus_profile_start (NULL); if (slot->details->mount_error) { error = g_error_copy (slot->details->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 && !slot->details->tried_mount) { slot->details->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 = slot; slot->details->mount_cancellable = data->cancellable; g_file_mount_enclosing_volume (location, 0, mount_op, slot->details->mount_cancellable, mount_not_mounted_callback, data); g_object_unref (location); g_object_unref (mount_op); goto done; } mount = NULL; default_location = NULL; if (slot->details->pending_use_default_location) { mount = nautilus_file_get_mount (file); slot->details->pending_use_default_location = FALSE; } if (mount != NULL) { default_location = g_mount_get_default_location (mount); g_object_unref (mount); } if (default_location != NULL && !g_file_equal (slot->details->pending_location, default_location)) { g_clear_object (&slot->details->pending_location); slot->details->pending_location = default_location; slot->details->determine_view_file = nautilus_file_get (default_location); nautilus_file_invalidate_all_attributes (slot->details->determine_view_file); nautilus_file_call_when_ready (slot->details->determine_view_file, NAUTILUS_FILE_ATTRIBUTE_INFO | NAUTILUS_FILE_ATTRIBUTE_MOUNT, got_file_info_for_view_selection_callback, slot); goto done; } parent_file = nautilus_file_get_parent (file); if ((parent_file != NULL) && nautilus_file_get_file_type (file) == G_FILE_TYPE_REGULAR) { if (slot->details->pending_selection != NULL) { g_list_free_full (slot->details->pending_selection, (GDestroyNotify) nautilus_file_unref); } g_clear_object (&slot->details->pending_location); g_free (slot->details->pending_scroll_to); slot->details->pending_location = nautilus_file_get_parent_location (file); slot->details->pending_selection = g_list_prepend (NULL, nautilus_file_ref (file)); slot->details->determine_view_file = parent_file; slot->details->pending_scroll_to = nautilus_file_get_uri (file); nautilus_file_invalidate_all_attributes (slot->details->determine_view_file); nautilus_file_call_when_ready (slot->details->determine_view_file, NAUTILUS_FILE_ATTRIBUTE_INFO | NAUTILUS_FILE_ATTRIBUTE_MOUNT, got_file_info_for_view_selection_callback, slot); goto done; } nautilus_file_unref (parent_file); location = slot->details->pending_location; view_id = NULL; if (error == NULL || (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_SUPPORTED)) { /* We got the information we need, now pick what view to use: */ /* If we are in search, try to use by default list view. This will be deactivated * if the user manually switch to a diferent view mode */ if (nautilus_file_is_in_search (nautilus_file_get (location))) { if (g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_ON_SEARCH)) { /* 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 (slot->details->view_mode_before_search == NULL) { slot->details->view_mode_before_search = g_strdup (nautilus_view_get_view_id (slot->details->content_view)); } view_id = g_strdup (NAUTILUS_LIST_VIEW_IID); } else { g_free (slot->details->view_mode_before_search); slot->details->view_mode_before_search = 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 (slot->details->content_view != NULL && view_id == NULL) { if (slot->details->view_mode_before_search != NULL) { view_id = g_strdup (slot->details->view_mode_before_search); g_free (slot->details->view_mode_before_search); slot->details->view_mode_before_search = NULL; } else { view_id = g_strdup (nautilus_view_get_view_id (slot->details->content_view)); } } /* If there is not previous view in this slot, use the default view mode * from preferences */ if (view_id == NULL) { view_id = nautilus_global_preferences_get_default_folder_viewer_preference_as_iid (); } } if (view_id != NULL) { GError *err = NULL; create_content_view (slot, view_id, &err); g_free (view_id); report_callback (slot, err); g_clear_error (&err); } else { if (error == NULL) { error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Unable to load location")); } if (!report_callback (slot, error)) { 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 (slot, 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 (slot, location, 0); 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 (slot); slot_location = nautilus_window_slot_get_location (slot); /* We're missing a previous location (if opened location * in a new tab) so close it and return */ if (slot_location == NULL) { nautilus_window_slot_close (window, slot); } else { /* We disconnected this, so we need to re-connect it */ viewed_file = nautilus_file_get (slot_location); nautilus_window_slot_set_viewed_file (slot, 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 create_content_view (NautilusWindowSlot *slot, const char *view_id, GError **error_out) { NautilusWindow *window; NautilusView *view; GList *selection; gboolean ret = TRUE; GError *error = NULL; NautilusDirectory *old_directory, *new_directory; GFile *old_location; window = nautilus_window_slot_get_window (slot); nautilus_profile_start (NULL); /* FIXME bugzilla.gnome.org 41243: * We should use inheritance instead of these special cases * for the desktop window. */ if (NAUTILUS_IS_DESKTOP_WINDOW (window)) { /* We force the desktop to use a desktop_icon_view. It's simpler * to fix it here than trying to make it pick the right view in * the first place. */ view_id = NAUTILUS_DESKTOP_ICON_VIEW_IID; } nautilus_window_slot_disconnect_content_view (slot); if (nautilus_window_slot_content_view_matches_iid (slot, view_id)) { /* reuse existing content view */ view = slot->details->content_view; slot->details->new_content_view = view; g_object_ref (view); } else { /* create a new content view */ view = nautilus_view_new (view_id, slot); slot->details->new_content_view = view; } nautilus_window_slot_connect_new_content_view (slot); /* Forward search selection and state before loading the new model */ old_location = nautilus_window_slot_get_location (slot); old_directory = nautilus_directory_get (old_location); new_directory = nautilus_directory_get (slot->details->pending_location); /* Connect to the done loading signal if it is a search directory to update * the no results widget */ connect_directory_signals (slot, new_directory); if (NAUTILUS_IS_SEARCH_DIRECTORY (new_directory) && !NAUTILUS_IS_SEARCH_DIRECTORY (old_directory)) { nautilus_search_directory_set_base_model (NAUTILUS_SEARCH_DIRECTORY (new_directory), old_directory); } if (NAUTILUS_IS_SEARCH_DIRECTORY (old_directory) && !NAUTILUS_IS_SEARCH_DIRECTORY (new_directory)) { /* Reset the search_active state when going out of a search directory, * before nautilus_window_slot_sync_search_widgets() is called * if we're not being loaded with search visible. */ if (!slot->details->load_with_search) { slot->details->search_visible = FALSE; } slot->details->load_with_search = FALSE; if (slot->details->pending_selection == NULL) { slot->details->pending_selection = nautilus_view_get_selection (slot->details->content_view); } } /* Actually load the pending location and selection: */ if (slot->details->pending_location != NULL) { load_new_location (slot, slot->details->pending_location, slot->details->pending_selection, FALSE, TRUE); g_list_free_full (slot->details->pending_selection, g_object_unref); slot->details->pending_selection = NULL; } else if (old_location != NULL) { selection = nautilus_view_get_selection (slot->details->content_view); load_new_location (slot, old_location, selection, FALSE, TRUE); g_list_free_full (selection, g_object_unref); } else { /* Something is busted, there was no location to load. */ ret = FALSE; error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Unable to load location")); } if (error != NULL) { g_propagate_error (error_out, error); } nautilus_profile_end (NULL); return ret; } static void load_new_location (NautilusWindowSlot *slot, GFile *location, GList *selection, gboolean tell_current_content_view, gboolean tell_new_content_view) { GList *selection_copy; NautilusView *view; g_assert (slot != NULL); g_assert (location != NULL); selection_copy = g_list_copy_deep (selection, (GCopyFunc) g_object_ref, NULL); view = NULL; nautilus_profile_start (NULL); /* Note, these may recurse into report_load_underway */ if (slot->details->content_view != NULL && tell_current_content_view) { view = slot->details->content_view; nautilus_view_load_location (slot->details->content_view, location); } if (slot->details->new_content_view != NULL && tell_new_content_view && (!tell_current_content_view || slot->details->new_content_view != slot->details->content_view) ) { view = slot->details->new_content_view; nautilus_view_load_location (slot->details->new_content_view, location); } if (view != NULL) { /* new_content_view might have changed here if report_load_underway was called from load_location */ nautilus_view_set_selection (view, selection_copy); } g_list_free_full (selection_copy, g_object_unref); nautilus_profile_end (NULL); } static void end_location_change (NautilusWindowSlot *slot) { char *uri; uri = nautilus_window_slot_get_location_uri (slot); if (uri) { DEBUG ("Finished loading window for uri %s", uri); g_free (uri); } nautilus_window_slot_set_allow_stop (slot, FALSE); /* Now we can free details->pending_scroll_to, since the load_complete * callback already has been emitted. */ g_free (slot->details->pending_scroll_to); slot->details->pending_scroll_to = NULL; free_location_change (slot); } static void free_location_change (NautilusWindowSlot *slot) { g_clear_object (&slot->details->pending_location); g_list_free_full (slot->details->pending_selection, g_object_unref); slot->details->pending_selection = NULL; /* Don't free details->pending_scroll_to, since thats needed until * the load_complete callback. */ if (slot->details->mount_cancellable != NULL) { g_cancellable_cancel (slot->details->mount_cancellable); slot->details->mount_cancellable = NULL; } if (slot->details->determine_view_file != NULL) { nautilus_file_cancel_call_when_ready (slot->details->determine_view_file, got_file_info_for_view_selection_callback, slot); slot->details->determine_view_file = NULL; } if (slot->details->new_content_view != NULL) { g_object_unref (slot->details->new_content_view); slot->details->new_content_view = NULL; } } static void cancel_location_change (NautilusWindowSlot *slot) { GList *selection; GFile *location; NautilusDirectory *directory; location = nautilus_window_slot_get_location (slot); directory = nautilus_directory_get (slot->details->location); disconnect_directory_signals (slot, directory); /* Stops current loading or search if any, so we are not slow */ nautilus_view_stop_loading (slot->details->content_view); nautilus_directory_unref (directory); if (slot->details->pending_location != NULL && location != NULL && slot->details->content_view != NULL) { /* 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. */ selection = nautilus_view_get_selection (slot->details->content_view); load_new_location (slot, location, selection, TRUE, FALSE); g_list_free_full (selection, g_object_unref); } end_location_change (slot); } 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_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); } eel_show_error_dialog (error_message, detail_message, GTK_WINDOW (window)); done: g_free (error_message); g_free (detail_message); } void nautilus_window_slot_set_content_view (NautilusWindowSlot *slot, const char *id) { char *uri; g_assert (slot != NULL); g_assert (id != NULL); uri = nautilus_window_slot_get_location_uri (slot); DEBUG ("Change view of window %s to %s", uri, id); g_free (uri); if (nautilus_window_slot_content_view_matches_iid (slot, id)) { return; } end_location_change (slot); nautilus_window_slot_set_allow_stop (slot, TRUE); if (nautilus_view_get_selection_count (slot->details->content_view) == 0) { /* If there is no selection, queue a scroll to the same icon that * is currently visible */ slot->details->pending_scroll_to = nautilus_view_get_first_visible_file (slot->details->content_view); } slot->details->location_change_type = NAUTILUS_LOCATION_CHANGE_RELOAD; if (!create_content_view (slot, id, NULL)) { /* Just load the homedir. */ nautilus_window_slot_go_home (slot, FALSE); } } void nautilus_window_back_or_forward (NautilusWindow *window, gboolean back, guint distance, NautilusWindowOpenFlags flags) { NautilusWindowSlot *slot; GList *list; GFile *location; guint len; NautilusBookmark *bookmark; GFile *old_location; slot = nautilus_window_get_active_slot (window); list = back ? slot->details->back_list : slot->details->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 (slot, location, flags); } else { char *scroll_pos; old_location = nautilus_window_slot_get_location (slot); scroll_pos = nautilus_bookmark_get_scroll_pos (bookmark); begin_location_change (slot, location, old_location, NULL, back ? NAUTILUS_LOCATION_CHANGE_BACK : NAUTILUS_LOCATION_CHANGE_FORWARD, distance, scroll_pos, NULL, NULL); g_free (scroll_pos); } g_object_unref (location); } /* reload the contents of the window */ static void nautilus_window_slot_force_reload (NautilusWindowSlot *slot) { GFile *location; char *current_pos; GList *selection; g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); location = nautilus_window_slot_get_location (slot); 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; selection = NULL; if (slot->details->content_view != NULL) { current_pos = nautilus_view_get_first_visible_file (slot->details->content_view); selection = nautilus_view_get_selection (slot->details->content_view); } begin_location_change (slot, location, location, selection, NAUTILUS_LOCATION_CHANGE_RELOAD, 0, current_pos, NULL, NULL); g_free (current_pos); g_object_unref (location); g_list_free_full (selection, g_object_unref); } void nautilus_window_slot_queue_reload (NautilusWindowSlot *slot) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); if (nautilus_window_slot_get_location (slot) == NULL) { return; } if (slot->details->pending_location != NULL || slot->details->content_view == NULL || nautilus_view_get_loading (slot->details->content_view)) { /* there is a reload in flight */ slot->details->needs_reload = TRUE; return; } nautilus_window_slot_force_reload (slot); } static void nautilus_window_slot_clear_forward_list (NautilusWindowSlot *slot) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); g_list_free_full (slot->details->forward_list, g_object_unref); slot->details->forward_list = NULL; } static void nautilus_window_slot_clear_back_list (NautilusWindowSlot *slot) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); g_list_free_full (slot->details->back_list, g_object_unref); slot->details->back_list = NULL; } static void nautilus_window_slot_update_bookmark (NautilusWindowSlot *slot, NautilusFile *file) { gboolean recreate; GFile *new_location; new_location = nautilus_file_get_location (file); if (slot->details->current_location_bookmark == NULL) { recreate = TRUE; } else { GFile *bookmark_location; bookmark_location = nautilus_bookmark_get_location (slot->details->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 (&slot->details->last_location_bookmark); slot->details->last_location_bookmark = slot->details->current_location_bookmark; display_name = nautilus_file_get_display_name (file); slot->details->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 *slot) { check_bookmark_location_matches (slot->details->last_location_bookmark, nautilus_window_slot_get_location (slot)); } static void handle_go_direction (NautilusWindowSlot *slot, GFile *location, gboolean forward) { GList **list_ptr, **other_list_ptr; GList *list, *other_list, *link; NautilusBookmark *bookmark; gint i; list_ptr = (forward) ? (&slot->details->forward_list) : (&slot->details->back_list); other_list_ptr = (forward) ? (&slot->details->back_list) : (&slot->details->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) > slot->details->location_change_distance); check_bookmark_location_matches (g_list_nth_data (list, slot->details->location_change_distance), location); g_assert (nautilus_window_slot_get_location (slot) != NULL); /* Move current location to list */ check_last_bookmark_location_matches_slot (slot); /* Use the first bookmark in the history list rather than creating a new one. */ other_list = g_list_prepend (other_list, slot->details->last_location_bookmark); g_object_ref (other_list->data); /* Move extra links from the list to the other list */ for (i = 0; i < slot->details->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 *slot, GFile *location) { GFile *slot_location; /* Clobber the entire forward list, and move displayed location to back list */ nautilus_window_slot_clear_forward_list (slot); slot_location = nautilus_window_slot_get_location (slot); 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 (slot); /* Use the first bookmark in the history list rather than creating a new one. */ slot->details->back_list = g_list_prepend (slot->details->back_list, slot->details->last_location_bookmark); g_object_ref (slot->details->back_list->data); } } } static void update_history (NautilusWindowSlot *slot, NautilusLocationChangeType type, GFile *new_location) { switch (type) { case NAUTILUS_LOCATION_CHANGE_STANDARD: handle_go_elsewhere (slot, 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 (slot, new_location, FALSE); return; case NAUTILUS_LOCATION_CHANGE_FORWARD: handle_go_direction (slot, 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 *slot, GMount *mount, const char **x_content_types) { GtkWidget *bar; g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); bar = nautilus_x_content_bar_new (mount, x_content_types); gtk_widget_show (bar); nautilus_window_slot_add_extra_location_widget (slot, bar); } static void found_content_type_cb (const char **x_content_types, gpointer user_data) { NautilusWindowSlot *slot; FindMountData *data = user_data; if (g_cancellable_is_cancelled (data->cancellable)) { goto out; } slot = data->slot; if (x_content_types != NULL && x_content_types[0] != NULL) { nautilus_window_slot_show_x_content_bar (slot, data->mount, x_content_types); } slot->details->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; GMount *mount; 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; } data->slot->details->find_mount_cancellable = NULL; out: g_object_unref (data->cancellable); g_free (data); } static void nautilus_window_slot_emit_location_change (NautilusWindowSlot *slot, GFile *from, GFile *to) { char *from_uri = NULL; char *to_uri = NULL; if (from != NULL) from_uri = g_file_get_uri (from); if (to != NULL) to_uri = g_file_get_uri (to); g_signal_emit_by_name (slot, "location-changed", from_uri, to_uri); g_free (to_uri); g_free (from_uri); } static void nautilus_window_slot_show_trash_bar (NautilusWindowSlot *slot) { GtkWidget *bar; NautilusView *view; view = nautilus_window_slot_get_current_view (slot); bar = nautilus_trash_bar_new (view); gtk_widget_show (bar); nautilus_window_slot_add_extra_location_widget (slot, bar); } static void nautilus_window_slot_show_special_location_bar (NautilusWindowSlot *slot, NautilusSpecialLocation special_location) { GtkWidget *bar; bar = nautilus_special_location_bar_new (special_location); gtk_widget_show (bar); nautilus_window_slot_add_extra_location_widget (slot, bar); } static void slot_add_extension_extra_widgets (NautilusWindowSlot *slot) { 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 (slot); uri = nautilus_window_slot_get_location_uri (slot); 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 (slot, widget); } } g_free (uri); nautilus_module_extension_list_free (providers); } static void nautilus_window_slot_update_for_new_location (NautilusWindowSlot *slot) { NautilusWindow *window; GFile *new_location, *old_location; NautilusFile *file; gboolean location_really_changed; window = nautilus_window_slot_get_window (slot); new_location = slot->details->pending_location; slot->details->pending_location = NULL; file = nautilus_file_get (new_location); nautilus_window_slot_update_bookmark (slot, file); update_history (slot, slot->details->location_change_type, new_location); old_location = nautilus_window_slot_get_location (slot); location_really_changed = old_location == NULL || !g_file_equal (old_location, new_location); /* Create a NautilusFile for this location, so we can catch it * if it goes away. */ nautilus_window_slot_set_viewed_file (slot, file); slot->details->viewed_file_seen = !nautilus_file_is_not_yet_confirmed (file); slot->details->viewed_file_in_trash = nautilus_file_is_in_trash (file); nautilus_file_unref (file); nautilus_window_slot_set_location (slot, new_location); if (slot == nautilus_window_get_active_slot (window)) { /* Sync the content view menu for this new location. */ nautilus_window_slot_sync_view_mode (slot); } if (slot == nautilus_window_get_active_slot (window) && location_really_changed) { nautilus_window_slot_sync_search_widgets (slot); } } static void view_end_loading_cb (NautilusView *view, gboolean all_files_seen, NautilusWindowSlot *slot) { /* Only handle this if we're expecting it. * Don't handle it if its from an old view we've switched from */ if (view == slot->details->content_view && all_files_seen) { if (slot->details->pending_scroll_to != NULL) { nautilus_view_scroll_to_file (slot->details->content_view, slot->details->pending_scroll_to); } end_location_change (slot); } if (slot->details->needs_reload) { nautilus_window_slot_queue_reload (slot); slot->details->needs_reload = FALSE; } /* If it is a search directory, it will hide the toolbar when the search engine * finishes, not every time the view end loading the new files */ if (!NAUTILUS_IS_SEARCH_DIRECTORY (nautilus_view_get_model (slot->details->content_view))) { remove_loading_floating_bar (slot); nautilus_window_slot_set_allow_stop (slot, FALSE); } check_empty_states (slot); } static void real_setup_loading_floating_bar (NautilusWindowSlot *slot) { gboolean disable_chrome; g_object_get (nautilus_window_slot_get_window (slot), "disable-chrome", &disable_chrome, NULL); if (disable_chrome) { gtk_widget_hide (slot->details->floating_bar); return; } nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (slot->details->floating_bar)); nautilus_floating_bar_set_primary_label (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), NAUTILUS_IS_SEARCH_DIRECTORY (nautilus_view_get_model (slot->details->content_view)) ? _("Searching…") : _("Loading…")); nautilus_floating_bar_set_details_label (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), NULL); nautilus_floating_bar_set_show_spinner (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), slot->details->allow_stop); nautilus_floating_bar_add_action (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), "process-stop-symbolic", NAUTILUS_FLOATING_BAR_ACTION_ID_STOP); gtk_widget_set_halign (slot->details->floating_bar, GTK_ALIGN_END); gtk_widget_show (slot->details->floating_bar); } static gboolean setup_loading_floating_bar_timeout_cb (gpointer user_data) { NautilusWindowSlot *slot = user_data; slot->details->loading_timeout_id = 0; real_setup_loading_floating_bar (slot); return FALSE; } static void setup_loading_floating_bar (NautilusWindowSlot *slot) { /* setup loading overlay */ if (slot->details->set_status_timeout_id != 0) { g_source_remove (slot->details->set_status_timeout_id); slot->details->set_status_timeout_id = 0; } if (slot->details->loading_timeout_id != 0) { g_source_remove (slot->details->loading_timeout_id); slot->details->loading_timeout_id = 0; } slot->details->loading_timeout_id = g_timeout_add (500, setup_loading_floating_bar_timeout_cb, slot); } static void view_begin_loading_cb (NautilusView *view, NautilusWindowSlot *slot) { nautilus_profile_start (NULL); if (view == slot->details->new_content_view) { location_has_really_changed (slot); } else { nautilus_window_slot_set_allow_stop (slot, TRUE); } setup_loading_floating_bar (slot); check_empty_states (slot); nautilus_profile_end (NULL); } static void nautilus_window_slot_setup_extra_location_widgets (NautilusWindowSlot *slot) { GFile *location; FindMountData *data; NautilusDirectory *directory; location = nautilus_window_slot_get_current_location (slot); if (location == NULL) { return; } directory = nautilus_directory_get (location); if (nautilus_directory_is_in_trash (directory)) { nautilus_window_slot_show_trash_bar (slot); } 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 (slot, NAUTILUS_SPECIAL_LOCATION_TEMPLATES); } else if (g_file_equal (location, scripts_file)) { nautilus_window_slot_show_special_location_bar (slot, 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 (slot->details->find_mount_cancellable != NULL) { g_cancellable_cancel (slot->details->find_mount_cancellable); slot->details->find_mount_cancellable = NULL; } data = g_new (FindMountData, 1); data->slot = slot; data->cancellable = g_cancellable_new (); data->mount = NULL; slot->details->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 (slot); } static void view_end_file_changes_cb (NautilusView *view, NautilusWindowSlot *slot) { /* When creating or deleting a file the done-loading signal is not emitted, * given that the view doesn't actually reload, so connect to the * end-file-changes for update the empty states */ check_empty_states (slot); } static void nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *slot) { if (slot->details->new_content_view != NULL) { g_signal_connect (slot->details->new_content_view, "begin-loading", G_CALLBACK (view_begin_loading_cb), slot); g_signal_connect (slot->details->new_content_view, "end-loading", G_CALLBACK (view_end_loading_cb), slot); g_signal_connect (slot->details->new_content_view, "end-file-changes", G_CALLBACK (view_end_file_changes_cb), slot); } } static void nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *slot) { if (slot->details->content_view != NULL) { /* disconnect old view */ g_signal_handlers_disconnect_by_func (slot->details->content_view, G_CALLBACK (view_end_loading_cb), slot); g_signal_handlers_disconnect_by_func (slot->details->content_view, G_CALLBACK (view_begin_loading_cb), slot); g_signal_handlers_disconnect_by_func (slot->details->content_view, G_CALLBACK (view_end_file_changes_cb), slot); } } static void nautilus_window_slot_switch_new_content_view (NautilusWindowSlot *slot) { GtkWidget *widget; if ((slot->details->new_content_view == NULL) || gtk_widget_get_parent (GTK_WIDGET (slot->details->new_content_view)) != NULL) { return; } if (slot->details->content_view != NULL) { widget = GTK_WIDGET (slot->details->content_view); gtk_widget_destroy (widget); g_object_unref (slot->details->content_view); slot->details->content_view = NULL; } if (slot->details->new_content_view != NULL) { slot->details->content_view = slot->details->new_content_view; slot->details->new_content_view = NULL; widget = GTK_WIDGET (slot->details->content_view); gtk_container_add (GTK_CONTAINER (slot->details->view_overlay), widget); gtk_widget_show (widget); } } /* This is called when we have decided we can actually change to the new view/location situation. */ static void location_has_really_changed (NautilusWindowSlot *slot) { NautilusWindow *window; GFile *location; window = nautilus_window_slot_get_window (slot); /* 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 (slot); nautilus_window_slot_switch_new_content_view (slot); if (slot->details->pending_location != NULL) { /* Tell the window we are finished. */ nautilus_window_slot_update_for_new_location (slot); } /* Now that we finished switching to the new location, * add back the extra location widgets. */ nautilus_window_slot_setup_extra_location_widgets (slot); location = nautilus_window_slot_get_location (slot); if (location != NULL) { g_object_ref (location); } if (location != NULL) { if (slot == nautilus_window_get_active_slot (window)) { char *uri; uri = g_file_get_uri (location); g_signal_emit_by_name (window, "loading-uri", uri); g_free (uri); } g_object_unref (location); } } static void nautilus_window_slot_dispose (GObject *object) { NautilusWindowSlot *slot; NautilusDirectory *directory; GtkWidget *widget; slot = NAUTILUS_WINDOW_SLOT (object); nautilus_window_slot_clear_forward_list (slot); nautilus_window_slot_clear_back_list (slot); nautilus_window_slot_remove_extra_location_widgets (slot); if (slot->details->content_view) { widget = GTK_WIDGET (slot->details->content_view); gtk_widget_destroy (widget); g_object_unref (slot->details->content_view); slot->details->content_view = NULL; } if (slot->details->new_content_view) { widget = GTK_WIDGET (slot->details->new_content_view); gtk_widget_destroy (widget); g_object_unref (slot->details->new_content_view); slot->details->new_content_view = NULL; } if (slot->details->set_status_timeout_id != 0) { g_source_remove (slot->details->set_status_timeout_id); slot->details->set_status_timeout_id = 0; } if (slot->details->loading_timeout_id != 0) { g_source_remove (slot->details->loading_timeout_id); slot->details->loading_timeout_id = 0; } nautilus_window_slot_set_viewed_file (slot, NULL); /* TODO? why do we unref here? the file is NULL. * It was already here before the slot move, though */ nautilus_file_unref (slot->details->viewed_file); if (slot->details->location) { /* TODO? why do we ref here, instead of unreffing? * It was already here before the slot migration, though */ g_object_ref (slot->details->location); directory = nautilus_directory_get (slot->details->location); disconnect_directory_signals (slot, directory); g_object_unref (directory); } if (slot->details->view_mode_before_search) { g_free (slot->details->view_mode_before_search); slot->details->view_mode_before_search = NULL; } g_list_free_full (slot->details->pending_selection, g_object_unref); slot->details->pending_selection = NULL; g_clear_object (&slot->details->current_location_bookmark); g_clear_object (&slot->details->last_location_bookmark); if (slot->details->find_mount_cancellable != NULL) { g_cancellable_cancel (slot->details->find_mount_cancellable); slot->details->find_mount_cancellable = NULL; } slot->details->window = NULL; g_free (slot->details->title); slot->details->title = NULL; free_location_change (slot); G_OBJECT_CLASS (nautilus_window_slot_parent_class)->dispose (object); } static void nautilus_window_slot_class_init (NautilusWindowSlotClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS (klass); klass->active = real_active; klass->inactive = real_inactive; oclass->dispose = nautilus_window_slot_dispose; oclass->constructed = nautilus_window_slot_constructed; oclass->set_property = nautilus_window_slot_set_property; oclass->get_property = nautilus_window_slot_get_property; signals[ACTIVE] = g_signal_new ("active", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NautilusWindowSlotClass, active), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[INACTIVE] = g_signal_new ("inactive", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NautilusWindowSlotClass, inactive), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[LOCATION_CHANGED] = g_signal_new ("location-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); 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); g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); g_type_class_add_private (klass, sizeof (NautilusWindowSlotDetails)); } GFile * nautilus_window_slot_get_location (NautilusWindowSlot *slot) { g_assert (slot != NULL); return slot->details->location; } const gchar * nautilus_window_slot_get_title (NautilusWindowSlot *slot) { return slot->details->title; } char * nautilus_window_slot_get_location_uri (NautilusWindowSlot *slot) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); if (slot->details->location) { return g_file_get_uri (slot->details->location); } return NULL; } NautilusWindow * nautilus_window_slot_get_window (NautilusWindowSlot *slot) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); return slot->details->window; } void nautilus_window_slot_set_window (NautilusWindowSlot *slot, NautilusWindow *window) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); g_assert (NAUTILUS_IS_WINDOW (window)); if (slot->details->window != window) { slot->details->window = window; g_object_notify_by_pspec (G_OBJECT (slot), properties[PROP_WINDOW]); } } NautilusView * nautilus_window_slot_get_view (NautilusWindowSlot *slot) { return slot->details->content_view; } /* 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 *slot) { NautilusWindow *window; char *title; gboolean do_sync = FALSE; title = nautilus_compute_title_for_location (slot->details->location); window = nautilus_window_slot_get_window (slot); if (g_strcmp0 (title, slot->details->title) != 0) { do_sync = TRUE; g_free (slot->details->title); slot->details->title = title; title = NULL; } if (strlen (slot->details->title) > 0) { do_sync = TRUE; } if (do_sync) { nautilus_window_sync_title (window, slot); } if (title != NULL) { g_free (title); } } gboolean nautilus_window_slot_get_allow_stop (NautilusWindowSlot *slot) { return slot->details->allow_stop; } void nautilus_window_slot_set_allow_stop (NautilusWindowSlot *slot, gboolean allow) { NautilusWindow *window; g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); slot->details->allow_stop = allow; window = nautilus_window_slot_get_window (slot); nautilus_window_sync_allow_stop (window, slot); } void nautilus_window_slot_stop_loading (NautilusWindowSlot *slot) { remove_loading_floating_bar (slot); cancel_location_change (slot); } static void real_slot_set_short_status (NautilusWindowSlot *slot, const gchar *primary_status, const gchar *detail_status) { gboolean disable_chrome; nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (slot->details->floating_bar)); nautilus_floating_bar_set_show_spinner (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), slot->details->allow_stop); g_object_get (nautilus_window_slot_get_window (slot), "disable-chrome", &disable_chrome, NULL); if ((primary_status == NULL && detail_status == NULL) || disable_chrome) { gtk_widget_hide (slot->details->floating_bar); return; } nautilus_floating_bar_set_labels (NAUTILUS_FLOATING_BAR (slot->details->floating_bar), primary_status, detail_status); gtk_widget_show (slot->details->floating_bar); } typedef struct { gchar *primary_status; gchar *detail_status; NautilusWindowSlot *slot; } SetStatusData; static void set_status_data_free (gpointer data) { SetStatusData *status_data = data; g_free (status_data->primary_status); g_free (status_data->detail_status); g_slice_free (SetStatusData, data); } static gboolean set_status_timeout_cb (gpointer data) { SetStatusData *status_data = data; status_data->slot->details->set_status_timeout_id = 0; real_slot_set_short_status (status_data->slot, status_data->primary_status, status_data->detail_status); return FALSE; } static void set_floating_bar_status (NautilusWindowSlot *slot, const gchar *primary_status, const gchar *detail_status) { GtkSettings *settings; gint double_click_time; SetStatusData *status_data; if (slot->details->set_status_timeout_id != 0) { g_source_remove (slot->details->set_status_timeout_id); slot->details->set_status_timeout_id = 0; } settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (slot->details->content_view))); g_object_get (settings, "gtk-double-click-time", &double_click_time, NULL); status_data = g_slice_new0 (SetStatusData); status_data->primary_status = g_strdup (primary_status); status_data->detail_status = g_strdup (detail_status); status_data->slot = slot; /* waiting for half of the double-click-time before setting * the status seems to be a good approximation of not setting it * too often and not delaying the statusbar too much. */ slot->details->set_status_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) (double_click_time / 2), set_status_timeout_cb, status_data, set_status_data_free); } void nautilus_window_slot_set_status (NautilusWindowSlot *slot, const char *primary_status, const char *detail_status) { g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); if (slot->details->content_view != NULL) { set_floating_bar_status (slot, primary_status, detail_status); } } /* returns either the pending or the actual current uri */ char * nautilus_window_slot_get_current_uri (NautilusWindowSlot *slot) { GFile *location; location = nautilus_window_slot_get_current_location (slot); if (location != NULL) { return g_file_get_uri (location); } return NULL; } NautilusView * nautilus_window_slot_get_current_view (NautilusWindowSlot *slot) { if (slot->details->content_view != NULL) { return slot->details->content_view; } else if (slot->details->new_content_view) { return slot->details->new_content_view; } return NULL; } void nautilus_window_slot_go_home (NautilusWindowSlot *slot, NautilusWindowOpenFlags flags) { GFile *home; g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot)); home = g_file_new_for_path (g_get_home_dir ()); nautilus_window_slot_open_location (slot, home, flags); g_object_unref (home); } void nautilus_window_slot_go_up (NautilusWindowSlot *slot, NautilusWindowOpenFlags flags) { GFile *parent; if (slot->details->location == NULL) { return; } parent = g_file_get_parent (slot->details->location); if (parent == NULL) { return; } nautilus_window_slot_open_location (slot, parent, flags); g_object_unref (parent); } NautilusFile * nautilus_window_slot_get_file (NautilusWindowSlot *slot) { return slot->details->viewed_file; } NautilusBookmark * nautilus_window_slot_get_bookmark (NautilusWindowSlot *slot) { return slot->details->current_location_bookmark; } GList * nautilus_window_slot_get_back_history (NautilusWindowSlot *slot) { return slot->details->back_list; } GList * nautilus_window_slot_get_forward_history (NautilusWindowSlot *slot) { return slot->details->forward_list; } NautilusWindowSlot * nautilus_window_slot_new (NautilusWindow *window) { return g_object_new (NAUTILUS_TYPE_WINDOW_SLOT, "window", window, NULL); }