summaryrefslogtreecommitdiff
path: root/src/caja-window-manage-views.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/caja-window-manage-views.c')
-rw-r--r--src/caja-window-manage-views.c2337
1 files changed, 2337 insertions, 0 deletions
diff --git a/src/caja-window-manage-views.c b/src/caja-window-manage-views.c
new file mode 100644
index 00000000..df36f821
--- /dev/null
+++ b/src/caja-window-manage-views.c
@@ -0,0 +1,2337 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * Caja
+ *
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
+ *
+ * Caja 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.
+ *
+ * Caja is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors: Elliot Lee <[email protected]>
+ * John Sullivan <[email protected]>
+ * Darin Adler <[email protected]>
+ */
+
+#include <config.h>
+#include "caja-window-manage-views.h"
+
+#include "caja-actions.h"
+#include "caja-application.h"
+#include "caja-location-bar.h"
+#include "caja-search-bar.h"
+#include "caja-pathbar.h"
+#include "caja-main.h"
+#include "caja-window-private.h"
+#include "caja-window-slot.h"
+#include "caja-navigation-window-slot.h"
+#include "caja-trash-bar.h"
+#include "caja-x-content-bar.h"
+#include "caja-zoom-control.h"
+#include "caja-navigation-window-pane.h"
+#include <eel/eel-accessibility.h>
+#include <eel/eel-debug.h>
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+#include <libcaja-extension/caja-location-widget-provider.h>
+#include <libcaja-private/caja-debug-log.h>
+#include <libcaja-private/caja-file-attributes.h>
+#include <libcaja-private/caja-file-utilities.h>
+#include <libcaja-private/caja-file.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-metadata.h>
+#include <libcaja-private/caja-mime-actions.h>
+#include <libcaja-private/caja-module.h>
+#include <libcaja-private/caja-monitor.h>
+#include <libcaja-private/caja-search-directory.h>
+#include <libcaja-private/caja-view-factory.h>
+#include <libcaja-private/caja-window-info.h>
+#include <libcaja-private/caja-window-slot-info.h>
+#include <libcaja-private/caja-autorun.h>
+
+/* FIXME bugzilla.gnome.org 41243:
+ * We should use inheritance instead of these special cases
+ * for the desktop window.
+ */
+#include "caja-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 (CajaWindowSlot *slot,
+ GFile *location,
+ GList *new_selection,
+ CajaLocationChangeType type,
+ guint distance,
+ const char *scroll_pos);
+static void free_location_change (CajaWindowSlot *slot);
+static void end_location_change (CajaWindowSlot *slot);
+static void cancel_location_change (CajaWindowSlot *slot);
+static void got_file_info_for_view_selection_callback (CajaFile *file,
+ gpointer callback_data);
+static void create_content_view (CajaWindowSlot *slot,
+ const char *view_id);
+static void display_view_selection_failure (CajaWindow *window,
+ CajaFile *file,
+ GFile *location,
+ GError *error);
+static void load_new_location (CajaWindowSlot *slot,
+ GFile *location,
+ GList *selection,
+ gboolean tell_current_content_view,
+ gboolean tell_new_content_view);
+static void location_has_really_changed (CajaWindowSlot *slot);
+static void update_for_new_location (CajaWindowSlot *slot);
+
+void
+caja_window_report_selection_changed (CajaWindowInfo *window)
+{
+ if (window->details->temporarily_ignore_view_signals)
+ {
+ return;
+ }
+
+ g_signal_emit_by_name (window, "selection_changed");
+}
+
+/* set_displayed_location:
+ */
+static void
+set_displayed_location (CajaWindowSlot *slot, GFile *location)
+{
+ CajaWindow *window;
+ GFile *bookmark_location;
+ gboolean recreate;
+ char *name;
+
+ window = slot->pane->window;
+
+ if (slot->current_location_bookmark == NULL || location == NULL)
+ {
+ recreate = TRUE;
+ }
+ else
+ {
+ bookmark_location = caja_bookmark_get_location (slot->current_location_bookmark);
+ recreate = !g_file_equal (bookmark_location, location);
+ g_object_unref (bookmark_location);
+ }
+
+ if (recreate)
+ {
+ /* We've changed locations, must recreate bookmark for current location. */
+ if (slot->last_location_bookmark != NULL)
+ {
+ g_object_unref (slot->last_location_bookmark);
+ }
+ slot->last_location_bookmark = slot->current_location_bookmark;
+ name = g_file_get_basename (location);
+ slot->current_location_bookmark = (location == NULL) ? NULL
+ : caja_bookmark_new (location, name, FALSE, NULL);
+ g_free (name);
+ }
+}
+
+static void
+check_bookmark_location_matches (CajaBookmark *bookmark, GFile *location)
+{
+ GFile *bookmark_location;
+ char *bookmark_uri, *uri;
+
+ bookmark_location = caja_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 (CajaWindowSlot *slot)
+{
+ check_bookmark_location_matches (slot->last_location_bookmark,
+ slot->location);
+}
+
+static void
+handle_go_back (CajaNavigationWindowSlot *navigation_slot,
+ GFile *location)
+{
+ CajaWindowSlot *slot;
+ guint i;
+ GList *link;
+ CajaBookmark *bookmark;
+
+ slot = CAJA_WINDOW_SLOT (navigation_slot);
+
+ /* Going back. Move items from the back list to the forward list. */
+ g_assert (g_list_length (navigation_slot->back_list) > slot->location_change_distance);
+ check_bookmark_location_matches (CAJA_BOOKMARK (g_list_nth_data (navigation_slot->back_list,
+ slot->location_change_distance)),
+ location);
+ g_assert (slot->location != NULL);
+
+ /* Move current location to Forward list */
+
+ check_last_bookmark_location_matches_slot (slot);
+
+ /* Use the first bookmark in the history list rather than creating a new one. */
+ navigation_slot->forward_list = g_list_prepend (navigation_slot->forward_list,
+ slot->last_location_bookmark);
+ g_object_ref (navigation_slot->forward_list->data);
+
+ /* Move extra links from Back to Forward list */
+ for (i = 0; i < slot->location_change_distance; ++i)
+ {
+ bookmark = CAJA_BOOKMARK (navigation_slot->back_list->data);
+ navigation_slot->back_list =
+ g_list_remove (navigation_slot->back_list, bookmark);
+ navigation_slot->forward_list =
+ g_list_prepend (navigation_slot->forward_list, bookmark);
+ }
+
+ /* One bookmark falls out of back/forward lists and becomes viewed location */
+ link = navigation_slot->back_list;
+ navigation_slot->back_list = g_list_remove_link (navigation_slot->back_list, link);
+ g_object_unref (link->data);
+ g_list_free_1 (link);
+}
+
+static void
+handle_go_forward (CajaNavigationWindowSlot *navigation_slot,
+ GFile *location)
+{
+ CajaWindowSlot *slot;
+ guint i;
+ GList *link;
+ CajaBookmark *bookmark;
+
+ slot = CAJA_WINDOW_SLOT (navigation_slot);
+
+ /* Going forward. Move items from the forward list to the back list. */
+ g_assert (g_list_length (navigation_slot->forward_list) > slot->location_change_distance);
+ check_bookmark_location_matches (CAJA_BOOKMARK (g_list_nth_data (navigation_slot->forward_list,
+ slot->location_change_distance)),
+ location);
+ g_assert (slot->location != NULL);
+
+ /* Move current location to Back list */
+ check_last_bookmark_location_matches_slot (slot);
+
+ /* Use the first bookmark in the history list rather than creating a new one. */
+ navigation_slot->back_list = g_list_prepend (navigation_slot->back_list,
+ slot->last_location_bookmark);
+ g_object_ref (navigation_slot->back_list->data);
+
+ /* Move extra links from Forward to Back list */
+ for (i = 0; i < slot->location_change_distance; ++i)
+ {
+ bookmark = CAJA_BOOKMARK (navigation_slot->forward_list->data);
+ navigation_slot->forward_list =
+ g_list_remove (navigation_slot->back_list, bookmark);
+ navigation_slot->back_list =
+ g_list_prepend (navigation_slot->forward_list, bookmark);
+ }
+
+ /* One bookmark falls out of back/forward lists and becomes viewed location */
+ link = navigation_slot->forward_list;
+ navigation_slot->forward_list = g_list_remove_link (navigation_slot->forward_list, link);
+ g_object_unref (link->data);
+ g_list_free_1 (link);
+}
+
+static void
+handle_go_elsewhere (CajaWindowSlot *slot, GFile *location)
+{
+#if !NEW_UI_COMPLETE
+ CajaNavigationWindowSlot *navigation_slot;
+
+ if (CAJA_IS_NAVIGATION_WINDOW_SLOT (slot))
+ {
+ navigation_slot = CAJA_NAVIGATION_WINDOW_SLOT (slot);
+
+ /* Clobber the entire forward list, and move displayed location to back list */
+ caja_navigation_window_slot_clear_forward_list (navigation_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. */
+ navigation_slot->back_list = g_list_prepend (navigation_slot->back_list,
+ slot->last_location_bookmark);
+ g_object_ref (navigation_slot->back_list->data);
+ }
+ }
+ }
+#endif
+}
+
+void
+caja_window_update_up_button (CajaWindow *window)
+{
+ CajaWindowSlot *slot;
+ gboolean allowed;
+ GFile *parent;
+
+ slot = window->details->active_pane->active_slot;
+
+ allowed = FALSE;
+ if (slot->location != NULL)
+ {
+ parent = g_file_get_parent (slot->location);
+ allowed = parent != NULL;
+ if (parent != NULL)
+ {
+ g_object_unref (parent);
+ }
+ }
+
+ caja_window_allow_up (window, allowed);
+}
+
+static void
+viewed_file_changed_callback (CajaFile *file,
+ CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+ GFile *new_location;
+ gboolean is_in_trash, was_in_trash;
+
+ window = slot->pane->window;
+
+ g_assert (CAJA_IS_FILE (file));
+ g_assert (CAJA_IS_WINDOW_PANE (slot->pane));
+ g_assert (CAJA_IS_WINDOW (window));
+
+ g_assert (file == slot->viewed_file);
+
+ if (!caja_file_is_not_yet_confirmed (file))
+ {
+ slot->viewed_file_seen = TRUE;
+ }
+
+ was_in_trash = slot->viewed_file_in_trash;
+
+ slot->viewed_file_in_trash = is_in_trash = caja_file_is_in_trash (file);
+
+ /* Close window if the file it's viewing has been deleted or moved to trash. */
+ if (caja_file_is_gone (file) || (is_in_trash && !was_in_trash))
+ {
+ /* Don't close the window in the case where the
+ * file was never seen in the first place.
+ */
+ if (slot->viewed_file_seen)
+ {
+ /* Detecting a file is gone may happen in the
+ * middle of a pending location change, we
+ * need to cancel it before closing the window
+ * or things break.
+ */
+ /* FIXME: It makes no sense that this call is
+ * needed. When the window is destroyed, it
+ * calls caja_window_manage_views_destroy,
+ * which calls free_location_change, which
+ * should be sufficient. Also, if this was
+ * really needed, wouldn't it be needed for
+ * all other caja_window_close callers?
+ */
+ end_location_change (slot);
+
+ if (CAJA_IS_NAVIGATION_WINDOW (window))
+ {
+ /* auto-show existing parent. */
+ GFile *go_to_file, *parent, *location;
+
+ go_to_file = NULL;
+ location = caja_file_get_location (file);
+ parent = g_file_get_parent (location);
+ g_object_unref (location);
+ if (parent)
+ {
+ go_to_file = caja_find_existing_uri_in_hierarchy (parent);
+ g_object_unref (parent);
+ }
+
+ if (go_to_file != NULL)
+ {
+ /* the path bar URI will be set to go_to_uri immediately
+ * in begin_location_change, but we don't want the
+ * inexistant children to show up anymore */
+ if (slot == slot->pane->active_slot)
+ {
+ /* multiview-TODO also update CajaWindowSlot
+ * [which as of writing doesn't save/store any path bar state]
+ */
+ caja_path_bar_clear_buttons (CAJA_PATH_BAR (CAJA_NAVIGATION_WINDOW_PANE (slot->pane)->path_bar));
+ }
+
+ caja_window_slot_go_to (slot, go_to_file, FALSE);
+ g_object_unref (go_to_file);
+ }
+ else
+ {
+ caja_window_slot_go_home (slot, FALSE);
+ }
+ }
+ else
+ {
+ caja_window_close (window);
+ }
+ }
+ }
+ else
+ {
+ new_location = caja_file_get_location (file);
+
+ /* If the file was renamed, update location and/or
+ * title. */
+ if (!g_file_equal (new_location,
+ slot->location))
+ {
+ g_object_unref (slot->location);
+ slot->location = new_location;
+ if (slot == slot->pane->active_slot)
+ {
+ caja_window_pane_sync_location_widgets (slot->pane);
+ }
+ }
+ else
+ {
+ /* TODO?
+ * why do we update title & icon at all in this case? */
+ g_object_unref (new_location);
+ }
+
+ caja_window_slot_update_title (slot);
+ caja_window_slot_update_icon (slot);
+ }
+}
+
+static void
+update_history (CajaWindowSlot *slot,
+ CajaLocationChangeType type,
+ GFile *new_location)
+{
+ switch (type)
+ {
+ case CAJA_LOCATION_CHANGE_STANDARD:
+ case CAJA_LOCATION_CHANGE_FALLBACK:
+ caja_window_slot_add_current_location_to_history_list (slot);
+ handle_go_elsewhere (slot, new_location);
+ return;
+ case CAJA_LOCATION_CHANGE_RELOAD:
+ /* for reload there is no work to do */
+ return;
+ case CAJA_LOCATION_CHANGE_BACK:
+ caja_window_slot_add_current_location_to_history_list (slot);
+ handle_go_back (CAJA_NAVIGATION_WINDOW_SLOT (slot), new_location);
+ return;
+ case CAJA_LOCATION_CHANGE_FORWARD:
+ caja_window_slot_add_current_location_to_history_list (slot);
+ handle_go_forward (CAJA_NAVIGATION_WINDOW_SLOT (slot), new_location);
+ return;
+ case CAJA_LOCATION_CHANGE_REDIRECT:
+ /* for the redirect case, the caller can do the updating */
+ return;
+ }
+ g_return_if_fail (FALSE);
+}
+
+static void
+cancel_viewed_file_changed_callback (CajaWindowSlot *slot)
+{
+ CajaFile *file;
+
+ file = slot->viewed_file;
+ if (file != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (file),
+ G_CALLBACK (viewed_file_changed_callback),
+ slot);
+ caja_file_monitor_remove (file, &slot->viewed_file);
+ }
+}
+
+static void
+new_window_show_callback (GtkWidget *widget,
+ gpointer user_data)
+{
+ CajaWindow *window;
+
+ window = CAJA_WINDOW (user_data);
+
+ caja_window_close (window);
+
+ g_signal_handlers_disconnect_by_func (widget,
+ G_CALLBACK (new_window_show_callback),
+ user_data);
+}
+
+
+void
+caja_window_slot_open_location_full (CajaWindowSlot *slot,
+ GFile *location,
+ CajaWindowOpenMode mode,
+ CajaWindowOpenFlags flags,
+ GList *new_selection)
+{
+ CajaWindow *window;
+ CajaWindow *target_window;
+ CajaWindowPane *pane;
+ CajaWindowSlot *target_slot;
+ CajaWindowOpenFlags slot_flags;
+ gboolean do_load_location = TRUE;
+ GFile *old_location;
+ char *old_uri, *new_uri;
+ int new_slot_position;
+ GList *l;
+
+ window = slot->pane->window;
+
+ target_window = NULL;
+ target_slot = NULL;
+
+ old_uri = caja_window_slot_get_location_uri (slot);
+ if (old_uri == NULL)
+ {
+ old_uri = g_strdup ("(none)");
+ }
+ new_uri = g_file_get_uri (location);
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "window %p open location: old=\"%s\", new=\"%s\"",
+ window,
+ old_uri,
+ new_uri);
+ g_free (old_uri);
+ g_free (new_uri);
+
+ g_assert (!((flags & CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0 &&
+ (flags & CAJA_WINDOW_OPEN_FLAG_NEW_TAB) != 0));
+
+
+ old_location = caja_window_slot_get_location (slot);
+ switch (mode)
+ {
+ case CAJA_WINDOW_OPEN_ACCORDING_TO_MODE :
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER))
+ {
+ target_window = window;
+ if (CAJA_IS_SPATIAL_WINDOW (window))
+ {
+ if (!CAJA_SPATIAL_WINDOW (window)->affect_spatial_window_on_next_location_change)
+ {
+ target_window = caja_application_create_navigation_window
+ (window->application,
+ NULL,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ }
+ else
+ {
+ CAJA_SPATIAL_WINDOW (window)->affect_spatial_window_on_next_location_change = FALSE;
+ }
+ }
+ else if ((flags & CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0)
+ {
+ target_window = caja_application_create_navigation_window
+ (window->application,
+ NULL,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ }
+ }
+ else if (CAJA_IS_SPATIAL_WINDOW (window))
+ {
+ if (!CAJA_SPATIAL_WINDOW (window)->affect_spatial_window_on_next_location_change)
+ {
+ target_window = caja_application_present_spatial_window_with_selection (
+ window->application,
+ window,
+ NULL,
+ location,
+ new_selection,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ do_load_location = FALSE;
+ }
+ else
+ {
+ CAJA_SPATIAL_WINDOW (window)->affect_spatial_window_on_next_location_change = FALSE;
+ target_window = window;
+ }
+ }
+ else if (flags & CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW)
+ {
+ target_window = caja_application_create_navigation_window
+ (window->application,
+ NULL,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ }
+ else
+ {
+ target_window = window;
+ }
+ break;
+ case CAJA_WINDOW_OPEN_IN_SPATIAL :
+ target_window = caja_application_present_spatial_window (
+ window->application,
+ window,
+ NULL,
+ location,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ break;
+ case CAJA_WINDOW_OPEN_IN_NAVIGATION :
+ target_window = caja_application_create_navigation_window
+ (window->application,
+ NULL,
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ break;
+ default :
+ g_warning ("Unknown open location mode");
+ g_object_unref (old_location);
+ return;
+ }
+
+ g_assert (target_window != NULL);
+
+ if ((flags & CAJA_WINDOW_OPEN_FLAG_NEW_TAB) != 0 &&
+ CAJA_IS_NAVIGATION_WINDOW (window))
+ {
+ g_assert (target_window == window);
+
+ slot_flags = 0;
+
+ new_slot_position = eel_preferences_get_enum (CAJA_PREFERENCES_NEW_TAB_POSITION);
+ if (new_slot_position == CAJA_NEW_TAB_POSITION_END)
+ {
+ slot_flags = CAJA_WINDOW_OPEN_SLOT_APPEND;
+ }
+
+ target_slot = caja_window_open_slot (window->details->active_pane, slot_flags);
+ }
+
+ if ((flags & CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0)
+ {
+ if (CAJA_IS_SPATIAL_WINDOW (window) && !CAJA_IS_DESKTOP_WINDOW (window))
+ {
+ if (gtk_widget_get_visible (GTK_WIDGET (target_window)))
+ {
+ caja_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 = target_window->details->active_pane->active_slot;
+ }
+ }
+
+ if ((!do_load_location) ||
+ (target_window == window && target_slot == slot &&
+ old_location && g_file_equal (old_location, location)))
+ {
+ if (old_location)
+ {
+ g_object_unref (old_location);
+ }
+ return;
+ }
+
+ if (old_location)
+ {
+ g_object_unref (old_location);
+ }
+
+ begin_location_change (target_slot, location, new_selection,
+ CAJA_LOCATION_CHANGE_STANDARD, 0, NULL);
+
+ /* Additionally, load this in all slots that have no location, this means
+ we load both panes in e.g. a newly opened dual pane window. */
+ for (l = target_window->details->panes; l != NULL; l = l->next)
+ {
+ pane = l->data;
+ slot = pane->active_slot;
+ if (slot->location == NULL && slot->pending_location == NULL)
+ {
+ begin_location_change (slot, location, new_selection,
+ CAJA_LOCATION_CHANGE_STANDARD, 0, NULL);
+ }
+ }
+}
+
+void
+caja_window_slot_open_location (CajaWindowSlot *slot,
+ GFile *location,
+ gboolean close_behind)
+{
+ CajaWindowOpenFlags flags;
+
+ flags = 0;
+ if (close_behind)
+ {
+ flags = CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND;
+ }
+
+ caja_window_slot_open_location_full (slot, location,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ flags, NULL);
+}
+
+void
+caja_window_slot_open_location_with_selection (CajaWindowSlot *slot,
+ GFile *location,
+ GList *selection,
+ gboolean close_behind)
+{
+ CajaWindowOpenFlags flags;
+
+ flags = 0;
+ if (close_behind)
+ {
+ flags = CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND;
+ }
+ caja_window_slot_open_location_full (slot, location,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ flags, selection);
+}
+
+
+void
+caja_window_slot_go_home (CajaWindowSlot *slot, gboolean new_tab)
+{
+ GFile *home;
+ CajaWindowOpenFlags flags;
+
+ g_return_if_fail (CAJA_IS_WINDOW_SLOT (slot));
+
+ if (new_tab)
+ {
+ flags = CAJA_WINDOW_OPEN_FLAG_NEW_TAB;
+ }
+ else
+ {
+ flags = 0;
+ }
+
+ home = g_file_new_for_path (g_get_home_dir ());
+ caja_window_slot_open_location_full (slot, home,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ flags, NULL);
+ g_object_unref (home);
+}
+
+#if 0
+static char *
+caja_window_slot_get_view_label (CajaWindowSlot *slot)
+{
+ const CajaViewInfo *info;
+
+ info = caja_view_factory_lookup (caja_window_slot_get_content_view_id (slot));
+
+ return g_strdup (info->label);
+}
+#endif
+
+static char *
+caja_window_slot_get_view_error_label (CajaWindowSlot *slot)
+{
+ const CajaViewInfo *info;
+
+ info = caja_view_factory_lookup (caja_window_slot_get_content_view_id (slot));
+
+ return g_strdup (info->error_label);
+}
+
+static char *
+caja_window_slot_get_view_startup_error_label (CajaWindowSlot *slot)
+{
+ const CajaViewInfo *info;
+
+ info = caja_view_factory_lookup (caja_window_slot_get_content_view_id (slot));
+
+ return g_strdup (info->startup_error_label);
+}
+
+static void
+report_current_content_view_failure_to_user (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+ char *message;
+
+ window = slot->pane->window;
+
+ message = caja_window_slot_get_view_startup_error_label (slot);
+ eel_show_error_dialog (message,
+ _("You can choose another view or go to a different location."),
+ GTK_WINDOW (window));
+ g_free (message);
+}
+
+static void
+report_nascent_content_view_failure_to_user (CajaWindowSlot *slot,
+ CajaView *view)
+{
+ CajaWindow *window;
+ char *message;
+
+ window = slot->pane->window;
+
+ /* TODO? why are we using the current view's error label here, instead of the next view's?
+ * This behavior has already been present in pre-slot days.
+ */
+ message = caja_window_slot_get_view_error_label (slot);
+ eel_show_error_dialog (message,
+ _("The location cannot be displayed with this viewer."),
+ GTK_WINDOW (window));
+ g_free (message);
+}
+
+
+const char *
+caja_window_slot_get_content_view_id (CajaWindowSlot *slot)
+{
+ if (slot->content_view == NULL)
+ {
+ return NULL;
+ }
+ return caja_view_get_view_id (slot->content_view);
+}
+
+gboolean
+caja_window_slot_content_view_matches_iid (CajaWindowSlot *slot,
+ const char *iid)
+{
+ if (slot->content_view == NULL)
+ {
+ return FALSE;
+ }
+ return eel_strcmp (caja_view_get_view_id (slot->content_view), iid) == 0;
+}
+
+
+/*
+ * begin_location_change
+ *
+ * Change a window's location.
+ * @window: The CajaWindow whose location should be changed.
+ * @location: A url specifying the location to load
+ * @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 (CajaWindowSlot *slot,
+ GFile *location,
+ GList *new_selection,
+ CajaLocationChangeType type,
+ guint distance,
+ const char *scroll_pos)
+{
+ CajaWindow *window;
+ CajaDirectory *directory;
+ CajaFile *file;
+ gboolean force_reload;
+ char *current_pos;
+
+ g_assert (slot != NULL);
+ g_assert (location != NULL);
+ g_assert (type == CAJA_LOCATION_CHANGE_BACK
+ || type == CAJA_LOCATION_CHANGE_FORWARD
+ || distance == 0);
+
+ window = slot->pane->window;
+ g_assert (CAJA_IS_WINDOW (window));
+ g_object_ref (window);
+
+ end_location_change (slot);
+
+ caja_window_slot_set_allow_stop (slot, TRUE);
+ caja_window_slot_set_status (slot, " ");
+
+ g_assert (slot->pending_location == NULL);
+ g_assert (slot->pending_selection == NULL);
+
+ slot->pending_location = g_object_ref (location);
+ slot->location_change_type = type;
+ slot->location_change_distance = distance;
+ slot->tried_mount = FALSE;
+ slot->pending_selection = eel_g_object_list_copy (new_selection);
+
+ slot->pending_scroll_to = g_strdup (scroll_pos);
+
+ directory = caja_directory_get (location);
+
+ /* 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 == CAJA_LOCATION_CHANGE_RELOAD)
+ {
+ force_reload = TRUE;
+ }
+ else if (!caja_monitor_active ())
+ {
+ force_reload = TRUE;
+ }
+ else
+ {
+ force_reload = !caja_directory_is_local (directory);
+ }
+
+ if (force_reload)
+ {
+ caja_directory_force_reload (directory);
+ file = caja_directory_get_corresponding_file (directory);
+ caja_file_invalidate_all_attributes (file);
+ caja_file_unref (file);
+ }
+
+ caja_directory_unref (directory);
+
+ /* Set current_bookmark scroll pos */
+ if (slot->current_location_bookmark != NULL &&
+ slot->content_view != NULL)
+ {
+ current_pos = caja_view_get_first_visible_file (slot->content_view);
+ caja_bookmark_set_scroll_pos (slot->current_location_bookmark, current_pos);
+ g_free (current_pos);
+ }
+
+ /* Get the info needed for view selection */
+
+ slot->determine_view_file = caja_file_get (location);
+ g_assert (slot->determine_view_file != NULL);
+
+ /* if the currently viewed file is marked gone while loading the new location,
+ * this ensures that the window isn't destroyed */
+ cancel_viewed_file_changed_callback (slot);
+
+ caja_file_call_when_ready (slot->determine_view_file,
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT,
+ got_file_info_for_view_selection_callback,
+ slot);
+
+ g_object_unref (window);
+}
+
+static void
+setup_new_spatial_window (CajaWindowSlot *slot, CajaFile *file)
+{
+ CajaWindow *window;
+ char *show_hidden_file_setting;
+ char *geometry_string;
+ char *scroll_string;
+ gboolean maximized, sticky, above;
+ GtkAction *action;
+
+ window = slot->pane->window;
+
+ if (CAJA_IS_SPATIAL_WINDOW (window) && !CAJA_IS_DESKTOP_WINDOW (window))
+ {
+ /* load show hidden state */
+ show_hidden_file_setting = caja_file_get_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_SHOW_HIDDEN_FILES,
+ NULL);
+ if (show_hidden_file_setting != NULL)
+ {
+ if (strcmp (show_hidden_file_setting, "1") == 0)
+ {
+ window->details->show_hidden_files_mode = CAJA_WINDOW_SHOW_HIDDEN_FILES_ENABLE;
+ }
+ else
+ {
+ window->details->show_hidden_files_mode = CAJA_WINDOW_SHOW_HIDDEN_FILES_DISABLE;
+ }
+
+ /* Update the UI, since we initialize it to the default */
+ action = gtk_action_group_get_action (window->details->main_action_group, CAJA_ACTION_SHOW_HIDDEN_FILES);
+ gtk_action_block_activate (action);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ window->details->show_hidden_files_mode == CAJA_WINDOW_SHOW_HIDDEN_FILES_ENABLE);
+ gtk_action_unblock_activate (action);
+ }
+ else
+ {
+ CAJA_WINDOW (window)->details->show_hidden_files_mode = CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT;
+ }
+ g_free (show_hidden_file_setting);
+
+ /* load the saved window geometry */
+ maximized = caja_file_get_boolean_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_MAXIMIZED, FALSE);
+ if (maximized)
+ {
+ gtk_window_maximize (GTK_WINDOW (window));
+ }
+ else
+ {
+ gtk_window_unmaximize (GTK_WINDOW (window));
+ }
+
+ sticky = caja_file_get_boolean_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_STICKY, FALSE);
+ if (sticky)
+ {
+ gtk_window_stick (GTK_WINDOW (window));
+ }
+ else
+ {
+ gtk_window_unstick (GTK_WINDOW (window));
+ }
+
+ above = caja_file_get_boolean_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_KEEP_ABOVE, FALSE);
+ if (above)
+ {
+ gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
+ }
+ else
+ {
+ gtk_window_set_keep_above (GTK_WINDOW (window), FALSE);
+ }
+
+ geometry_string = caja_file_get_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_GEOMETRY, NULL);
+ if (geometry_string != NULL)
+ {
+ eel_gtk_window_set_initial_geometry_from_string
+ (GTK_WINDOW (window),
+ geometry_string,
+ CAJA_SPATIAL_WINDOW_MIN_WIDTH,
+ CAJA_SPATIAL_WINDOW_MIN_HEIGHT,
+ FALSE);
+ }
+ g_free (geometry_string);
+
+ if (slot->pending_selection == NULL)
+ {
+ /* If there is no pending selection, then load the saved scroll position. */
+ scroll_string = caja_file_get_metadata
+ (file, CAJA_METADATA_KEY_WINDOW_SCROLL_POSITION,
+ NULL);
+ }
+ else
+ {
+ /* If there is a pending selection, we want to scroll to an item in
+ * the pending selection list. */
+ scroll_string = g_file_get_uri (slot->pending_selection->data);
+ }
+
+ /* scroll_string might be NULL if there was no saved scroll position. */
+ if (scroll_string != NULL)
+ {
+ slot->pending_scroll_to = scroll_string;
+ }
+ }
+}
+
+typedef struct
+{
+ GCancellable *cancellable;
+ CajaWindowSlot *slot;
+} MountNotMountedData;
+
+static void
+mount_not_mounted_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ MountNotMountedData *data;
+ CajaWindow *window;
+ CajaWindowSlot *slot;
+ GError *error;
+ GCancellable *cancellable;
+
+ data = user_data;
+ slot = data->slot;
+ window = slot->pane->window;
+ cancellable = data->cancellable;
+ g_free (data);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ {
+ /* Cancelled, don't call back */
+ g_object_unref (cancellable);
+ return;
+ }
+
+ slot->mount_cancellable = NULL;
+
+ slot->determine_view_file = caja_file_get (slot->pending_location);
+
+ error = NULL;
+ if (!g_file_mount_enclosing_volume_finish (G_FILE (source_object), res, &error))
+ {
+ slot->mount_error = error;
+ got_file_info_for_view_selection_callback (slot->determine_view_file, slot);
+ slot->mount_error = NULL;
+ g_error_free (error);
+ }
+ else
+ {
+ caja_file_invalidate_all_attributes (slot->determine_view_file);
+ caja_file_call_when_ready (slot->determine_view_file,
+ CAJA_FILE_ATTRIBUTE_INFO,
+ got_file_info_for_view_selection_callback,
+ slot);
+ }
+
+ g_object_unref (cancellable);
+}
+
+static void
+got_file_info_for_view_selection_callback (CajaFile *file,
+ gpointer callback_data)
+{
+ GError *error;
+ char *view_id;
+ char *mimetype;
+ CajaWindow *window;
+ CajaWindowSlot *slot;
+ CajaFile *viewed_file;
+ GFile *location;
+ GMountOperation *mount_op;
+ MountNotMountedData *data;
+
+ slot = callback_data;
+ g_assert (CAJA_IS_WINDOW_SLOT (slot));
+ g_assert (slot->determine_view_file == file);
+
+ window = slot->pane->window;
+ g_assert (CAJA_IS_WINDOW (window));
+
+ slot->determine_view_file = NULL;
+
+ if (slot->mount_error)
+ {
+ error = slot->mount_error;
+ }
+ else
+ {
+ error = caja_file_get_file_info_error (file);
+ }
+
+ if (error && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED &&
+ !slot->tried_mount)
+ {
+ slot->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 = caja_file_get_location (file);
+ data = g_new0 (MountNotMountedData, 1);
+ data->cancellable = g_cancellable_new ();
+ data->slot = slot;
+ slot->mount_cancellable = data->cancellable;
+ g_file_mount_enclosing_volume (location, 0, mount_op, slot->mount_cancellable,
+ mount_not_mounted_callback, data);
+ g_object_unref (location);
+ g_object_unref (mount_op);
+
+ caja_file_unref (file);
+
+ return;
+ }
+
+ location = slot->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: */
+
+ mimetype = caja_file_get_mime_type (file);
+
+ /* If fallback, don't use view from metadata */
+ if (slot->location_change_type != CAJA_LOCATION_CHANGE_FALLBACK)
+ {
+ /* Look in metadata for view */
+ view_id = caja_file_get_metadata
+ (file, CAJA_METADATA_KEY_DEFAULT_VIEW, NULL);
+ if (view_id != NULL &&
+ !caja_view_factory_view_supports_uri (view_id,
+ location,
+ caja_file_get_file_type (file),
+ mimetype))
+ {
+ g_free (view_id);
+ view_id = NULL;
+ }
+ }
+
+ /* Otherwise, use default */
+ if (view_id == NULL)
+ {
+ view_id = caja_global_preferences_get_default_folder_viewer_preference_as_iid ();
+
+ if (view_id != NULL &&
+ !caja_view_factory_view_supports_uri (view_id,
+ location,
+ caja_file_get_file_type (file),
+ mimetype))
+ {
+ g_free (view_id);
+ view_id = NULL;
+ }
+ }
+
+ g_free (mimetype);
+ }
+
+ if (view_id != NULL)
+ {
+ if (!gtk_widget_get_visible (GTK_WIDGET (window)) && CAJA_IS_SPATIAL_WINDOW (window))
+ {
+ /* We now have the metadata to set up the window position, etc */
+ setup_new_spatial_window (slot, file);
+ }
+ create_content_view (slot, view_id);
+ g_free (view_id);
+ }
+ else
+ {
+ 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 */
+ if (caja_application_get_n_windows () <= 1)
+ {
+ g_assert (caja_application_get_n_windows () == 1);
+
+ /* Make sure we re-use this window */
+ if (CAJA_IS_SPATIAL_WINDOW (window))
+ {
+ CAJA_SPATIAL_WINDOW (window)->affect_spatial_window_on_next_location_change = TRUE;
+ }
+ /* 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 (!caja_is_root_directory (location))
+ {
+ if (!caja_is_home_directory (location))
+ {
+ caja_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! */
+ caja_window_slot_go_to (slot, location, FALSE);
+ g_object_unref (root);
+ }
+ }
+ else
+ {
+ gtk_object_destroy (GTK_OBJECT (window));
+ }
+ }
+ else
+ {
+ /* Since this is a window, destroying it will also unref it. */
+ gtk_object_destroy (GTK_OBJECT (window));
+ }
+ }
+ else
+ {
+ /* Clean up state of already-showing window */
+ end_location_change (slot);
+
+ /* TODO? shouldn't we call
+ * cancel_viewed_file_changed_callback (slot);
+ * at this point, or in end_location_change()
+ */
+ /* We're missing a previous location (if opened location
+ * in a new tab) so close it and return */
+ if (slot->location == NULL)
+ {
+ caja_window_slot_close (slot);
+ }
+ else
+ {
+ /* We disconnected this, so we need to re-connect it */
+ viewed_file = caja_file_get (slot->location);
+ caja_window_slot_set_viewed_file (slot, viewed_file);
+ caja_file_monitor_add (viewed_file, &slot->viewed_file, 0);
+ g_signal_connect_object (viewed_file, "changed",
+ G_CALLBACK (viewed_file_changed_callback), slot, 0);
+ caja_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.
+ */
+ }
+ }
+ }
+
+ caja_file_unref (file);
+}
+
+/* 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 void
+create_content_view (CajaWindowSlot *slot,
+ const char *view_id)
+{
+ CajaWindow *window;
+ CajaView *view;
+ GList *selection;
+
+ window = slot->pane->window;
+
+ /* FIXME bugzilla.gnome.org 41243:
+ * We should use inheritance instead of these special cases
+ * for the desktop window.
+ */
+ if (CAJA_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 = CAJA_DESKTOP_ICON_VIEW_IID;
+ }
+
+ if (slot->content_view != NULL &&
+ eel_strcmp (caja_view_get_view_id (slot->content_view),
+ view_id) == 0)
+ {
+ /* reuse existing content view */
+ view = slot->content_view;
+ slot->new_content_view = view;
+ g_object_ref (view);
+ }
+ else
+ {
+ /* create a new content view */
+ view = caja_view_factory_create (view_id,
+ CAJA_WINDOW_SLOT_INFO (slot));
+
+ eel_accessibility_set_name (view, _("Content View"));
+ eel_accessibility_set_description (view, _("View of the current folder"));
+
+ slot->new_content_view = view;
+ caja_window_slot_connect_content_view (slot, slot->new_content_view);
+ }
+
+ /* Actually load the pending location and selection: */
+
+ if (slot->pending_location != NULL)
+ {
+ load_new_location (slot,
+ slot->pending_location,
+ slot->pending_selection,
+ FALSE,
+ TRUE);
+
+ eel_g_object_list_free (slot->pending_selection);
+ slot->pending_selection = NULL;
+ }
+ else if (slot->location != NULL)
+ {
+ selection = caja_view_get_selection (slot->content_view);
+ load_new_location (slot,
+ slot->location,
+ selection,
+ FALSE,
+ TRUE);
+ eel_g_object_list_free (selection);
+ }
+ else
+ {
+ /* Something is busted, there was no location to load.
+ Just load the homedir. */
+ caja_window_slot_go_home (slot, FALSE);
+
+ }
+}
+
+static void
+load_new_location (CajaWindowSlot *slot,
+ GFile *location,
+ GList *selection,
+ gboolean tell_current_content_view,
+ gboolean tell_new_content_view)
+{
+ CajaWindow *window;
+ GList *selection_copy;
+ CajaView *view;
+ char *uri;
+
+ g_assert (slot != NULL);
+ g_assert (location != NULL);
+
+ window = slot->pane->window;
+ g_assert (CAJA_IS_WINDOW (window));
+
+ selection_copy = eel_g_object_list_copy (selection);
+
+ view = NULL;
+
+ /* Note, these may recurse into report_load_underway */
+ if (slot->content_view != NULL && tell_current_content_view)
+ {
+ view = slot->content_view;
+ uri = g_file_get_uri (location);
+ caja_view_load_location (slot->content_view, uri);
+ g_free (uri);
+ }
+
+ if (slot->new_content_view != NULL && tell_new_content_view &&
+ (!tell_current_content_view ||
+ slot->new_content_view != slot->content_view) )
+ {
+ view = slot->new_content_view;
+ uri = g_file_get_uri (location);
+ caja_view_load_location (slot->new_content_view, uri);
+ g_free (uri);
+ }
+ if (view != NULL)
+ {
+ /* slot->new_content_view might have changed here if
+ report_load_underway was called from load_location */
+ caja_view_set_selection (view, selection_copy);
+ }
+
+ eel_g_object_list_free (selection_copy);
+}
+
+/* A view started to load the location its viewing, either due to
+ * a load_location request, or some internal reason. Expect
+ * a matching load_compete later
+ */
+void
+caja_window_report_load_underway (CajaWindow *window,
+ CajaView *view)
+{
+ CajaWindowSlot *slot;
+
+ g_assert (CAJA_IS_WINDOW (window));
+
+ if (window->details->temporarily_ignore_view_signals)
+ {
+ return;
+ }
+
+ slot = caja_window_get_slot_for_view (window, view);
+ g_assert (slot != NULL);
+
+ if (view == slot->new_content_view)
+ {
+ location_has_really_changed (slot);
+ }
+ else
+ {
+ caja_window_slot_set_allow_stop (slot, TRUE);
+ }
+}
+
+static void
+caja_window_emit_location_change (CajaWindow *window,
+ GFile *location)
+{
+ char *uri;
+
+ uri = g_file_get_uri (location);
+ g_signal_emit_by_name (window, "loading_uri", uri);
+ g_free (uri);
+}
+
+/* reports location change to window's "loading-uri" clients, i.e.
+ * sidebar panels [used when switching tabs]. It will emit the pending
+ * location, or the existing location if none is pending.
+ */
+void
+caja_window_report_location_change (CajaWindow *window)
+{
+ CajaWindowSlot *slot;
+ GFile *location;
+
+ g_assert (CAJA_IS_WINDOW (window));
+
+ slot = window->details->active_pane->active_slot;
+ g_assert (CAJA_IS_WINDOW_SLOT (slot));
+
+ location = NULL;
+
+ if (slot->pending_location != NULL)
+ {
+ location = slot->pending_location;
+ }
+
+ if (location == NULL && slot->location != NULL)
+ {
+ location = slot->location;
+ }
+
+ if (location != NULL)
+ {
+ caja_window_emit_location_change (window, location);
+ }
+}
+
+/* This is called when we have decided we can actually change to the new view/location situation. */
+static void
+location_has_really_changed (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+ GtkWidget *widget;
+ GFile *location_copy;
+
+ window = slot->pane->window;
+
+ if (slot->new_content_view != NULL)
+ {
+ widget = caja_view_get_widget (slot->new_content_view);
+ /* Switch to the new content view. */
+ if (gtk_widget_get_parent (widget) == NULL)
+ {
+ if (slot->content_view != NULL)
+ {
+ caja_window_slot_disconnect_content_view (slot, slot->content_view);
+ }
+ caja_window_slot_set_content_view_widget (slot, slot->new_content_view);
+ }
+ g_object_unref (slot->new_content_view);
+ slot->new_content_view = NULL;
+ }
+
+ if (slot->pending_location != NULL)
+ {
+ /* Tell the window we are finished. */
+ update_for_new_location (slot);
+ }
+
+ location_copy = NULL;
+ if (slot->location != NULL)
+ {
+ location_copy = g_object_ref (slot->location);
+ }
+
+ free_location_change (slot);
+
+ if (location_copy != NULL)
+ {
+ if (slot == caja_window_get_active_slot (window))
+ {
+ caja_window_emit_location_change (window, location_copy);
+ }
+
+ g_object_unref (location_copy);
+ }
+}
+
+static void
+slot_add_extension_extra_widgets (CajaWindowSlot *slot)
+{
+ GList *providers, *l;
+ GtkWidget *widget;
+ char *uri;
+
+ providers = caja_module_get_extensions_for_type (CAJA_TYPE_LOCATION_WIDGET_PROVIDER);
+
+ uri = g_file_get_uri (slot->location);
+ for (l = providers; l != NULL; l = l->next)
+ {
+ CajaLocationWidgetProvider *provider;
+
+ provider = CAJA_LOCATION_WIDGET_PROVIDER (l->data);
+ widget = caja_location_widget_provider_get_widget (provider, uri, GTK_WIDGET (slot->pane->window));
+ if (widget != NULL)
+ {
+ caja_window_slot_add_extra_location_widget (slot, widget);
+ }
+ }
+ g_free (uri);
+
+ caja_module_extension_list_free (providers);
+}
+
+static void
+caja_window_slot_show_x_content_bar (CajaWindowSlot *slot, GMount *mount, const char **x_content_types)
+{
+ unsigned int n;
+
+ g_assert (CAJA_IS_WINDOW_SLOT (slot));
+
+ for (n = 0; x_content_types[n] != NULL; n++)
+ {
+ GAppInfo *default_app;
+
+ /* skip blank media; the burn:/// location will provide it's own cluebar */
+ if (g_str_has_prefix (x_content_types[n], "x-content/blank-"))
+ {
+ continue;
+ }
+
+ /* don't show the cluebar for windows software */
+ if (g_content_type_is_a (x_content_types[n], "x-content/win32-software"))
+ {
+ continue;
+ }
+
+ /* only show the cluebar if a default app is available */
+ default_app = g_app_info_get_default_for_type (x_content_types[n], FALSE);
+ if (default_app != NULL)
+ {
+ GtkWidget *bar;
+ bar = caja_x_content_bar_new (mount, x_content_types[n]);
+ gtk_widget_show (bar);
+ caja_window_slot_add_extra_location_widget (slot, bar);
+ g_object_unref (default_app);
+ }
+ }
+}
+
+static void
+caja_window_slot_show_trash_bar (CajaWindowSlot *slot,
+ CajaWindow *window)
+{
+ GtkWidget *bar;
+
+ bar = caja_trash_bar_new (window);
+ gtk_widget_show (bar);
+
+ caja_window_slot_add_extra_location_widget (slot, bar);
+}
+
+typedef struct
+{
+ CajaWindowSlot *slot;
+ GCancellable *cancellable;
+ GMount *mount;
+} FindMountData;
+
+static void
+found_content_type_cb (const char **x_content_types, FindMountData *data)
+{
+ CajaWindowSlot *slot;
+
+ if (g_cancellable_is_cancelled (data->cancellable))
+ {
+ goto out;
+ }
+
+ slot = data->slot;
+
+ if (x_content_types != NULL && x_content_types[0] != NULL)
+ {
+ caja_window_slot_show_x_content_bar (slot, data->mount, x_content_types);
+ }
+
+ slot->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;
+ CajaWindowSlot *slot;
+
+ if (g_cancellable_is_cancelled (data->cancellable))
+ {
+ goto out;
+ }
+
+ slot = data->slot;
+
+ mount = g_file_find_enclosing_mount_finish (G_FILE (source_object),
+ res,
+ NULL);
+ if (mount != NULL)
+ {
+ data->mount = mount;
+ caja_autorun_get_x_content_types_for_mount_async (mount,
+ (CajaAutorunGetContent)found_content_type_cb,
+ data->cancellable,
+ data);
+ return;
+ }
+
+ data->slot->find_mount_cancellable = NULL;
+
+out:
+ g_object_unref (data->cancellable);
+ g_free (data);
+}
+
+/* Handle the changes for the CajaWindow itself. */
+static void
+update_for_new_location (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+ GFile *new_location;
+ CajaFile *file;
+ CajaDirectory *directory;
+ gboolean location_really_changed;
+ FindMountData *data;
+
+ window = slot->pane->window;
+
+ new_location = slot->pending_location;
+ slot->pending_location = NULL;
+
+ set_displayed_location (slot, new_location);
+
+ update_history (slot, slot->location_change_type, new_location);
+
+ location_really_changed =
+ slot->location == NULL ||
+ !g_file_equal (slot->location, new_location);
+
+ /* Set the new location. */
+ if (slot->location)
+ {
+ g_object_unref (slot->location);
+ }
+ slot->location = new_location;
+
+ /* Create a CajaFile for this location, so we can catch it
+ * if it goes away.
+ */
+ cancel_viewed_file_changed_callback (slot);
+ file = caja_file_get (slot->location);
+ caja_window_slot_set_viewed_file (slot, file);
+ slot->viewed_file_seen = !caja_file_is_not_yet_confirmed (file);
+ slot->viewed_file_in_trash = caja_file_is_in_trash (file);
+ caja_file_monitor_add (file, &slot->viewed_file, 0);
+ g_signal_connect_object (file, "changed",
+ G_CALLBACK (viewed_file_changed_callback), slot, 0);
+ caja_file_unref (file);
+
+ if (slot == window->details->active_pane->active_slot)
+ {
+ /* Check if we can go up. */
+ caja_window_update_up_button (window);
+
+ caja_window_sync_zoom_widgets (window);
+
+ /* Set up the content view menu for this new location. */
+ caja_window_load_view_as_menus (window);
+
+ /* Load menus from caja extensions for this location */
+ caja_window_load_extension_menus (window);
+ }
+
+ if (location_really_changed)
+ {
+ caja_window_slot_remove_extra_location_widgets (slot);
+
+ directory = caja_directory_get (slot->location);
+
+ caja_window_slot_update_query_editor (slot);
+
+ if (caja_directory_is_in_trash (directory))
+ {
+ caja_window_slot_show_trash_bar (slot, window);
+ }
+
+ /* need the mount to determine if we should put up the x-content cluebar */
+ if (slot->find_mount_cancellable != NULL)
+ {
+ g_cancellable_cancel (slot->find_mount_cancellable);
+ slot->find_mount_cancellable = NULL;
+ }
+
+ data = g_new (FindMountData, 1);
+ data->slot = slot;
+ data->cancellable = g_cancellable_new ();
+ data->mount = NULL;
+
+ slot->find_mount_cancellable = data->cancellable;
+ g_file_find_enclosing_mount_async (slot->location,
+ G_PRIORITY_DEFAULT,
+ data->cancellable,
+ found_mount_cb,
+ data);
+
+ caja_directory_unref (directory);
+
+ slot_add_extension_extra_widgets (slot);
+ }
+
+ caja_window_slot_update_title (slot);
+ caja_window_slot_update_icon (slot);
+
+ if (slot == slot->pane->active_slot)
+ {
+ caja_window_pane_sync_location_widgets (slot->pane);
+
+ if (location_really_changed)
+ {
+ caja_window_pane_sync_search_widgets (slot->pane);
+ }
+
+ if (CAJA_IS_NAVIGATION_WINDOW (window) &&
+ slot->pane == window->details->active_pane)
+ {
+ caja_navigation_window_load_extension_toolbar_items (CAJA_NAVIGATION_WINDOW (window));
+ }
+ }
+}
+
+/* A location load previously announced by load_underway
+ * has been finished */
+void
+caja_window_report_load_complete (CajaWindow *window,
+ CajaView *view)
+{
+ CajaWindowSlot *slot;
+
+ g_assert (CAJA_IS_WINDOW (window));
+
+ if (window->details->temporarily_ignore_view_signals)
+ {
+ return;
+ }
+
+ slot = caja_window_get_slot_for_view (window, view);
+ g_assert (slot != NULL);
+
+ /* 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->content_view)
+ {
+ if (slot->pending_scroll_to != NULL)
+ {
+ caja_view_scroll_to_file (slot->content_view,
+ slot->pending_scroll_to);
+ }
+ end_location_change (slot);
+ }
+}
+
+static void
+end_location_change (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+ char *uri;
+
+ window = slot->pane->window;
+
+ uri = caja_window_slot_get_location_uri (slot);
+ if (uri)
+ {
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "finished loading window %p: %s", window, uri);
+ g_free (uri);
+ }
+
+ caja_window_slot_set_allow_stop (slot, FALSE);
+
+ /* Now we can free pending_scroll_to, since the load_complete
+ * callback already has been emitted.
+ */
+ g_free (slot->pending_scroll_to);
+ slot->pending_scroll_to = NULL;
+
+ free_location_change (slot);
+}
+
+static void
+free_location_change (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+
+ window = slot->pane->window;
+ g_assert (CAJA_IS_WINDOW (window));
+
+ if (slot->pending_location)
+ {
+ g_object_unref (slot->pending_location);
+ }
+ slot->pending_location = NULL;
+
+ eel_g_object_list_free (slot->pending_selection);
+ slot->pending_selection = NULL;
+
+ /* Don't free pending_scroll_to, since thats needed until
+ * the load_complete callback.
+ */
+
+ if (slot->mount_cancellable != NULL)
+ {
+ g_cancellable_cancel (slot->mount_cancellable);
+ slot->mount_cancellable = NULL;
+ }
+
+ if (slot->determine_view_file != NULL)
+ {
+ caja_file_cancel_call_when_ready
+ (slot->determine_view_file,
+ got_file_info_for_view_selection_callback, slot);
+ slot->determine_view_file = NULL;
+ }
+
+ if (slot->new_content_view != NULL)
+ {
+ window->details->temporarily_ignore_view_signals = TRUE;
+ caja_view_stop_loading (slot->new_content_view);
+ window->details->temporarily_ignore_view_signals = FALSE;
+
+ caja_window_slot_disconnect_content_view (slot, slot->new_content_view);
+ g_object_unref (slot->new_content_view);
+ slot->new_content_view = NULL;
+ }
+}
+
+static void
+cancel_location_change (CajaWindowSlot *slot)
+{
+ GList *selection;
+
+ if (slot->pending_location != NULL
+ && slot->location != NULL
+ && slot->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 = caja_view_get_selection (slot->content_view);
+ load_new_location (slot,
+ slot->location,
+ selection,
+ TRUE,
+ FALSE);
+ eel_g_object_list_free (selection);
+ }
+
+ end_location_change (slot);
+}
+
+void
+caja_window_report_view_failed (CajaWindow *window,
+ CajaView *view)
+{
+ CajaWindowSlot *slot;
+ gboolean do_close_window;
+ GFile *fallback_load_location;
+
+ if (window->details->temporarily_ignore_view_signals)
+ {
+ return;
+ }
+
+ slot = caja_window_get_slot_for_view (window, view);
+ g_assert (slot != NULL);
+
+ g_warning ("A view failed. The UI will handle this with a dialog but this should be debugged.");
+
+ do_close_window = FALSE;
+ fallback_load_location = NULL;
+
+ if (view == slot->content_view)
+ {
+ caja_window_slot_disconnect_content_view (slot, view);
+ caja_window_slot_set_content_view_widget (slot, NULL);
+
+ report_current_content_view_failure_to_user (slot);
+ }
+ else
+ {
+ /* Only report error on first try */
+ if (slot->location_change_type != CAJA_LOCATION_CHANGE_FALLBACK)
+ {
+ report_nascent_content_view_failure_to_user (slot, view);
+
+ fallback_load_location = g_object_ref (slot->pending_location);
+ }
+ else
+ {
+ if (!gtk_widget_get_visible (GTK_WIDGET (window)))
+ {
+ do_close_window = TRUE;
+ }
+ }
+ }
+
+ cancel_location_change (slot);
+
+ if (fallback_load_location != NULL)
+ {
+ /* We loose the pending selection change here, but who cares... */
+ begin_location_change (slot, fallback_load_location, NULL,
+ CAJA_LOCATION_CHANGE_FALLBACK, 0, NULL);
+ g_object_unref (fallback_load_location);
+ }
+
+ if (do_close_window)
+ {
+ gtk_widget_destroy (GTK_WIDGET (window));
+ }
+}
+
+static void
+display_view_selection_failure (CajaWindow *window, CajaFile *file,
+ GFile *location, GError *error)
+{
+ char *full_uri_for_display;
+ char *uri_for_display;
+ char *error_message;
+ char *detail_message;
+ char *scheme_string;
+ GtkDialog *dialog;
+
+ /* Some sort of failure occurred. How 'bout we tell the user? */
+ full_uri_for_display = g_file_get_parse_name (location);
+ /* Truncate the URI so it doesn't get insanely wide. Note that even
+ * though the dialog uses wrapped text, if the URI doesn't contain
+ * white space then the text-wrapping code is too stupid to wrap it.
+ */
+ uri_for_display = eel_str_middle_truncate
+ (full_uri_for_display, MAX_URI_IN_DIALOG_LENGTH);
+ g_free (full_uri_for_display);
+
+ error_message = NULL;
+ detail_message = NULL;
+ if (error == NULL)
+ {
+ if (caja_file_is_directory (file))
+ {
+ error_message = g_strdup_printf
+ (_("Could not display \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup
+ (_("Caja has no installed viewer capable of displaying the folder."));
+ }
+ else
+ {
+ error_message = g_strdup_printf
+ (_("Could not display \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup
+ (_("The location is not a folder."));
+ }
+ }
+ else if (error->domain == G_IO_ERROR)
+ {
+ switch (error->code)
+ {
+ case G_IO_ERROR_NOT_FOUND:
+ error_message = g_strdup_printf
+ (_("Could not find \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup
+ (_("Please check the spelling and try again."));
+ break;
+ case G_IO_ERROR_NOT_SUPPORTED:
+ scheme_string = g_file_get_uri_scheme (location);
+
+ error_message = g_strdup_printf (_("Could not display \"%s\"."),
+ uri_for_display);
+ if (scheme_string != NULL)
+ {
+ detail_message = g_strdup_printf (_("Caja cannot handle \"%s\" locations."),
+ scheme_string);
+ }
+ else
+ {
+ detail_message = g_strdup (_("Caja cannot handle this kind of location."));
+ }
+ g_free (scheme_string);
+ break;
+ case G_IO_ERROR_NOT_MOUNTED:
+ error_message = g_strdup_printf (_("Could not display \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup (_("Unable to mount the location."));
+ break;
+
+ case G_IO_ERROR_PERMISSION_DENIED:
+ error_message = g_strdup_printf (_("Could not display \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup (_("Access was denied."));
+ 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.
+ */
+ error_message = g_strdup_printf (_("Could not display \"%s\", because the host could not be found."),
+ uri_for_display);
+ detail_message = g_strdup (_("Check that the spelling is correct and that your proxy settings are correct."));
+ break;
+ case G_IO_ERROR_CANCELLED:
+ case G_IO_ERROR_FAILED_HANDLED:
+ g_free (uri_for_display);
+ return;
+
+ default:
+ break;
+ }
+ }
+
+ if (error_message == NULL)
+ {
+ error_message = g_strdup_printf (_("Could not display \"%s\"."),
+ uri_for_display);
+ detail_message = g_strdup_printf (_("Error: %s\nPlease select another viewer and try again."), error->message);
+ }
+
+ dialog = eel_show_error_dialog (error_message, detail_message, NULL);
+
+ g_free (uri_for_display);
+ g_free (error_message);
+ g_free (detail_message);
+}
+
+
+void
+caja_window_slot_stop_loading (CajaWindowSlot *slot)
+{
+ CajaWindow *window;
+
+ window = CAJA_WINDOW (slot->pane->window);
+ g_assert (CAJA_IS_WINDOW (window));
+
+ caja_view_stop_loading (slot->content_view);
+
+ if (slot->new_content_view != NULL)
+ {
+ window->details->temporarily_ignore_view_signals = TRUE;
+ caja_view_stop_loading (slot->new_content_view);
+ window->details->temporarily_ignore_view_signals = FALSE;
+ }
+
+ cancel_location_change (slot);
+}
+
+void
+caja_window_slot_set_content_view (CajaWindowSlot *slot,
+ const char *id)
+{
+ CajaWindow *window;
+ CajaFile *file;
+ char *uri;
+
+ g_assert (slot != NULL);
+ g_assert (slot->location != NULL);
+ g_assert (id != NULL);
+
+ window = slot->pane->window;
+ g_assert (CAJA_IS_WINDOW (window));
+
+ uri = caja_window_slot_get_location_uri (slot);
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "change view of window %p: \"%s\" to \"%s\"",
+ window, uri, id);
+ g_free (uri);
+
+ if (caja_window_slot_content_view_matches_iid (slot, id))
+ {
+ return;
+ }
+
+ end_location_change (slot);
+
+ file = caja_file_get (slot->location);
+ caja_file_set_metadata
+ (file, CAJA_METADATA_KEY_DEFAULT_VIEW, NULL, id);
+ caja_file_unref (file);
+
+ caja_window_slot_set_allow_stop (slot, TRUE);
+
+ if (caja_view_get_selection_count (slot->content_view) == 0)
+ {
+ /* If there is no selection, queue a scroll to the same icon that
+ * is currently visible */
+ slot->pending_scroll_to = caja_view_get_first_visible_file (slot->content_view);
+ }
+ slot->location_change_type = CAJA_LOCATION_CHANGE_RELOAD;
+
+ create_content_view (slot, id);
+}
+
+void
+caja_window_manage_views_close_slot (CajaWindowPane *pane,
+ CajaWindowSlot *slot)
+{
+ if (slot->content_view != NULL)
+ {
+ caja_window_slot_disconnect_content_view (slot, slot->content_view);
+ }
+
+ free_location_change (slot);
+ cancel_viewed_file_changed_callback (slot);
+}
+
+void
+caja_navigation_window_back_or_forward (CajaNavigationWindow *window,
+ gboolean back, guint distance, gboolean new_tab)
+{
+ CajaWindowSlot *slot;
+ CajaNavigationWindowSlot *navigation_slot;
+ GList *list;
+ GFile *location;
+ guint len;
+ CajaBookmark *bookmark;
+
+ slot = CAJA_WINDOW (window)->details->active_pane->active_slot;
+ navigation_slot = (CajaNavigationWindowSlot *) slot;
+ list = back ? navigation_slot->back_list : navigation_slot->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 = caja_bookmark_get_location (bookmark);
+
+ if (new_tab)
+ {
+ caja_window_slot_open_location_full (slot, location,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ CAJA_WINDOW_OPEN_FLAG_NEW_TAB,
+ NULL);
+ }
+ else
+ {
+ char *scroll_pos;
+
+ scroll_pos = caja_bookmark_get_scroll_pos (bookmark);
+ begin_location_change
+ (slot,
+ location, NULL,
+ back ? CAJA_LOCATION_CHANGE_BACK : CAJA_LOCATION_CHANGE_FORWARD,
+ distance,
+ scroll_pos);
+
+ g_free (scroll_pos);
+ }
+
+ g_object_unref (location);
+}
+
+/* reload the contents of the window */
+void
+caja_window_slot_reload (CajaWindowSlot *slot)
+{
+ GFile *location;
+ char *current_pos;
+ GList *selection;
+
+ g_assert (CAJA_IS_WINDOW_SLOT (slot));
+
+ if (slot->location == NULL)
+ {
+ return;
+ }
+
+ /* peek_slot_field (window, location) can be free'd during the processing
+ * of begin_location_change, so make a copy
+ */
+ location = g_object_ref (slot->location);
+ current_pos = NULL;
+ selection = NULL;
+ if (slot->content_view != NULL)
+ {
+ current_pos = caja_view_get_first_visible_file (slot->content_view);
+ selection = caja_view_get_selection (slot->content_view);
+ }
+ begin_location_change
+ (slot, location, selection,
+ CAJA_LOCATION_CHANGE_RELOAD, 0, current_pos);
+ g_free (current_pos);
+ g_object_unref (location);
+ eel_g_object_list_free (selection);
+}
+
+void
+caja_window_reload (CajaWindow *window)
+{
+ g_assert (CAJA_IS_WINDOW (window));
+
+ caja_window_slot_reload (window->details->active_pane->active_slot);
+}
+