summaryrefslogtreecommitdiff
path: root/src/file-manager/fm-directory-view.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/file-manager/fm-directory-view.c')
-rw-r--r--src/file-manager/fm-directory-view.c10936
1 files changed, 10936 insertions, 0 deletions
diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c
new file mode 100644
index 00000000..a192aa58
--- /dev/null
+++ b/src/file-manager/fm-directory-view.c
@@ -0,0 +1,10936 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* fm-directory-view.c
+ *
+ * Copyright (C) 1999, 2000 Free Software Foundation
+ * Copyright (C) 2000, 2001 Eazel, 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Ettore Perazzoli,
+ * John Sullivan <[email protected]>,
+ * Darin Adler <[email protected]>,
+ * Pavel Cisler <[email protected]>,
+ * David Emory Watson <[email protected]>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "fm-directory-view.h"
+#include "fm-list-view.h"
+#include "fm-desktop-icon-view.h"
+
+#include "fm-actions.h"
+#include "fm-error-reporting.h"
+#include "fm-properties-window.h"
+#include "libcaja-private/caja-open-with-dialog.h"
+
+#include <eel/eel-background.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-mate-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 <eel/eel-vfs-extensions.h>
+#include <eel/eel-marshal.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <libcaja-private/caja-recent.h>
+#include <libcaja-extension/caja-menu-provider.h>
+#include <libcaja-private/caja-clipboard.h>
+#include <libcaja-private/caja-clipboard-monitor.h>
+#include <libcaja-private/caja-debug-log.h>
+#include <libcaja-private/caja-desktop-icon-file.h>
+#include <libcaja-private/caja-desktop-directory.h>
+#include <libcaja-private/caja-search-directory.h>
+#include <libcaja-private/caja-directory-background.h>
+#include <libcaja-private/caja-directory.h>
+#include <libcaja-private/caja-dnd.h>
+#include <libcaja-private/caja-file-attributes.h>
+#include <libcaja-private/caja-file-changes-queue.h>
+#include <libcaja-private/caja-file-dnd.h>
+#include <libcaja-private/caja-file-operations.h>
+#include <libcaja-private/caja-file-utilities.h>
+#include <libcaja-private/caja-file-private.h> /* for caja_file_get_existing_by_uri */
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-link.h>
+#include <libcaja-private/caja-marshal.h>
+#include <libcaja-private/caja-metadata.h>
+#include <libcaja-private/caja-mime-actions.h>
+#include <libcaja-private/caja-module.h>
+#include <libcaja-private/caja-program-choosing.h>
+#include <libcaja-private/caja-trash-monitor.h>
+#include <libcaja-private/caja-ui-utilities.h>
+#include <libcaja-private/caja-signaller.h>
+#include <libcaja-private/caja-autorun.h>
+#include <libcaja-private/caja-icon-names.h>
+
+/* Minimum starting update inverval */
+#define UPDATE_INTERVAL_MIN 100
+/* Maximum update interval */
+#define UPDATE_INTERVAL_MAX 2000
+/* Amount of miliseconds the update interval is increased */
+#define UPDATE_INTERVAL_INC 250
+/* Interval at which the update interval is increased */
+#define UPDATE_INTERVAL_TIMEOUT_INTERVAL 250
+/* Milliseconds that have to pass without a change to reset the update interval */
+#define UPDATE_INTERVAL_RESET 1000
+
+#define SILENT_WINDOW_OPEN_LIMIT 5
+
+#define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
+#define DUPLICATE_VERTICAL_ICON_OFFSET 30
+
+#define MAX_QUEUED_UPDATES 500
+
+#define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER "/MenuBar/File/Open Placeholder/Open With/Applications Placeholder"
+#define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER "/MenuBar/File/Open Placeholder/Applications Placeholder"
+#define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER "/MenuBar/File/Open Placeholder/Scripts/Scripts Placeholder"
+#define FM_DIRECTORY_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER "/MenuBar/Edit/Extension Actions"
+#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER "/MenuBar/File/New Items Placeholder/New Documents/New Documents Placeholder"
+#define FM_DIRECTORY_VIEW_MENU_PATH_OPEN "/MenuBar/File/Open Placeholder/Open"
+
+#define FM_DIRECTORY_VIEW_POPUP_PATH_SELECTION "/selection"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER "/selection/Open Placeholder/Open With/Applications Placeholder"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER "/selection/Open Placeholder/Applications Placeholder"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER "/selection/Open Placeholder/Scripts/Scripts Placeholder"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_EXTENSION_ACTIONS "/selection/Extension Actions"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_OPEN "/selection/Open Placeholder/Open"
+
+#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND "/background"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/Scripts/Scripts Placeholder"
+#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/New Documents/New Documents Placeholder"
+
+#define FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION "/location"
+
+#define MAX_MENU_LEVELS 5
+#define TEMPLATE_LIMIT 30
+
+enum {
+ ADD_FILE,
+ BEGIN_FILE_CHANGES,
+ BEGIN_LOADING,
+ CLEAR,
+ END_FILE_CHANGES,
+ FLUSH_ADDED_FILES,
+ END_LOADING,
+ FILE_CHANGED,
+ LOAD_ERROR,
+ MOVE_COPY_ITEMS,
+ REMOVE_FILE,
+ TRASH,
+ DELETE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_WINDOW_SLOT
+};
+
+
+static guint signals[LAST_SIGNAL];
+
+static GdkAtom copied_files_atom;
+
+static gboolean show_delete_command_auto_value;
+static gboolean confirm_trash_auto_value;
+
+static char *scripts_directory_uri;
+static int scripts_directory_uri_length;
+
+struct FMDirectoryViewDetails
+{
+ CajaWindowInfo *window;
+ CajaWindowSlotInfo *slot;
+ CajaDirectory *model;
+ CajaFile *directory_as_file;
+ CajaFile *location_popup_directory_as_file;
+ GdkEventButton *location_popup_event;
+ GtkActionGroup *dir_action_group;
+ guint dir_merge_id;
+
+ GList *scripts_directory_list;
+ GtkActionGroup *scripts_action_group;
+ guint scripts_merge_id;
+
+ GList *templates_directory_list;
+ GtkActionGroup *templates_action_group;
+ guint templates_merge_id;
+
+ GtkActionGroup *extensions_menu_action_group;
+ guint extensions_menu_merge_id;
+
+ guint display_selection_idle_id;
+ guint update_menus_timeout_id;
+ guint update_status_idle_id;
+ guint reveal_selection_idle_id;
+
+ guint display_pending_source_id;
+ guint changes_timeout_id;
+
+ guint update_interval;
+ guint64 last_queued;
+
+ guint files_added_handler_id;
+ guint files_changed_handler_id;
+ guint load_error_handler_id;
+ guint done_loading_handler_id;
+ guint file_changed_handler_id;
+
+ guint delayed_rename_file_id;
+
+ GList *new_added_files;
+ GList *new_changed_files;
+
+ GHashTable *non_ready_files;
+
+ GList *old_added_files;
+ GList *old_changed_files;
+
+ GList *pending_locations_selected;
+
+ /* whether we are in the active slot */
+ gboolean active;
+
+ /* loading indicates whether this view has begun loading a directory.
+ * This flag should need not be set inside subclasses. FMDirectoryView automatically
+ * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
+ * after it finishes loading the directory and its view.
+ */
+ gboolean loading;
+ gboolean menu_states_untrustworthy;
+ gboolean scripts_invalid;
+ gboolean templates_invalid;
+ gboolean reported_load_error;
+
+ /* flag to indicate that no file updates should be dispatched to subclasses.
+ * This is a workaround for bug #87701 that prevents the list view from
+ * losing focus when the underlying GtkTreeView is updated.
+ */
+ gboolean updates_frozen;
+ guint updates_queued;
+ gboolean needs_reload;
+
+ gboolean sort_directories_first;
+
+ gboolean show_foreign_files;
+ gboolean show_hidden_files;
+ gboolean show_backup_files;
+ gboolean ignore_hidden_file_preferences;
+
+ gboolean batching_selection_level;
+ gboolean selection_changed_while_batched;
+
+ gboolean selection_was_removed;
+
+ gboolean metadata_for_directory_as_file_pending;
+ gboolean metadata_for_files_in_directory_pending;
+
+ gboolean selection_change_is_due_to_shell;
+ gboolean send_selection_change_to_shell;
+
+ GtkActionGroup *open_with_action_group;
+ guint open_with_merge_id;
+
+ GList *subdirectory_list;
+
+ gboolean allow_moves;
+
+ GdkPoint context_menu_position;
+};
+
+typedef struct {
+ CajaFile *file;
+ CajaDirectory *directory;
+} FileAndDirectory;
+
+/* forward declarations */
+
+static gboolean display_selection_info_idle_callback (gpointer data);
+static void fm_directory_view_class_init (FMDirectoryViewClass *klass);
+static void fm_directory_view_init (FMDirectoryView *view);
+static void fm_directory_view_duplicate_selection (FMDirectoryView *view,
+ GList *files,
+ GArray *item_locations);
+static void fm_directory_view_create_links_for_files (FMDirectoryView *view,
+ GList *files,
+ GArray *item_locations);
+static void trash_or_delete_files (GtkWindow *parent_window,
+ const GList *files,
+ gboolean delete_if_all_already_in_trash,
+ FMDirectoryView *view);
+static void load_directory (FMDirectoryView *view,
+ CajaDirectory *directory);
+static void fm_directory_view_merge_menus (FMDirectoryView *view);
+static void fm_directory_view_unmerge_menus (FMDirectoryView *view);
+static void fm_directory_view_init_show_hidden_files (FMDirectoryView *view);
+static void fm_directory_view_load_location (CajaView *caja_view,
+ const char *location);
+static void fm_directory_view_stop_loading (CajaView *caja_view);
+static void fm_directory_view_drop_proxy_received_uris (FMDirectoryView *view,
+ const GList *source_uri_list,
+ const char *target_uri,
+ GdkDragAction action);
+static void fm_directory_view_drop_proxy_received_netscape_url (FMDirectoryView *view,
+ const char *netscape_url,
+ const char *target_uri,
+ GdkDragAction action);
+static void clipboard_changed_callback (CajaClipboardMonitor *monitor,
+ FMDirectoryView *view);
+static void open_one_in_new_window (gpointer data,
+ gpointer callback_data);
+static void open_one_in_folder_window (gpointer data,
+ gpointer callback_data);
+static void schedule_update_menus (FMDirectoryView *view);
+static void schedule_update_menus_callback (gpointer callback_data);
+static void remove_update_menus_timeout_callback (FMDirectoryView *view);
+static void schedule_update_status (FMDirectoryView *view);
+static void remove_update_status_idle_callback (FMDirectoryView *view);
+static void reset_update_interval (FMDirectoryView *view);
+static void schedule_idle_display_of_pending_files (FMDirectoryView *view);
+static void unschedule_display_of_pending_files (FMDirectoryView *view);
+static void disconnect_model_handlers (FMDirectoryView *view);
+static void metadata_for_directory_as_file_ready_callback (CajaFile *file,
+ gpointer callback_data);
+static void metadata_for_files_in_directory_ready_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data);
+static void fm_directory_view_trash_state_changed_callback (CajaTrashMonitor *trash,
+ gboolean state,
+ gpointer callback_data);
+static void fm_directory_view_select_file (FMDirectoryView *view,
+ CajaFile *file);
+
+static GdkDragAction ask_link_action (FMDirectoryView *view);
+static void update_templates_directory (FMDirectoryView *view);
+static void user_dirs_changed (FMDirectoryView *view);
+static void fm_directory_view_set_is_active (FMDirectoryView *view,
+ gboolean is_active);
+
+static gboolean file_list_all_are_folders (GList *file_list);
+
+static void action_open_scripts_folder_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_cut_files_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_copy_files_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_paste_files_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_copy_to_next_pane_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_move_to_next_pane_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_rename_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_rename_select_all_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_paste_files_into_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_connect_to_server_link_callback (GtkAction *action,
+ gpointer data);
+static void action_mount_volume_callback (GtkAction *action,
+ gpointer data);
+static void action_unmount_volume_callback (GtkAction *action,
+ gpointer data);
+static void action_format_volume_callback (GtkAction *action,
+ gpointer data);
+static void action_start_volume_callback (GtkAction *action,
+ gpointer data);
+static void action_stop_volume_callback (GtkAction *action,
+ gpointer data);
+static void action_detect_media_callback (GtkAction *action,
+ gpointer data);
+
+/* location popup-related actions */
+
+static void action_location_open_alternate_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_location_open_folder_window_callback (GtkAction *action,
+ gpointer callback_data);
+
+static void action_location_cut_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_location_copy_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_location_trash_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_location_delete_callback (GtkAction *action,
+ gpointer callback_data);
+static void action_location_properties_callback (GtkAction *action,
+ gpointer callback_data);
+
+static void unschedule_pop_up_location_context_menu (FMDirectoryView *view);
+
+static inline void fm_directory_view_widget_to_file_operation_position (FMDirectoryView *view,
+ GdkPoint *position);
+static void fm_directory_view_widget_to_file_operation_position_xy (FMDirectoryView *view,
+ int *x, int *y);
+
+EEL_CLASS_BOILERPLATE (FMDirectoryView, fm_directory_view, GTK_TYPE_SCROLLED_WINDOW)
+
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, add_file)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, bump_zoom_level)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_in)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_out)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, file_changed)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_background_widget)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection_for_file_transfer)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_item_count)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, is_empty)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, reset_to_defaults)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, restore_default_zoom_level)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, select_all)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, set_selection)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, zoom_to_level)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_zoom_level)
+EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, invert_selection)
+
+typedef struct {
+ GAppInfo *application;
+ GList *files;
+ FMDirectoryView *directory_view;
+} ApplicationLaunchParameters;
+
+typedef struct {
+ CajaFile *file;
+ FMDirectoryView *directory_view;
+} ScriptLaunchParameters;
+
+typedef struct {
+ CajaFile *file;
+ FMDirectoryView *directory_view;
+} CreateTemplateParameters;
+
+static ApplicationLaunchParameters *
+application_launch_parameters_new (GAppInfo *application,
+ GList *files,
+ FMDirectoryView *directory_view)
+{
+ ApplicationLaunchParameters *result;
+
+ result = g_new0 (ApplicationLaunchParameters, 1);
+ result->application = g_object_ref (application);
+ result->files = caja_file_list_copy (files);
+
+ if (directory_view != NULL) {
+ g_object_ref (directory_view);
+ result->directory_view = directory_view;
+ }
+
+ return result;
+}
+
+static void
+application_launch_parameters_free (ApplicationLaunchParameters *parameters)
+{
+ g_object_unref (parameters->application);
+ caja_file_list_free (parameters->files);
+
+ if (parameters->directory_view != NULL) {
+ g_object_unref (parameters->directory_view);
+ }
+
+ g_free (parameters);
+}
+
+static GList *
+file_and_directory_list_to_files (GList *fad_list)
+{
+ GList *res, *l;
+ FileAndDirectory *fad;
+
+ res = NULL;
+ for (l = fad_list; l != NULL; l = l->next) {
+ fad = l->data;
+ res = g_list_prepend (res, caja_file_ref (fad->file));
+ }
+ return g_list_reverse (res);
+}
+
+
+static GList *
+file_and_directory_list_from_files (CajaDirectory *directory, GList *files)
+{
+ GList *res, *l;
+ FileAndDirectory *fad;
+
+ res = NULL;
+ for (l = files; l != NULL; l = l->next) {
+ fad = g_new0 (FileAndDirectory, 1);
+ fad->directory = caja_directory_ref (directory);
+ fad->file = caja_file_ref (l->data);
+ res = g_list_prepend (res, fad);
+ }
+ return g_list_reverse (res);
+}
+
+static void
+file_and_directory_free (FileAndDirectory *fad)
+{
+ caja_directory_unref (fad->directory);
+ caja_file_unref (fad->file);
+ g_free (fad);
+}
+
+
+static void
+file_and_directory_list_free (GList *list)
+{
+ GList *l;
+
+ for (l = list; l != NULL; l = l->next) {
+ file_and_directory_free (l->data);
+ }
+
+ g_list_free (list);
+}
+
+static gboolean
+file_and_directory_equal (gconstpointer v1,
+ gconstpointer v2)
+{
+ const FileAndDirectory *fad1, *fad2;
+ fad1 = v1;
+ fad2 = v2;
+
+ return (fad1->file == fad2->file &&
+ fad1->directory == fad2->directory);
+}
+
+static guint
+file_and_directory_hash (gconstpointer v)
+{
+ const FileAndDirectory *fad;
+
+ fad = v;
+ return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
+}
+
+
+
+
+static ScriptLaunchParameters *
+script_launch_parameters_new (CajaFile *file,
+ FMDirectoryView *directory_view)
+{
+ ScriptLaunchParameters *result;
+
+ result = g_new0 (ScriptLaunchParameters, 1);
+ g_object_ref (directory_view);
+ result->directory_view = directory_view;
+ caja_file_ref (file);
+ result->file = file;
+
+ return result;
+}
+
+static void
+script_launch_parameters_free (ScriptLaunchParameters *parameters)
+{
+ g_object_unref (parameters->directory_view);
+ caja_file_unref (parameters->file);
+ g_free (parameters);
+}
+
+static CreateTemplateParameters *
+create_template_parameters_new (CajaFile *file,
+ FMDirectoryView *directory_view)
+{
+ CreateTemplateParameters *result;
+
+ result = g_new0 (CreateTemplateParameters, 1);
+ g_object_ref (directory_view);
+ result->directory_view = directory_view;
+ caja_file_ref (file);
+ result->file = file;
+
+ return result;
+}
+
+static void
+create_templates_parameters_free (CreateTemplateParameters *parameters)
+{
+ g_object_unref (parameters->directory_view);
+ caja_file_unref (parameters->file);
+ g_free (parameters);
+}
+
+CajaWindowInfo *
+fm_directory_view_get_caja_window (FMDirectoryView *view)
+{
+ g_assert (view->details->window != NULL);
+
+ return view->details->window;
+}
+
+CajaWindowSlotInfo *
+fm_directory_view_get_caja_window_slot (FMDirectoryView *view)
+{
+ g_assert (view->details->slot != NULL);
+
+ return view->details->slot;
+}
+
+/* Returns the GtkWindow that this directory view occupies, or NULL
+ * if at the moment this directory view is not in a GtkWindow or the
+ * GtkWindow cannot be determined. Primarily used for parenting dialogs.
+ */
+GtkWindow *
+fm_directory_view_get_containing_window (FMDirectoryView *view)
+{
+ GtkWidget *window;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
+ if (window == NULL) {
+ return NULL;
+ }
+
+ return GTK_WINDOW (window);
+}
+
+static gboolean
+fm_directory_view_confirm_multiple (GtkWindow *parent_window,
+ int count,
+ gboolean tabs)
+{
+ GtkDialog *dialog;
+ char *prompt;
+ char *detail;
+ int response;
+
+ if (count <= SILENT_WINDOW_OPEN_LIMIT) {
+ return TRUE;
+ }
+
+ prompt = _("Are you sure you want to open all files?");
+ if (tabs) {
+ detail = g_strdup_printf (ngettext("This will open %'d separate tab.",
+ "This will open %'d separate tabs.", count), count);
+ } else {
+ detail = g_strdup_printf (ngettext("This will open %'d separate window.",
+ "This will open %'d separate windows.", count), count);
+ }
+ dialog = eel_show_yes_no_dialog (prompt, detail,
+ GTK_STOCK_OK, GTK_STOCK_CANCEL,
+ parent_window);
+ g_free (detail);
+
+ response = gtk_dialog_run (dialog);
+ gtk_object_destroy (GTK_OBJECT (dialog));
+
+ return response == GTK_RESPONSE_YES;
+}
+
+static gboolean
+selection_contains_one_item_in_menu_callback (FMDirectoryView *view, GList *selection)
+{
+ if (eel_g_list_exactly_one_item (selection)) {
+ return TRUE;
+ }
+
+ /* If we've requested a menu update that hasn't yet occurred, then
+ * the mismatch here doesn't surprise us, and we won't complain.
+ * Otherwise, we will complain.
+ */
+ if (!view->details->menu_states_untrustworthy) {
+ g_warning ("Expected one selected item, found %'d. No action will be performed.",
+ g_list_length (selection));
+ }
+
+ return FALSE;
+}
+
+static gboolean
+selection_not_empty_in_menu_callback (FMDirectoryView *view, GList *selection)
+{
+ if (selection != NULL) {
+ return TRUE;
+ }
+
+ /* If we've requested a menu update that hasn't yet occurred, then
+ * the mismatch here doesn't surprise us, and we won't complain.
+ * Otherwise, we will complain.
+ */
+ if (!view->details->menu_states_untrustworthy) {
+ g_warning ("Empty selection found when selection was expected. No action will be performed.");
+ }
+
+ return FALSE;
+}
+
+static char *
+get_view_directory (FMDirectoryView *view)
+{
+ char *uri, *path;
+ GFile *f;
+
+ uri = caja_directory_get_uri (view->details->model);
+ if (eel_uri_is_desktop (uri)) {
+ g_free (uri);
+ uri = caja_get_desktop_directory_uri ();
+
+ }
+ f = g_file_new_for_uri (uri);
+ path = g_file_get_path (f);
+ g_object_unref (f);
+ g_free (uri);
+
+ return path;
+}
+
+void
+fm_directory_view_activate_files (FMDirectoryView *view,
+ GList *files,
+ CajaWindowOpenMode mode,
+ CajaWindowOpenFlags flags,
+ gboolean confirm_multiple)
+{
+ char *path;
+
+ path = get_view_directory (view);
+ caja_mime_activate_files (fm_directory_view_get_containing_window (view),
+ view->details->slot,
+ files,
+ path,
+ mode,
+ flags,
+ confirm_multiple);
+
+ g_free (path);
+}
+
+void
+fm_directory_view_activate_file (FMDirectoryView *view,
+ CajaFile *file,
+ CajaWindowOpenMode mode,
+ CajaWindowOpenFlags flags)
+{
+ char *path;
+
+ path = get_view_directory (view);
+ caja_mime_activate_file (fm_directory_view_get_containing_window (view),
+ view->details->slot,
+ file,
+ path,
+ mode,
+ flags);
+
+ g_free (path);
+}
+
+static void
+action_open_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ GList *selection;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ selection = fm_directory_view_get_selection (view);
+ fm_directory_view_activate_files (view,
+ selection,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ 0,
+ TRUE);
+ caja_file_list_free (selection);
+}
+
+static void
+action_open_close_parent_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ GList *selection;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ selection = fm_directory_view_get_selection (view);
+ fm_directory_view_activate_files (view,
+ selection,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND,
+ TRUE);
+ caja_file_list_free (selection);
+}
+
+
+static void
+action_open_alternate_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GtkWindow *window;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+
+ window = fm_directory_view_get_containing_window (view);
+
+ if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
+ g_list_foreach (selection, open_one_in_new_window, view);
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_open_new_tab_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GtkWindow *window;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+
+ window = fm_directory_view_get_containing_window (view);
+
+ if (fm_directory_view_confirm_multiple (window, g_list_length (selection), TRUE)) {
+ fm_directory_view_activate_files (view,
+ selection,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ CAJA_WINDOW_OPEN_FLAG_NEW_TAB,
+ FALSE);
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_open_folder_window_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GtkWindow *window;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+
+ window = fm_directory_view_get_containing_window (view);
+
+ if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
+ g_list_foreach (selection, open_one_in_folder_window, view);
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+open_location (FMDirectoryView *directory_view,
+ const char *new_uri,
+ CajaWindowOpenMode mode,
+ CajaWindowOpenFlags flags)
+{
+ GtkWindow *window;
+ GFile *location;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (directory_view));
+ g_assert (new_uri != NULL);
+
+ window = fm_directory_view_get_containing_window (directory_view);
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "directory view open_location window=%p: %s", window, new_uri);
+ location = g_file_new_for_uri (new_uri);
+ caja_window_slot_info_open_location (directory_view->details->slot,
+ location, mode, flags, NULL);
+ g_object_unref (location);
+}
+
+static void
+application_selected_cb (CajaOpenWithDialog *dialog,
+ GAppInfo *app,
+ gpointer user_data)
+{
+ GtkWindow *parent_window;
+ CajaFile *file;
+ GList files;
+
+ parent_window = GTK_WINDOW (user_data);
+
+ file = g_object_get_data (G_OBJECT (dialog), "directory-view:file");
+
+ files.next = NULL;
+ files.prev = NULL;
+ files.data = file;
+ caja_launch_application (app, &files, parent_window);
+}
+
+static void
+choose_program (FMDirectoryView *view,
+ CajaFile *file)
+{
+ GtkWidget *dialog;
+ char *uri;
+ char *mime_type;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (CAJA_IS_FILE (file));
+
+ caja_file_ref (file);
+ uri = caja_file_get_uri (file);
+ mime_type = caja_file_get_mime_type (file);
+
+ dialog = caja_open_with_dialog_new (uri, mime_type, NULL);
+ g_object_set_data_full (G_OBJECT (dialog),
+ "directory-view:file",
+ g_object_ref (file),
+ (GDestroyNotify)g_object_unref);
+
+ gtk_window_set_screen (GTK_WINDOW (dialog),
+ gtk_widget_get_screen (GTK_WIDGET (view)));
+ gtk_widget_show (dialog);
+
+ g_signal_connect_object (dialog,
+ "application_selected",
+ G_CALLBACK (application_selected_cb),
+ fm_directory_view_get_containing_window (view),
+ 0);
+
+ g_free (uri);
+ g_free (mime_type);
+ caja_file_unref (file);
+}
+
+static void
+open_with_other_program (FMDirectoryView *view)
+{
+ GList *selection;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ selection = fm_directory_view_get_selection (view);
+
+ if (selection_contains_one_item_in_menu_callback (view, selection)) {
+ choose_program (view, CAJA_FILE (selection->data));
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_other_application_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ open_with_other_program (FM_DIRECTORY_VIEW (callback_data));
+}
+
+static void
+trash_or_delete_selected_files (FMDirectoryView *view)
+{
+ GList *selection;
+
+ /* This might be rapidly called multiple times for the same selection
+ * when using keybindings. So we remember if the current selection
+ * was already removed (but the view doesn't know about it yet).
+ */
+ if (!view->details->selection_was_removed) {
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ trash_or_delete_files (fm_directory_view_get_containing_window (view),
+ selection, TRUE,
+ view);
+ caja_file_list_free (selection);
+ view->details->selection_was_removed = TRUE;
+ }
+}
+
+static gboolean
+real_trash (FMDirectoryView *view)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_TRASH);
+ if (gtk_action_get_sensitive (action) &&
+ gtk_action_get_visible (action)) {
+ trash_or_delete_selected_files (view);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+action_trash_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ trash_or_delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
+}
+
+static void
+delete_selected_files (FMDirectoryView *view)
+{
+ GList *selection;
+ GList *node;
+ GList *locations;
+
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ if (selection == NULL) {
+ return;
+ }
+
+ locations = NULL;
+ for (node = selection; node != NULL; node = node->next) {
+ locations = g_list_prepend (locations,
+ caja_file_get_location ((CajaFile *) node->data));
+ }
+ locations = g_list_reverse (locations);
+
+ caja_file_operations_delete (locations, fm_directory_view_get_containing_window (view), NULL, NULL);
+
+ eel_g_object_list_free (locations);
+ caja_file_list_free (selection);
+}
+
+static void
+action_delete_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
+}
+
+static void
+action_restore_from_trash_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ caja_restore_files_from_trash (selection,
+ fm_directory_view_get_containing_window (view));
+
+ caja_file_list_free (selection);
+
+}
+
+static gboolean
+real_delete (FMDirectoryView *view)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_DELETE);
+ if (gtk_action_get_sensitive (action) &&
+ gtk_action_get_visible (action)) {
+ delete_selected_files (view);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+action_duplicate_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GArray *selected_item_locations;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ if (selection_not_empty_in_menu_callback (view, selection)) {
+ /* FIXME bugzilla.gnome.org 45061:
+ * should change things here so that we use a get_icon_locations (view, selection).
+ * Not a problem in this case but in other places the selection may change by
+ * the time we go and retrieve the icon positions, relying on the selection
+ * staying intact to ensure the right sequence and count of positions is fragile.
+ */
+ selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
+ fm_directory_view_duplicate_selection (view, selection, selected_item_locations);
+ g_array_free (selected_item_locations, TRUE);
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_create_link_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GArray *selected_item_locations;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+ if (selection_not_empty_in_menu_callback (view, selection)) {
+ selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
+ fm_directory_view_create_links_for_files (view, selection, selected_item_locations);
+ g_array_free (selected_item_locations, TRUE);
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_select_all_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_select_all (callback_data);
+}
+
+static void
+action_invert_selection_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_invert_selection (callback_data);
+}
+
+
+static void
+pattern_select_response_cb (GtkWidget *dialog, int response, gpointer user_data)
+{
+ FMDirectoryView *view;
+ CajaDirectory *directory;
+ GtkWidget *entry;
+ GList *selection;
+ GError *error;
+
+ view = FM_DIRECTORY_VIEW (user_data);
+
+ switch (response) {
+ case GTK_RESPONSE_OK :
+ entry = g_object_get_data (G_OBJECT (dialog), "entry");
+ directory = fm_directory_view_get_model (view);
+ selection = caja_directory_match_pattern (directory,
+ gtk_entry_get_text (GTK_ENTRY (entry)));
+
+ if (selection) {
+ fm_directory_view_set_selection (view, selection);
+ caja_file_list_free (selection);
+
+ fm_directory_view_reveal_selection(view);
+ }
+ /* fall through */
+ case GTK_RESPONSE_NONE :
+ case GTK_RESPONSE_DELETE_EVENT :
+ case GTK_RESPONSE_CANCEL :
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ case GTK_RESPONSE_HELP :
+ error = NULL;
+ gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (dialog)),
+ "ghelp:user-guide#caja-select-pattern",
+ gtk_get_current_event_time (), &error);
+ if (error) {
+ eel_show_error_dialog (_("There was an error displaying help."), error->message,
+ GTK_WINDOW (dialog));
+ g_error_free (error);
+ }
+ break;
+ default :
+ g_assert_not_reached ();
+ }
+}
+
+static void
+select_pattern (FMDirectoryView *view)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *example;
+ GtkWidget *table;
+ GtkWidget *entry;
+ GList *ret;
+ char *example_pattern;
+
+ ret = NULL;
+ dialog = gtk_dialog_new_with_buttons (_("Select Items Matching"),
+ fm_directory_view_get_containing_window (view),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_HELP,
+ GTK_RESPONSE_HELP,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+
+ label = gtk_label_new_with_mnemonic (_("_Pattern:"));
+ example = gtk_label_new (NULL);
+ example_pattern = g_strdup_printf ("<b>%s</b><i>%s</i>",
+ _("Examples: "),
+ "*.png, file\?\?.txt, pict*.\?\?\?");
+ gtk_label_set_markup (GTK_LABEL (example), example_pattern);
+ g_free (example_pattern);
+ gtk_misc_set_alignment (GTK_MISC (example), 0.0, 0.5);
+ entry = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+
+ table = gtk_table_new (2, 2, FALSE);
+
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1,
+ 0, 1,
+ GTK_FILL, GTK_FILL,
+ 5, 5);
+
+ gtk_table_attach (GTK_TABLE (table), entry,
+ 1, 2,
+ 0, 1,
+ GTK_EXPAND | GTK_FILL, GTK_FILL,
+ 5, 5);
+
+ gtk_table_attach (GTK_TABLE (table), example,
+ 1, 2,
+ 1, 2,
+ GTK_FILL, GTK_FILL,
+ 5, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ gtk_widget_show_all (table);
+ gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table);
+ g_object_set_data (G_OBJECT (dialog), "entry", entry);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (pattern_select_response_cb),
+ view);
+ gtk_widget_show_all (dialog);
+}
+
+static void
+action_select_pattern_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ select_pattern(callback_data);
+}
+
+static void
+action_reset_to_defaults_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_reset_to_defaults (callback_data);
+}
+
+
+static void
+hidden_files_mode_changed (CajaWindow *window,
+ gpointer callback_data)
+{
+ FMDirectoryView *directory_view;
+
+ directory_view = FM_DIRECTORY_VIEW (callback_data);
+
+ fm_directory_view_init_show_hidden_files (directory_view);
+}
+
+static void
+action_save_search_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ CajaSearchDirectory *search;
+ FMDirectoryView *directory_view;
+
+ directory_view = FM_DIRECTORY_VIEW (callback_data);
+
+ if (directory_view->details->model &&
+ CAJA_IS_SEARCH_DIRECTORY (directory_view->details->model)) {
+ search = CAJA_SEARCH_DIRECTORY (directory_view->details->model);
+ caja_search_directory_save_search (search);
+
+ /* Save search is disabled */
+ schedule_update_menus (directory_view);
+ }
+}
+
+static void
+query_name_entry_changed_cb (GtkWidget *entry, GtkWidget *button)
+{
+ const char *text;
+ gboolean sensitive;
+
+ text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+ sensitive = (text != NULL) && (*text != 0);
+
+ gtk_widget_set_sensitive (button, sensitive);
+}
+
+
+static void
+action_save_search_as_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *directory_view;
+ CajaSearchDirectory *search;
+ CajaQuery *query;
+ GtkWidget *dialog, *table, *label, *entry, *chooser, *save_button;
+ const char *entry_text;
+ char *filename, *filename_utf8, *dirname, *path, *uri;
+ GFile *location;
+
+ directory_view = FM_DIRECTORY_VIEW (callback_data);
+
+ if (directory_view->details->model &&
+ CAJA_IS_SEARCH_DIRECTORY (directory_view->details->model)) {
+ search = CAJA_SEARCH_DIRECTORY (directory_view->details->model);
+
+ query = caja_search_directory_get_query (search);
+
+ dialog = gtk_dialog_new_with_buttons (_("Save Search as"),
+ fm_directory_view_get_containing_window (directory_view),
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ save_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 5);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ label = gtk_label_new_with_mnemonic (_("Search _name:"));
+ gtk_misc_set_alignment (GTK_MISC(label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (label);
+ entry = gtk_entry_new ();
+ gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+ gtk_widget_set_sensitive (save_button, FALSE);
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (query_name_entry_changed_cb), save_button);
+
+ gtk_widget_show (entry);
+ label = gtk_label_new_with_mnemonic (_("_Folder:"));
+ gtk_misc_set_alignment (GTK_MISC(label), 0.0, 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (label);
+
+ chooser = gtk_file_chooser_button_new (_("Select Folder to Save Search In"),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ gtk_table_attach (GTK_TABLE (table), chooser, 1, 2, 1, 2, GTK_FILL | GTK_EXPAND, 0, 0, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser);
+ gtk_widget_show (chooser);
+
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
+ g_get_home_dir ());
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+ entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
+ if (g_str_has_suffix (entry_text, CAJA_SAVED_SEARCH_EXTENSION)) {
+ filename_utf8 = g_strdup (entry_text);
+ } else {
+ filename_utf8 = g_strconcat (entry_text, CAJA_SAVED_SEARCH_EXTENSION, NULL);
+ }
+
+ filename = g_filename_from_utf8 (filename_utf8, -1, NULL, NULL, NULL);
+ g_free (filename_utf8);
+
+ dirname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
+
+ path = g_build_filename (dirname, filename, NULL);
+ g_free (filename);
+ g_free (dirname);
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ g_free (path);
+
+ caja_search_directory_save_to_file (search, uri);
+ location = g_file_new_for_uri (uri);
+ caja_file_changes_queue_file_added (location);
+ g_object_unref (location);
+ caja_file_changes_consume_changes (TRUE);
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (dialog);
+ }
+}
+
+
+static void
+action_empty_trash_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ caja_file_operations_empty_trash (GTK_WIDGET (callback_data));
+}
+
+static void
+action_new_folder_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_new_folder (FM_DIRECTORY_VIEW (callback_data));
+}
+
+static void
+action_new_empty_file_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_new_file (FM_DIRECTORY_VIEW (callback_data), NULL, NULL);
+}
+
+static void
+action_new_launcher_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ char *parent_uri;
+ FMDirectoryView *view;
+ GtkWindow *window;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ parent_uri = fm_directory_view_get_backing_uri (view);
+
+ window = fm_directory_view_get_containing_window (view);
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "directory view create new launcher in window=%p: %s", window, parent_uri);
+ caja_launch_application_from_command (gtk_widget_get_screen (GTK_WIDGET (view)),
+ "mate-desktop-item-edit",
+ "mate-desktop-item-edit",
+ FALSE,
+ "--create-new", parent_uri, NULL);
+
+ g_free (parent_uri);
+}
+
+static void
+action_properties_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+ GList *files;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+ if (g_list_length (selection) == 0) {
+ if (view->details->directory_as_file != NULL) {
+ files = g_list_append (NULL, caja_file_ref (view->details->directory_as_file));
+
+ fm_properties_window_present (files, GTK_WIDGET (view));
+
+ caja_file_list_free (files);
+ }
+ } else {
+ fm_properties_window_present (selection, GTK_WIDGET (view));
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_location_properties_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *files;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
+
+ files = g_list_append (NULL, caja_file_ref (view->details->location_popup_directory_as_file));
+
+ fm_properties_window_present (files, GTK_WIDGET (view));
+
+ caja_file_list_free (files);
+}
+
+static gboolean
+all_files_in_trash (GList *files)
+{
+ GList *node;
+
+ /* Result is ambiguous if called on NULL, so disallow. */
+ g_return_val_if_fail (files != NULL, FALSE);
+
+ for (node = files; node != NULL; node = node->next) {
+ if (!caja_file_is_in_trash (CAJA_FILE (node->data))) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+all_selected_items_in_trash (FMDirectoryView *view)
+{
+ GList *selection;
+ gboolean result;
+
+ /* If the contents share a parent directory, we need only
+ * check that parent directory. Otherwise we have to inspect
+ * each selected item.
+ */
+ selection = fm_directory_view_get_selection (view);
+ result = (selection == NULL) ? FALSE : all_files_in_trash (selection);
+ caja_file_list_free (selection);
+
+ return result;
+}
+
+static gboolean
+we_are_in_vfolder_desktop_dir (FMDirectoryView *view)
+{
+ CajaFile *file;
+ char *mime_type;
+
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ if (view->details->model == NULL) {
+ return FALSE;
+ }
+
+ file = caja_directory_get_corresponding_file (view->details->model);
+ mime_type = caja_file_get_mime_type (file);
+ caja_file_unref (file);
+
+ if (mime_type != NULL
+ && strcmp (mime_type, "x-directory/vfolder-desktop") == 0) {
+ g_free (mime_type);
+ return TRUE;
+ } else {
+ g_free (mime_type);
+ return FALSE;
+ }
+}
+
+/* Preferences changed callbacks */
+static void
+text_attribute_names_changed_callback (gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ text_attribute_names_changed, (view));
+}
+
+static void
+image_display_policy_changed_callback (gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ image_display_policy_changed, (view));
+}
+
+static void
+click_policy_changed_callback (gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ click_policy_changed, (view));
+}
+
+gboolean
+fm_directory_view_should_sort_directories_first (FMDirectoryView *view)
+{
+ return view->details->sort_directories_first;
+}
+
+static void
+sort_directories_first_changed_callback (gpointer callback_data)
+{
+ FMDirectoryView *view;
+ gboolean preference_value;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ preference_value =
+ eel_preferences_get_boolean (CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST);
+
+ if (preference_value != view->details->sort_directories_first) {
+ view->details->sort_directories_first = preference_value;
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ sort_directories_first_changed, (view));
+ }
+}
+
+static void
+lockdown_disable_command_line_changed_callback (gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ schedule_update_menus (view);
+}
+
+static void set_up_scripts_directory_global(void)
+{
+ if (scripts_directory_uri != NULL)
+ {
+ return;
+ }
+
+ char* scripts_directory_path;
+ const char* override = g_getenv ("MATE22_USER_DIR"); //TODO: quitar?
+
+ if (override)
+ {
+ scripts_directory_path = g_build_filename(override, "caja", "scripts", NULL);
+ }
+ else
+ {
+ scripts_directory_path = g_build_filename(g_get_home_dir(), ".config", "caja", "scripts", NULL);
+ }
+
+ if (g_mkdir_with_parents(scripts_directory_path, 0755) == 0)
+ {
+ scripts_directory_uri = g_filename_to_uri(scripts_directory_path, NULL, NULL);
+ scripts_directory_uri_length = strlen(scripts_directory_uri);
+
+ /* Emulación de GNOME Nautilus scripts
+ */
+ char* nautilus_scripts_path = g_build_filename(g_get_home_dir(), ".gnome2", "nautilus-scripts", NULL);
+
+ if (g_file_test(nautilus_scripts_path, G_FILE_TEST_IS_DIR) == TRUE)
+ {
+ char* nautilus_syslink = g_build_filename(g_get_home_dir(), ".config", "caja", "scripts", "nautilus", NULL);
+ // G_FILE_TEST_IS_REGULAR
+ /* En caso de que exista el enlace, o algún otro tipo de archivo con
+ * el mismo nombre, ignoramos. Incluso si es una carpeta. */
+ if (g_file_test(nautilus_syslink, G_FILE_TEST_IS_SYMLINK) == FALSE &&
+ g_file_test(nautilus_syslink, G_FILE_TEST_EXISTS) == FALSE &&
+ g_file_test(nautilus_syslink, G_FILE_TEST_IS_DIR) == FALSE)
+ {
+ /* Nos fijamos si es necesario crear un enlace */
+ GDir* dir = g_dir_open(nautilus_scripts_path, 0, NULL);
+
+ if (dir)
+ {
+ /* Con tener más de un elemento en la carpeta, podemos hacer
+ * el enlace */
+ int count = 0;
+
+ while (g_dir_read_name(dir) != NULL)
+ {
+ count++;
+ }
+
+ if (count > 0)
+ {
+ /* creamos un enlace a la carpeta de nautilus */
+ symlink(nautilus_scripts_path, nautilus_syslink);
+ }
+
+ g_dir_close(dir);
+ }
+ }
+
+ g_free(nautilus_syslink);
+ }
+
+ g_free(nautilus_scripts_path);
+ }
+
+ g_free(scripts_directory_path);
+}
+
+static void
+scripts_added_or_changed_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ view->details->scripts_invalid = TRUE;
+ if (view->details->active) {
+ schedule_update_menus (view);
+ }
+}
+
+static void
+templates_added_or_changed_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ view->details->templates_invalid = TRUE;
+ if (view->details->active) {
+ schedule_update_menus (view);
+ }
+}
+
+static void
+add_directory_to_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory,
+ GList **directory_list,
+ GCallback changed_callback)
+{
+ CajaFileAttributes attributes;
+
+ if (g_list_find (*directory_list, directory) == NULL) {
+ caja_directory_ref (directory);
+
+ attributes =
+ CAJA_FILE_ATTRIBUTES_FOR_ICON |
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT;
+
+ caja_directory_file_monitor_add (directory, directory_list,
+ FALSE, FALSE, attributes,
+ (CajaDirectoryCallback)changed_callback, view);
+
+ g_signal_connect_object (directory, "files_added",
+ G_CALLBACK (changed_callback), view, 0);
+ g_signal_connect_object (directory, "files_changed",
+ G_CALLBACK (changed_callback), view, 0);
+
+ *directory_list = g_list_append (*directory_list, directory);
+ }
+}
+
+static void
+remove_directory_from_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory,
+ GList **directory_list,
+ GCallback changed_callback)
+{
+ *directory_list = g_list_remove (*directory_list, directory);
+
+ g_signal_handlers_disconnect_by_func (directory,
+ G_CALLBACK (changed_callback),
+ view);
+
+ caja_directory_file_monitor_remove (directory, directory_list);
+
+ caja_directory_unref (directory);
+}
+
+
+static void
+add_directory_to_scripts_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory)
+{
+ add_directory_to_directory_list (view, directory,
+ &view->details->scripts_directory_list,
+ G_CALLBACK (scripts_added_or_changed_callback));
+}
+
+static void
+remove_directory_from_scripts_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory)
+{
+ remove_directory_from_directory_list (view, directory,
+ &view->details->scripts_directory_list,
+ G_CALLBACK (scripts_added_or_changed_callback));
+}
+
+static void
+add_directory_to_templates_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory)
+{
+ add_directory_to_directory_list (view, directory,
+ &view->details->templates_directory_list,
+ G_CALLBACK (templates_added_or_changed_callback));
+}
+
+static void
+remove_directory_from_templates_directory_list (FMDirectoryView *view,
+ CajaDirectory *directory)
+{
+ remove_directory_from_directory_list (view, directory,
+ &view->details->templates_directory_list,
+ G_CALLBACK (templates_added_or_changed_callback));
+}
+
+static void
+slot_active (CajaWindowSlot *slot,
+ FMDirectoryView *view)
+{
+ g_assert (!view->details->active);
+ view->details->active = TRUE;
+
+ fm_directory_view_merge_menus (view);
+ schedule_update_menus (view);
+}
+
+static void
+slot_inactive (CajaWindowSlot *slot,
+ FMDirectoryView *view)
+{
+ g_assert (view->details->active ||
+ gtk_widget_get_parent (GTK_WIDGET (view)) == NULL);
+ view->details->active = FALSE;
+
+ fm_directory_view_unmerge_menus (view);
+ remove_update_menus_timeout_callback (view);
+}
+
+static void
+fm_directory_view_grab_focus (CajaView *view)
+{
+ /* focus the child of the scrolled window if it exists */
+ GtkWidget *child;
+ child = gtk_bin_get_child (GTK_BIN (view));
+ if (child) {
+ gtk_widget_grab_focus (GTK_WIDGET (child));
+ }
+}
+
+static void
+view_iface_update_menus (CajaView *view)
+{
+ fm_directory_view_update_menus (FM_DIRECTORY_VIEW (view));
+}
+
+static GtkWidget *
+fm_directory_view_get_widget (CajaView *view)
+{
+ return GTK_WIDGET (view);
+}
+
+static int
+fm_directory_view_get_selection_count (CajaView *view)
+{
+ /* FIXME: This could be faster if we special cased it in subclasses */
+ GList *files;
+ int len;
+
+ files = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
+ len = g_list_length (files);
+ caja_file_list_free (files);
+
+ return len;
+}
+
+static GList *
+fm_directory_view_get_selection_locations (CajaView *view)
+{
+ GList *files;
+ GList *locations;
+ GFile *location;
+ GList *l;
+
+ files = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
+ locations = NULL;
+ for (l = files; l != NULL; l = l->next) {
+ location = caja_file_get_location (CAJA_FILE (l->data));
+ locations = g_list_prepend (locations, location);
+ }
+ caja_file_list_free (files);
+
+ return g_list_reverse (locations);
+}
+
+static GList *
+file_list_from_location_list (const GList *uri_list)
+{
+ GList *file_list;
+ const GList *node;
+
+ file_list = NULL;
+ for (node = uri_list; node != NULL; node = node->next) {
+ file_list = g_list_prepend
+ (file_list,
+ caja_file_get (node->data));
+ }
+ return g_list_reverse (file_list);
+}
+
+static void
+fm_directory_view_set_selection_locations (CajaView *caja_view,
+ GList *selection_locations)
+{
+ GList *selection;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (caja_view);
+
+ if (!view->details->loading) {
+ /* If we aren't still loading, set the selection right now,
+ * and reveal the new selection.
+ */
+ selection = file_list_from_location_list (selection_locations);
+ view->details->selection_change_is_due_to_shell = TRUE;
+ fm_directory_view_set_selection (view, selection);
+ view->details->selection_change_is_due_to_shell = FALSE;
+ fm_directory_view_reveal_selection (view);
+ caja_file_list_free (selection);
+ } else {
+ /* If we are still loading, set the list of pending URIs instead.
+ * done_loading() will eventually select the pending URIs and reveal them.
+ */
+ eel_g_object_list_free (view->details->pending_locations_selected);
+ view->details->pending_locations_selected =
+ eel_g_object_list_copy (selection_locations);
+ }
+}
+
+
+void
+fm_directory_view_init_view_iface (CajaViewIface *iface)
+{
+ iface->grab_focus = fm_directory_view_grab_focus;
+ iface->update_menus = view_iface_update_menus;
+
+ iface->get_widget = fm_directory_view_get_widget;
+ iface->load_location = fm_directory_view_load_location;
+ iface->stop_loading = fm_directory_view_stop_loading;
+
+ iface->get_selection_count = fm_directory_view_get_selection_count;
+ iface->get_selection = fm_directory_view_get_selection_locations;
+ iface->set_selection = fm_directory_view_set_selection_locations;
+ iface->set_is_active = (gpointer)fm_directory_view_set_is_active;
+
+ iface->supports_zooming = (gpointer)fm_directory_view_supports_zooming;
+ iface->bump_zoom_level = (gpointer)fm_directory_view_bump_zoom_level;
+ iface->zoom_to_level = (gpointer)fm_directory_view_zoom_to_level;
+ iface->restore_default_zoom_level = (gpointer)fm_directory_view_restore_default_zoom_level;
+ iface->can_zoom_in = (gpointer)fm_directory_view_can_zoom_in;
+ iface->can_zoom_out = (gpointer)fm_directory_view_can_zoom_out;
+ iface->get_zoom_level = (gpointer)fm_directory_view_get_zoom_level;
+
+ iface->pop_up_location_context_menu = (gpointer)fm_directory_view_pop_up_location_context_menu;
+ iface->drop_proxy_received_uris = (gpointer)fm_directory_view_drop_proxy_received_uris;
+ iface->drop_proxy_received_netscape_url = (gpointer)fm_directory_view_drop_proxy_received_netscape_url;
+}
+
+static void
+fm_directory_view_init (FMDirectoryView *view)
+{
+ static gboolean setup_autos = FALSE;
+ CajaDirectory *scripts_directory;
+ CajaDirectory *templates_directory;
+ char *templates_uri;
+
+ if (!setup_autos) {
+ setup_autos = TRUE;
+ eel_preferences_add_auto_boolean (CAJA_PREFERENCES_CONFIRM_TRASH,
+ &confirm_trash_auto_value);
+ eel_preferences_add_auto_boolean (CAJA_PREFERENCES_ENABLE_DELETE,
+ &show_delete_command_auto_value);
+ }
+
+ view->details = g_new0 (FMDirectoryViewDetails, 1);
+
+ /* Default to true; desktop-icon-view sets to false */
+ view->details->show_foreign_files = TRUE;
+
+ view->details->non_ready_files =
+ g_hash_table_new_full (file_and_directory_hash,
+ file_and_directory_equal,
+ (GDestroyNotify)file_and_directory_free,
+ NULL);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (view), NULL);
+ gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (view), NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view), GTK_SHADOW_ETCHED_IN);
+
+ set_up_scripts_directory_global ();
+ scripts_directory = caja_directory_get_by_uri (scripts_directory_uri);
+ add_directory_to_scripts_directory_list (view, scripts_directory);
+ caja_directory_unref (scripts_directory);
+
+ if (caja_should_use_templates_directory ()) {
+ templates_uri = caja_get_templates_directory_uri ();
+ templates_directory = caja_directory_get_by_uri (templates_uri);
+ g_free (templates_uri);
+ add_directory_to_templates_directory_list (view, templates_directory);
+ caja_directory_unref (templates_directory);
+ }
+ update_templates_directory (view);
+ g_signal_connect_object (caja_signaller_get_current (),
+ "user_dirs_changed",
+ G_CALLBACK (user_dirs_changed),
+ view, G_CONNECT_SWAPPED);
+
+ view->details->sort_directories_first =
+ eel_preferences_get_boolean (CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST);
+
+ g_signal_connect_object (caja_trash_monitor_get (), "trash_state_changed",
+ G_CALLBACK (fm_directory_view_trash_state_changed_callback), view, 0);
+
+ /* React to clipboard changes */
+ g_signal_connect_object (caja_clipboard_monitor_get (), "clipboard_changed",
+ G_CALLBACK (clipboard_changed_callback), view, 0);
+
+ /* Register to menu provider extension signal managing menu updates */
+ g_signal_connect_object (caja_signaller_get_current (), "popup_menu_changed",
+ G_CALLBACK (fm_directory_view_update_menus), view, G_CONNECT_SWAPPED);
+
+ gtk_widget_show (GTK_WIDGET (view));
+
+ eel_preferences_add_callback (CAJA_PREFERENCES_CONFIRM_TRASH,
+ schedule_update_menus_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_ENABLE_DELETE,
+ schedule_update_menus_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_ICON_VIEW_CAPTIONS,
+ text_attribute_names_changed_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS,
+ image_display_policy_changed_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_CLICK_POLICY,
+ click_policy_changed_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST,
+ sort_directories_first_changed_callback, view);
+ eel_preferences_add_callback (CAJA_PREFERENCES_LOCKDOWN_COMMAND_LINE,
+ lockdown_disable_command_line_changed_callback, view);
+}
+
+static void
+real_unmerge_menus (FMDirectoryView *view)
+{
+ GtkUIManager *ui_manager;
+
+ if (view->details->window == NULL) {
+ return;
+ }
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->dir_merge_id,
+ &view->details->dir_action_group);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->extensions_menu_merge_id,
+ &view->details->extensions_menu_action_group);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->open_with_merge_id,
+ &view->details->open_with_action_group);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->scripts_merge_id,
+ &view->details->scripts_action_group);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->templates_merge_id,
+ &view->details->templates_action_group);
+}
+
+static void
+fm_directory_view_destroy (GtkObject *object)
+{
+ FMDirectoryView *view;
+ GList *node, *next;
+
+ view = FM_DIRECTORY_VIEW (object);
+
+ disconnect_model_handlers (view);
+
+ fm_directory_view_unmerge_menus (view);
+
+ /* We don't own the window, so no unref */
+ view->details->slot = NULL;
+ view->details->window = NULL;
+
+ fm_directory_view_stop (view);
+ fm_directory_view_clear (view);
+
+ for (node = view->details->scripts_directory_list; node != NULL; node = next) {
+ next = node->next;
+ remove_directory_from_scripts_directory_list (view, node->data);
+ }
+
+ for (node = view->details->templates_directory_list; node != NULL; node = next) {
+ next = node->next;
+ remove_directory_from_templates_directory_list (view, node->data);
+ }
+
+ while (view->details->subdirectory_list != NULL) {
+ fm_directory_view_remove_subdirectory (view,
+ view->details->subdirectory_list->data);
+ }
+
+ remove_update_menus_timeout_callback (view);
+ remove_update_status_idle_callback (view);
+
+ if (view->details->display_selection_idle_id != 0) {
+ g_source_remove (view->details->display_selection_idle_id);
+ view->details->display_selection_idle_id = 0;
+ }
+
+ if (view->details->reveal_selection_idle_id != 0) {
+ g_source_remove (view->details->reveal_selection_idle_id);
+ view->details->reveal_selection_idle_id = 0;
+ }
+
+ if (view->details->delayed_rename_file_id != 0) {
+ g_source_remove (view->details->delayed_rename_file_id);
+ view->details->delayed_rename_file_id = 0;
+ }
+
+ if (view->details->model) {
+ caja_directory_unref (view->details->model);
+ view->details->model = NULL;
+ }
+
+ if (view->details->directory_as_file) {
+ caja_file_unref (view->details->directory_as_file);
+ view->details->directory_as_file = NULL;
+ }
+
+ EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static void
+fm_directory_view_finalize (GObject *object)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (object);
+
+ eel_preferences_remove_callback (CAJA_PREFERENCES_CONFIRM_TRASH,
+ schedule_update_menus_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_ENABLE_DELETE,
+ schedule_update_menus_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_ICON_VIEW_CAPTIONS,
+ text_attribute_names_changed_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS,
+ image_display_policy_changed_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_CLICK_POLICY,
+ click_policy_changed_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST,
+ sort_directories_first_changed_callback, view);
+ eel_preferences_remove_callback (CAJA_PREFERENCES_LOCKDOWN_COMMAND_LINE,
+ lockdown_disable_command_line_changed_callback, view);
+
+ unschedule_pop_up_location_context_menu (view);
+ if (view->details->location_popup_event != NULL) {
+ gdk_event_free ((GdkEvent *) view->details->location_popup_event);
+ }
+
+ g_hash_table_destroy (view->details->non_ready_files);
+
+ g_free (view->details);
+
+ EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
+}
+
+/**
+ * fm_directory_view_display_selection_info:
+ *
+ * Display information about the current selection, and notify the view frame of the changed selection.
+ * @view: FMDirectoryView for which to display selection info.
+ *
+ **/
+void
+fm_directory_view_display_selection_info (FMDirectoryView *view)
+{
+ GList *selection;
+ goffset non_folder_size;
+ gboolean non_folder_size_known;
+ guint non_folder_count, folder_count, folder_item_count;
+ gboolean folder_item_count_known;
+ guint file_item_count;
+ GList *p;
+ char *first_item_name;
+ char *non_folder_str;
+ char *folder_count_str;
+ char *folder_item_count_str;
+ char *status_string;
+ char *free_space_str;
+ char *obj_selected_free_space_str;
+ CajaFile *file;
+
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ selection = fm_directory_view_get_selection (view);
+
+ folder_item_count_known = TRUE;
+ folder_count = 0;
+ folder_item_count = 0;
+ non_folder_count = 0;
+ non_folder_size_known = FALSE;
+ non_folder_size = 0;
+ first_item_name = NULL;
+ folder_count_str = NULL;
+ non_folder_str = NULL;
+ folder_item_count_str = NULL;
+ free_space_str = NULL;
+ obj_selected_free_space_str = NULL;
+
+ for (p = selection; p != NULL; p = p->next) {
+ file = p->data;
+ if (caja_file_is_directory (file)) {
+ folder_count++;
+ if (caja_file_get_directory_item_count (file, &file_item_count, NULL)) {
+ folder_item_count += file_item_count;
+ } else {
+ folder_item_count_known = FALSE;
+ }
+ } else {
+ non_folder_count++;
+ if (!caja_file_can_get_size (file)) {
+ non_folder_size_known = TRUE;
+ non_folder_size += caja_file_get_size (file);
+ }
+ }
+
+ if (first_item_name == NULL) {
+ first_item_name = caja_file_get_display_name (file);
+ }
+ }
+
+ caja_file_list_free (selection);
+
+ /* Break out cases for localization's sake. But note that there are still pieces
+ * being assembled in a particular order, which may be a problem for some localizers.
+ */
+
+ if (folder_count != 0) {
+ if (folder_count == 1 && non_folder_count == 0) {
+ folder_count_str = g_strdup_printf (_("\"%s\" selected"), first_item_name);
+ } else {
+ folder_count_str = g_strdup_printf (ngettext("%'d folder selected",
+ "%'d folders selected",
+ folder_count),
+ folder_count);
+ }
+
+ if (folder_count == 1) {
+ if (!folder_item_count_known) {
+ folder_item_count_str = g_strdup ("");
+ } else {
+ folder_item_count_str = g_strdup_printf (ngettext(" (containing %'d item)",
+ " (containing %'d items)",
+ folder_item_count),
+ folder_item_count);
+ }
+ }
+ else {
+ if (!folder_item_count_known) {
+ folder_item_count_str = g_strdup ("");
+ } else {
+ /* translators: this is preceded with a string of form 'N folders' (N more than 1) */
+ folder_item_count_str = g_strdup_printf (ngettext(" (containing a total of %'d item)",
+ " (containing a total of %'d items)",
+ folder_item_count),
+ folder_item_count);
+ }
+
+ }
+ }
+
+ if (non_folder_count != 0) {
+ char *items_string;
+
+ if (folder_count == 0) {
+ if (non_folder_count == 1) {
+ items_string = g_strdup_printf (_("\"%s\" selected"),
+ first_item_name);
+ } else {
+ items_string = g_strdup_printf (ngettext("%'d item selected",
+ "%'d items selected",
+ non_folder_count),
+ non_folder_count);
+ }
+ } else {
+ /* Folders selected also, use "other" terminology */
+ items_string = g_strdup_printf (ngettext("%'d other item selected",
+ "%'d other items selected",
+ non_folder_count),
+ non_folder_count);
+ }
+
+ if (non_folder_size_known) {
+ char *size_string;
+
+ #if GLIB_CHECK_VERSION(2, 30, 0)
+ size_string = g_format_size(non_folder_size);
+ #else
+ size_string = g_format_size_for_display(non_folder_size);
+ #endif
+
+ /* This is marked for translation in case a localiser
+ * needs to use something other than parentheses. The
+ * first message gives the number of items selected;
+ * the message in parentheses the size of those items.
+ */
+ non_folder_str = g_strdup_printf (_("%s (%s)"),
+ items_string,
+ size_string);
+
+ g_free (size_string);
+ g_free (items_string);
+ } else {
+ non_folder_str = items_string;
+ }
+ }
+
+ free_space_str = caja_file_get_volume_free_space (view->details->directory_as_file);
+ if (free_space_str != NULL) {
+ obj_selected_free_space_str = g_strdup_printf (_("Free space: %s"), free_space_str);
+ }
+ if (folder_count == 0 && non_folder_count == 0) {
+ char *item_count_str;
+ guint item_count;
+
+ item_count = fm_directory_view_get_item_count (view);
+
+ item_count_str = g_strdup_printf (ngettext ("%'u item", "%'u items", item_count), item_count);
+
+ if (free_space_str != NULL) {
+ status_string = g_strdup_printf (_("%s, Free space: %s"), item_count_str, free_space_str);
+ g_free (item_count_str);
+ } else {
+ status_string = item_count_str;
+ }
+
+ } else if (folder_count == 0) {
+ if (free_space_str == NULL) {
+ status_string = g_strdup (non_folder_str);
+ } else {
+ /* Marking this for translation, since you
+ * might want to change "," to something else.
+ * After the comma the amount of free space will
+ * be shown.
+ */
+ status_string = g_strdup_printf (_("%s, %s"),
+ non_folder_str,
+ obj_selected_free_space_str);
+ }
+ } else if (non_folder_count == 0) {
+ if (free_space_str == NULL) {
+ /* No use marking this for translation, since you
+ * can't reorder the strings, which is the main thing
+ * you'd want to do.
+ */
+ status_string = g_strdup_printf ("%s%s",
+ folder_count_str,
+ folder_item_count_str);
+ } else {
+ /* Marking this for translation, since you
+ * might want to change "," to something else.
+ * After the comma the amount of free space will
+ * be shown.
+ */
+ status_string = g_strdup_printf (_("%s%s, %s"),
+ folder_count_str,
+ folder_item_count_str,
+ obj_selected_free_space_str);
+ }
+ } else {
+ if (obj_selected_free_space_str == NULL) {
+ /* This is marked for translation in case a localizer
+ * needs to change ", " to something else. The comma
+ * is between the message about the number of folders
+ * and the number of items in those folders and the
+ * message about the number of other items and the
+ * total size of those items.
+ */
+ status_string = g_strdup_printf (_("%s%s, %s"),
+ folder_count_str,
+ folder_item_count_str,
+ non_folder_str);
+ } else {
+ /* This is marked for translation in case a localizer
+ * needs to change ", " to something else. The first comma
+ * is between the message about the number of folders
+ * and the number of items in those folders and the
+ * message about the number of other items and the
+ * total size of those items. After the second comma
+ * the free space is written.
+ */
+ status_string = g_strdup_printf (_("%s%s, %s, %s"),
+ folder_count_str,
+ folder_item_count_str,
+ non_folder_str,
+ obj_selected_free_space_str);
+ }
+ }
+
+ g_free (free_space_str);
+ g_free (obj_selected_free_space_str);
+ g_free (first_item_name);
+ g_free (folder_count_str);
+ g_free (folder_item_count_str);
+ g_free (non_folder_str);
+
+ caja_window_slot_info_set_status (view->details->slot,
+ status_string);
+ g_free (status_string);
+}
+
+void
+fm_directory_view_send_selection_change (FMDirectoryView *view)
+{
+ caja_window_info_report_selection_changed (view->details->window);
+
+ view->details->send_selection_change_to_shell = FALSE;
+}
+
+gboolean
+fm_directory_view_get_allow_moves (FMDirectoryView *view)
+{
+ return view->details->allow_moves;
+}
+
+static void
+fm_directory_view_load_location (CajaView *caja_view,
+ const char *location)
+{
+ CajaDirectory *directory;
+ FMDirectoryView *directory_view;
+
+ directory_view = FM_DIRECTORY_VIEW (caja_view);
+
+ if (eel_uri_is_search (location)) {
+ directory_view->details->allow_moves = FALSE;
+ } else {
+ directory_view->details->allow_moves = TRUE;
+ }
+
+ directory = caja_directory_get_by_uri (location);
+ load_directory (directory_view, directory);
+ caja_directory_unref (directory);
+}
+
+static void
+fm_directory_view_stop_loading (CajaView *caja_view)
+{
+ fm_directory_view_stop (FM_DIRECTORY_VIEW (caja_view));
+}
+
+static void
+fm_directory_view_file_limit_reached (FMDirectoryView *view)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
+ file_limit_reached, (view));
+}
+
+static void
+real_file_limit_reached (FMDirectoryView *view)
+{
+ CajaFile *file;
+ GtkDialog *dialog;
+ char *directory_name;
+ char *message;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ file = fm_directory_view_get_directory_as_file (view);
+ directory_name = caja_file_get_display_name (file);
+
+ /* Note that the number of items actually displayed varies somewhat due
+ * to the way files are collected in batches. So you can't assume that
+ * no more than the constant limit are displayed.
+ */
+ message = g_strdup_printf (_("The folder \"%s\" contains more files than "
+ "Caja can handle."),
+ directory_name);
+ g_free (directory_name);
+
+ dialog = eel_show_warning_dialog (message,
+ _("Some files will not be displayed."),
+ fm_directory_view_get_containing_window (view));
+ g_free (message);
+}
+
+static void
+check_for_directory_hard_limit (FMDirectoryView *view)
+{
+ if (caja_directory_file_list_length_reached (view->details->model)) {
+ fm_directory_view_file_limit_reached (view);
+ }
+}
+
+static gboolean
+reveal_selection_idle_callback (gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ view->details->reveal_selection_idle_id = 0;
+ fm_directory_view_reveal_selection (view);
+
+ return FALSE;
+}
+
+static void
+done_loading (FMDirectoryView *view,
+ gboolean all_files_seen)
+{
+ GList *locations_selected, *selection;
+
+ if (!view->details->loading) {
+ return;
+ }
+
+ /* This can be called during destruction, in which case there
+ * is no CajaWindowInfo any more.
+ */
+ if (view->details->window != NULL) {
+ if (all_files_seen) {
+ caja_window_info_report_load_complete (view->details->window, CAJA_VIEW (view));
+ }
+
+ schedule_update_menus (view);
+ schedule_update_status (view);
+ check_for_directory_hard_limit (view);
+ reset_update_interval (view);
+
+ locations_selected = view->details->pending_locations_selected;
+ if (locations_selected != NULL && all_files_seen) {
+ view->details->pending_locations_selected = NULL;
+
+ selection = file_list_from_location_list (locations_selected);
+
+ view->details->selection_change_is_due_to_shell = TRUE;
+ fm_directory_view_set_selection (view, selection);
+ view->details->selection_change_is_due_to_shell = FALSE;
+ caja_file_list_free (selection);
+
+ if (FM_IS_LIST_VIEW (view)) {
+ /* HACK: We should be able to directly call reveal_selection here,
+ * but at this point the GtkTreeView hasn't allocated the new nodes
+ * yet, and it has a bug in the scroll calculation dealing with this
+ * special case. It would always make the selection the top row, even
+ * if no scrolling would be neccessary to reveal it. So we let it
+ * allocate before revealing.
+ */
+ if (view->details->reveal_selection_idle_id != 0) {
+ g_source_remove (view->details->reveal_selection_idle_id);
+ }
+ view->details->reveal_selection_idle_id =
+ g_idle_add (reveal_selection_idle_callback, view);
+ } else {
+ fm_directory_view_reveal_selection (view);
+ }
+ }
+ eel_g_object_list_free (locations_selected);
+ fm_directory_view_display_selection_info (view);
+ }
+
+ fm_directory_view_end_loading (view, all_files_seen);
+
+ view->details->loading = FALSE;
+}
+
+
+typedef struct {
+ GHashTable *debuting_files;
+ GList *added_files;
+} DebutingFilesData;
+
+static void
+debuting_files_data_free (DebutingFilesData *data)
+{
+ g_hash_table_unref (data->debuting_files);
+ caja_file_list_free (data->added_files);
+ g_free (data);
+}
+
+/* This signal handler watch for the arrival of the icons created
+ * as the result of a file operation. Once the last one is detected
+ * it selects and reveals them all.
+ */
+static void
+debuting_files_add_file_callback (FMDirectoryView *view,
+ CajaFile *new_file,
+ CajaDirectory *directory,
+ DebutingFilesData *data)
+{
+ GFile *location;
+
+ location = caja_file_get_location (new_file);
+
+ if (g_hash_table_remove (data->debuting_files, location)) {
+ caja_file_ref (new_file);
+ data->added_files = g_list_prepend (data->added_files, new_file);
+
+ if (g_hash_table_size (data->debuting_files) == 0) {
+ fm_directory_view_set_selection (view, data->added_files);
+ fm_directory_view_reveal_selection (view);
+ g_signal_handlers_disconnect_by_func (view,
+ G_CALLBACK (debuting_files_add_file_callback),
+ data);
+ }
+ }
+
+ g_object_unref (location);
+}
+
+typedef struct {
+ GList *added_files;
+ FMDirectoryView *directory_view;
+} CopyMoveDoneData;
+
+static void
+copy_move_done_data_free (CopyMoveDoneData *data)
+{
+ g_assert (data != NULL);
+
+ eel_remove_weak_pointer (&data->directory_view);
+ caja_file_list_free (data->added_files);
+ g_free (data);
+}
+
+static void
+pre_copy_move_add_file_callback (FMDirectoryView *view,
+ CajaFile *new_file,
+ CajaDirectory *directory,
+ CopyMoveDoneData *data)
+{
+ caja_file_ref (new_file);
+ data->added_files = g_list_prepend (data->added_files, new_file);
+}
+
+/* This needs to be called prior to caja_file_operations_copy_move.
+ * It hooks up a signal handler to catch any icons that get added before
+ * the copy_done_callback is invoked. The return value should be passed
+ * as the data for uri_copy_move_done_callback.
+ */
+static CopyMoveDoneData *
+pre_copy_move (FMDirectoryView *directory_view)
+{
+ CopyMoveDoneData *copy_move_done_data;
+
+ copy_move_done_data = g_new0 (CopyMoveDoneData, 1);
+ copy_move_done_data->directory_view = directory_view;
+
+ eel_add_weak_pointer (&copy_move_done_data->directory_view);
+
+ /* We need to run after the default handler adds the folder we want to
+ * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
+ * must use connect_after.
+ */
+ g_signal_connect (directory_view, "add_file",
+ G_CALLBACK (pre_copy_move_add_file_callback), copy_move_done_data);
+
+ return copy_move_done_data;
+}
+
+/* This function is used to pull out any debuting uris that were added
+ * and (as a side effect) remove them from the debuting uri hash table.
+ */
+static gboolean
+copy_move_done_partition_func (gpointer data, gpointer callback_data)
+{
+ GFile *location;
+ gboolean result;
+
+ location = caja_file_get_location (CAJA_FILE (data));
+ result = g_hash_table_remove ((GHashTable *) callback_data, location);
+ g_object_unref (location);
+
+ return result;
+}
+
+static gboolean
+remove_not_really_moved_files (gpointer key,
+ gpointer value,
+ gpointer callback_data)
+{
+ GList **added_files;
+ GFile *loc;
+
+ loc = key;
+
+ if (GPOINTER_TO_INT (value)) {
+ return FALSE;
+ }
+
+ added_files = callback_data;
+ *added_files = g_list_prepend (*added_files,
+ caja_file_get (loc));
+ return TRUE;
+}
+
+
+/* When this function is invoked, the file operation is over, but all
+ * the icons may not have been added to the directory view yet, so
+ * we can't select them yet.
+ *
+ * We're passed a hash table of the uri's to look out for, we hook
+ * up a signal handler to await their arrival.
+ */
+static void
+copy_move_done_callback (GHashTable *debuting_files, gpointer data)
+{
+ FMDirectoryView *directory_view;
+ CopyMoveDoneData *copy_move_done_data;
+ DebutingFilesData *debuting_files_data;
+
+ copy_move_done_data = (CopyMoveDoneData *) data;
+ directory_view = copy_move_done_data->directory_view;
+
+ if (directory_view != NULL) {
+ g_assert (FM_IS_DIRECTORY_VIEW (directory_view));
+
+ debuting_files_data = g_new (DebutingFilesData, 1);
+ debuting_files_data->debuting_files = g_hash_table_ref (debuting_files);
+ debuting_files_data->added_files = eel_g_list_partition
+ (copy_move_done_data->added_files,
+ copy_move_done_partition_func,
+ debuting_files,
+ &copy_move_done_data->added_files);
+
+ /* We're passed the same data used by pre_copy_move_add_file_callback, so disconnecting
+ * it will free data. We've already siphoned off the added_files we need, and stashed the
+ * directory_view pointer.
+ */
+ g_signal_handlers_disconnect_by_func (directory_view,
+ G_CALLBACK (pre_copy_move_add_file_callback),
+ data);
+
+ /* Any items in the debuting_files hash table that have
+ * "FALSE" as their value aren't really being copied
+ * or moved, so we can't wait for an add_file signal
+ * to come in for those.
+ */
+ g_hash_table_foreach_remove (debuting_files,
+ remove_not_really_moved_files,
+ &debuting_files_data->added_files);
+
+ if (g_hash_table_size (debuting_files) == 0) {
+ /* on the off-chance that all the icons have already been added */
+ if (debuting_files_data->added_files != NULL) {
+ fm_directory_view_set_selection (directory_view,
+ debuting_files_data->added_files);
+ fm_directory_view_reveal_selection (directory_view);
+ }
+ debuting_files_data_free (debuting_files_data);
+ } else {
+ /* We need to run after the default handler adds the folder we want to
+ * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
+ * must use connect_after.
+ */
+ g_signal_connect_data (GTK_OBJECT (directory_view),
+ "add_file",
+ G_CALLBACK (debuting_files_add_file_callback),
+ debuting_files_data,
+ (GClosureNotify) debuting_files_data_free,
+ G_CONNECT_AFTER);
+ }
+ }
+
+ copy_move_done_data_free (copy_move_done_data);
+}
+
+static gboolean
+real_file_still_belongs (FMDirectoryView *view, CajaFile *file, CajaDirectory *directory)
+{
+ if (view->details->model != directory &&
+ g_list_find (view->details->subdirectory_list, directory) == NULL) {
+ return FALSE;
+ }
+
+ return caja_directory_contains_file (directory, file);
+}
+
+static gboolean
+still_should_show_file (FMDirectoryView *view, CajaFile *file, CajaDirectory *directory)
+{
+ return fm_directory_view_should_show_file (view, file)
+ && EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, file_still_belongs, (view, file, directory));
+}
+
+static gboolean
+ready_to_load (CajaFile *file)
+{
+ return caja_file_check_if_ready (file,
+ CAJA_FILE_ATTRIBUTES_FOR_ICON);
+}
+
+static int
+compare_files_cover (gconstpointer a, gconstpointer b, gpointer callback_data)
+{
+ const FileAndDirectory *fad1, *fad2;
+ FMDirectoryView *view;
+
+ view = callback_data;
+ fad1 = a; fad2 = b;
+
+ if (fad1->directory < fad2->directory) {
+ return -1;
+ } else if (fad1->directory > fad2->directory) {
+ return 1;
+ } else {
+ return EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, compare_files,
+ (view, fad1->file, fad2->file));
+ }
+}
+static void
+sort_files (FMDirectoryView *view, GList **list)
+{
+ *list = g_list_sort_with_data (*list, compare_files_cover, view);
+
+}
+
+/* Go through all the new added and changed files.
+ * Put any that are not ready to load in the non_ready_files hash table.
+ * Add all the rest to the old_added_files and old_changed_files lists.
+ * Sort the old_*_files lists if anything was added to them.
+ */
+static void
+process_new_files (FMDirectoryView *view)
+{
+ GList *new_added_files, *new_changed_files, *old_added_files, *old_changed_files;
+ GHashTable *non_ready_files;
+ GList *node, *next;
+ FileAndDirectory *pending;
+ gboolean in_non_ready;
+
+ new_added_files = view->details->new_added_files;
+ view->details->new_added_files = NULL;
+ new_changed_files = view->details->new_changed_files;
+ view->details->new_changed_files = NULL;
+
+ non_ready_files = view->details->non_ready_files;
+
+ old_added_files = view->details->old_added_files;
+ old_changed_files = view->details->old_changed_files;
+
+ /* Newly added files go into the old_added_files list if they're
+ * ready, and into the hash table if they're not.
+ */
+ for (node = new_added_files; node != NULL; node = next) {
+ next = node->next;
+ pending = (FileAndDirectory *)node->data;
+ in_non_ready = g_hash_table_lookup (non_ready_files, pending) != NULL;
+ if (fm_directory_view_should_show_file (view, pending->file)) {
+ if (ready_to_load (pending->file)) {
+ if (in_non_ready) {
+ g_hash_table_remove (non_ready_files, pending);
+ }
+ new_added_files = g_list_delete_link (new_added_files, node);
+ old_added_files = g_list_prepend (old_added_files, pending);
+ } else {
+ if (!in_non_ready) {
+ new_added_files = g_list_delete_link (new_added_files, node);
+ g_hash_table_insert (non_ready_files, pending, pending);
+ }
+ }
+ }
+ }
+ file_and_directory_list_free (new_added_files);
+
+ /* Newly changed files go into the old_added_files list if they're ready
+ * and were seen non-ready in the past, into the old_changed_files list
+ * if they are read and were not seen non-ready in the past, and into
+ * the hash table if they're not ready.
+ */
+ for (node = new_changed_files; node != NULL; node = next) {
+ next = node->next;
+ pending = (FileAndDirectory *)node->data;
+ if (!still_should_show_file (view, pending->file, pending->directory) || ready_to_load (pending->file)) {
+ if (g_hash_table_lookup (non_ready_files, pending) != NULL) {
+ g_hash_table_remove (non_ready_files, pending);
+ if (still_should_show_file (view, pending->file, pending->directory)) {
+ new_changed_files = g_list_delete_link (new_changed_files, node);
+ old_added_files = g_list_prepend (old_added_files, pending);
+ }
+ } else if (fm_directory_view_should_show_file (view, pending->file)) {
+ new_changed_files = g_list_delete_link (new_changed_files, node);
+ old_changed_files = g_list_prepend (old_changed_files, pending);
+ }
+ }
+ }
+ file_and_directory_list_free (new_changed_files);
+
+ /* If any files were added to old_added_files, then resort it. */
+ if (old_added_files != view->details->old_added_files) {
+ view->details->old_added_files = old_added_files;
+ sort_files (view, &view->details->old_added_files);
+ }
+
+ /* Resort old_changed_files too, since file attributes
+ * relevant to sorting could have changed.
+ */
+ if (old_changed_files != view->details->old_changed_files) {
+ view->details->old_changed_files = old_changed_files;
+ sort_files (view, &view->details->old_changed_files);
+ }
+
+}
+
+static void
+process_old_files (FMDirectoryView *view)
+{
+ GList *files_added, *files_changed, *node;
+ FileAndDirectory *pending;
+ GList *selection, *files;
+ gboolean send_selection_change;
+
+ files_added = view->details->old_added_files;
+ files_changed = view->details->old_changed_files;
+
+ send_selection_change = FALSE;
+
+ if (files_added != NULL || files_changed != NULL) {
+ g_signal_emit (view, signals[BEGIN_FILE_CHANGES], 0);
+
+ for (node = files_added; node != NULL; node = node->next) {
+ pending = node->data;
+ g_signal_emit (view,
+ signals[ADD_FILE], 0, pending->file, pending->directory);
+ }
+
+ for (node = files_changed; node != NULL; node = node->next) {
+ pending = node->data;
+ g_signal_emit (view,
+ signals[still_should_show_file (view, pending->file, pending->directory)
+ ? FILE_CHANGED : REMOVE_FILE], 0,
+ pending->file, pending->directory);
+ }
+
+ g_signal_emit (view, signals[END_FILE_CHANGES], 0);
+
+ if (files_changed != NULL) {
+ selection = fm_directory_view_get_selection (view);
+ files = file_and_directory_list_to_files (files_changed);
+ send_selection_change = eel_g_lists_sort_and_check_for_intersection
+ (&files, &selection);
+ caja_file_list_free (files);
+ caja_file_list_free (selection);
+ }
+
+ file_and_directory_list_free (view->details->old_added_files);
+ view->details->old_added_files = NULL;
+
+ file_and_directory_list_free (view->details->old_changed_files);
+ view->details->old_changed_files = NULL;
+ }
+
+ if (send_selection_change) {
+ /* Send a selection change since some file names could
+ * have changed.
+ */
+ fm_directory_view_send_selection_change (view);
+ }
+}
+
+static void
+display_pending_files (FMDirectoryView *view)
+{
+
+ /* Don't dispatch any updates while the view is frozen. */
+ if (view->details->updates_frozen) {
+ return;
+ }
+
+ process_new_files (view);
+ process_old_files (view);
+
+ if (view->details->model != NULL
+ && caja_directory_are_all_files_seen (view->details->model)
+ && g_hash_table_size (view->details->non_ready_files) == 0) {
+ done_loading (view, TRUE);
+ }
+}
+
+void
+fm_directory_view_freeze_updates (FMDirectoryView *view)
+{
+ view->details->updates_frozen = TRUE;
+ view->details->updates_queued = 0;
+ view->details->needs_reload = FALSE;
+}
+
+void
+fm_directory_view_unfreeze_updates (FMDirectoryView *view)
+{
+ view->details->updates_frozen = FALSE;
+
+ if (view->details->needs_reload) {
+ view->details->needs_reload = FALSE;
+ if (view->details->model != NULL) {
+ load_directory (view, view->details->model);
+ }
+ } else {
+ schedule_idle_display_of_pending_files (view);
+ }
+}
+
+static gboolean
+display_selection_info_idle_callback (gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ g_object_ref (G_OBJECT (view));
+
+ view->details->display_selection_idle_id = 0;
+ fm_directory_view_display_selection_info (view);
+ if (view->details->send_selection_change_to_shell) {
+ fm_directory_view_send_selection_change (view);
+ }
+
+ g_object_unref (G_OBJECT (view));
+
+ return FALSE;
+}
+
+static void
+remove_update_menus_timeout_callback (FMDirectoryView *view)
+{
+ if (view->details->update_menus_timeout_id != 0) {
+ g_source_remove (view->details->update_menus_timeout_id);
+ view->details->update_menus_timeout_id = 0;
+ }
+}
+
+static void
+update_menus_if_pending (FMDirectoryView *view)
+{
+ if (!view->details->menu_states_untrustworthy) {
+ return;
+ }
+
+ remove_update_menus_timeout_callback (view);
+ fm_directory_view_update_menus (view);
+}
+
+static gboolean
+update_menus_timeout_callback (gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ g_object_ref (G_OBJECT (view));
+
+ view->details->update_menus_timeout_id = 0;
+ fm_directory_view_update_menus (view);
+
+ g_object_unref (G_OBJECT (view));
+
+ return FALSE;
+}
+
+static gboolean
+display_pending_callback (gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ g_object_ref (G_OBJECT (view));
+
+ view->details->display_pending_source_id = 0;
+
+ display_pending_files (view);
+
+ g_object_unref (G_OBJECT (view));
+
+ return FALSE;
+}
+
+static void
+schedule_idle_display_of_pending_files (FMDirectoryView *view)
+{
+ /* Get rid of a pending source as it might be a timeout */
+ unschedule_display_of_pending_files (view);
+
+ /* We want higher priority than the idle that handles the relayout
+ to avoid a resort on each add. But we still want to allow repaints
+ and other hight prio events while we have pending files to show. */
+ view->details->display_pending_source_id =
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
+ display_pending_callback, view, NULL);
+}
+
+static void
+schedule_timeout_display_of_pending_files (FMDirectoryView *view, guint interval)
+{
+ /* No need to schedule an update if there's already one pending. */
+ if (view->details->display_pending_source_id != 0) {
+ return;
+ }
+
+ view->details->display_pending_source_id =
+ g_timeout_add (interval, display_pending_callback, view);
+}
+
+static void
+unschedule_display_of_pending_files (FMDirectoryView *view)
+{
+ /* Get rid of source if it's active. */
+ if (view->details->display_pending_source_id != 0) {
+ g_source_remove (view->details->display_pending_source_id);
+ view->details->display_pending_source_id = 0;
+ }
+}
+
+static void
+queue_pending_files (FMDirectoryView *view,
+ CajaDirectory *directory,
+ GList *files,
+ GList **pending_list)
+{
+ if (files == NULL) {
+ return;
+ }
+
+ /* Don't queue any more updates if we need to reload anyway */
+ if (view->details->needs_reload) {
+ return;
+ }
+
+ if (view->details->updates_frozen) {
+ view->details->updates_queued += g_list_length (files);
+ /* Mark the directory for reload when there are too much queued
+ * changes to prevent the pending list from growing infinitely.
+ */
+ if (view->details->updates_queued > MAX_QUEUED_UPDATES) {
+ view->details->needs_reload = TRUE;
+ return;
+ }
+ }
+
+
+
+ *pending_list = g_list_concat (file_and_directory_list_from_files (directory, files),
+ *pending_list);
+
+ if (! view->details->loading || caja_directory_are_all_files_seen (directory)) {
+ schedule_timeout_display_of_pending_files (view, view->details->update_interval);
+ }
+}
+
+static void
+remove_changes_timeout_callback (FMDirectoryView *view)
+{
+ if (view->details->changes_timeout_id != 0) {
+ g_source_remove (view->details->changes_timeout_id);
+ view->details->changes_timeout_id = 0;
+ }
+}
+
+static void
+reset_update_interval (FMDirectoryView *view)
+{
+ view->details->update_interval = UPDATE_INTERVAL_MIN;
+ remove_changes_timeout_callback (view);
+ /* Reschedule a pending timeout to idle */
+ if (view->details->display_pending_source_id != 0) {
+ schedule_idle_display_of_pending_files (view);
+ }
+}
+
+static gboolean
+changes_timeout_callback (gpointer data)
+{
+ gint64 now;
+ gint64 time_delta;
+ gboolean ret;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ g_object_ref (G_OBJECT (view));
+
+ now = eel_get_system_time();
+ time_delta = now - view->details->last_queued;
+
+ if (time_delta < UPDATE_INTERVAL_RESET*1000) {
+ if (view->details->update_interval < UPDATE_INTERVAL_MAX &&
+ view->details->loading) {
+ /* Increase */
+ view->details->update_interval += UPDATE_INTERVAL_INC;
+ }
+ ret = TRUE;
+ } else {
+ /* Reset */
+ reset_update_interval (view);
+ ret = FALSE;
+ }
+
+ g_object_unref (G_OBJECT (view));
+
+ return ret;
+}
+
+static void
+schedule_changes (FMDirectoryView *view)
+{
+ /* Remember when the change was queued */
+ view->details->last_queued = eel_get_system_time();
+
+ /* No need to schedule if there are already changes pending or during loading */
+ if (view->details->changes_timeout_id != 0 ||
+ view->details->loading) {
+ return;
+ }
+
+ view->details->changes_timeout_id =
+ g_timeout_add (UPDATE_INTERVAL_TIMEOUT_INTERVAL, changes_timeout_callback, view);
+}
+
+static void
+files_added_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GtkWindow *window;
+ char *uri;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ window = fm_directory_view_get_containing_window (view);
+ uri = fm_directory_view_get_uri (view);
+ caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_ASYNC, files,
+ "files added in window %p: %s",
+ window,
+ uri ? uri : "(no directory)");
+ g_free (uri);
+
+ schedule_changes (view);
+
+ queue_pending_files (view, directory, files, &view->details->new_added_files);
+
+ /* The number of items could have changed */
+ schedule_update_status (view);
+}
+
+static void
+files_changed_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GtkWindow *window;
+ char *uri;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ window = fm_directory_view_get_containing_window (view);
+ uri = fm_directory_view_get_uri (view);
+ caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_ASYNC, files,
+ "files changed in window %p: %s",
+ window,
+ uri ? uri : "(no directory)");
+ g_free (uri);
+
+ schedule_changes (view);
+
+ queue_pending_files (view, directory, files, &view->details->new_changed_files);
+
+ /* The free space or the number of items could have changed */
+ schedule_update_status (view);
+
+ /* A change in MIME type could affect the Open with menu, for
+ * one thing, so we need to update menus when files change.
+ */
+ schedule_update_menus (view);
+}
+
+static void
+done_loading_callback (CajaDirectory *directory,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ process_new_files (view);
+ if (g_hash_table_size (view->details->non_ready_files) == 0) {
+ /* Unschedule a pending update and schedule a new one with the minimal
+ * update interval. This gives the view a short chance at gathering the
+ * (cached) deep counts.
+ */
+ unschedule_display_of_pending_files (view);
+ schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
+ }
+}
+
+static void
+load_error_callback (CajaDirectory *directory,
+ GError *error,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ /* FIXME: By doing a stop, we discard some pending files. Is
+ * that OK?
+ */
+ fm_directory_view_stop (view);
+
+ /* Emit a signal to tell subclasses that a load error has
+ * occurred, so they can handle it in the UI.
+ */
+ g_signal_emit (view,
+ signals[LOAD_ERROR], 0, error);
+}
+
+static void
+real_load_error (FMDirectoryView *view, GError *error)
+{
+ /* Report only one error per failed directory load (from the UI
+ * point of view, not from the CajaDirectory point of view).
+ * Otherwise you can get multiple identical errors caused by
+ * unrelated code that just happens to try to iterate this
+ * directory.
+ */
+ if (!view->details->reported_load_error) {
+ fm_report_error_loading_directory
+ (fm_directory_view_get_directory_as_file (view),
+ error,
+ fm_directory_view_get_containing_window (view));
+ }
+ view->details->reported_load_error = TRUE;
+}
+
+void
+fm_directory_view_add_subdirectory (FMDirectoryView *view,
+ CajaDirectory*directory)
+{
+ CajaFileAttributes attributes;
+
+ g_assert (!g_list_find (view->details->subdirectory_list, directory));
+
+ caja_directory_ref (directory);
+
+ attributes =
+ CAJA_FILE_ATTRIBUTES_FOR_ICON |
+ CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_LINK_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_EXTENSION_INFO;
+
+ caja_directory_file_monitor_add (directory,
+ &view->details->model,
+ view->details->show_hidden_files,
+ view->details->show_backup_files,
+ attributes,
+ files_added_callback, view);
+
+ g_signal_connect
+ (directory, "files_added",
+ G_CALLBACK (files_added_callback), view);
+ g_signal_connect
+ (directory, "files_changed",
+ G_CALLBACK (files_changed_callback), view);
+
+ view->details->subdirectory_list = g_list_prepend (
+ view->details->subdirectory_list, directory);
+}
+
+void
+fm_directory_view_remove_subdirectory (FMDirectoryView *view,
+ CajaDirectory*directory)
+{
+ g_assert (g_list_find (view->details->subdirectory_list, directory));
+
+ view->details->subdirectory_list = g_list_remove (
+ view->details->subdirectory_list, directory);
+
+ g_signal_handlers_disconnect_by_func (directory,
+ G_CALLBACK (files_added_callback),
+ view);
+ g_signal_handlers_disconnect_by_func (directory,
+ G_CALLBACK (files_changed_callback),
+ view);
+
+ caja_directory_file_monitor_remove (directory, &view->details->model);
+
+ caja_directory_unref (directory);
+}
+
+/**
+ * fm_directory_view_clear:
+ *
+ * Emit the signal to clear the contents of the view. Subclasses must
+ * override the signal handler for this signal. This is normally called
+ * only by FMDirectoryView.
+ * @view: FMDirectoryView to empty.
+ *
+ **/
+void
+fm_directory_view_clear (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ g_signal_emit (view, signals[CLEAR], 0);
+}
+
+/**
+ * fm_directory_view_begin_loading:
+ *
+ * Emit the signal to prepare for loading the contents of a new location.
+ * Subclasses might want to override the signal handler for this signal.
+ * This is normally called only by FMDirectoryView.
+ * @view: FMDirectoryView that is switching to view a new location.
+ *
+ **/
+void
+fm_directory_view_begin_loading (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ g_signal_emit (view, signals[BEGIN_LOADING], 0);
+}
+
+/**
+ * fm_directory_view_end_loading:
+ *
+ * Emit the signal after loading the contents of a new location.
+ * Subclasses might want to override the signal handler for this signal.
+ * This is normally called only by FMDirectoryView.
+ * @view: FMDirectoryView that is switching to view a new location.
+ *
+ **/
+void
+fm_directory_view_end_loading (FMDirectoryView *view,
+ gboolean all_files_seen)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ g_signal_emit (view, signals[END_LOADING], 0, all_files_seen);
+}
+
+/**
+ * fm_directory_view_get_loading:
+ * @view: an #FMDirectoryView.
+ *
+ * Return value: #gboolean inicating whether @view is currently loaded.
+ *
+ **/
+gboolean
+fm_directory_view_get_loading (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return view->details->loading;
+}
+
+/**
+ * fm_directory_view_bump_zoom_level:
+ *
+ * bump the current zoom level by invoking the relevant subclass through the slot
+ *
+ **/
+void
+fm_directory_view_bump_zoom_level (FMDirectoryView *view, int zoom_increment)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return;
+ }
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ bump_zoom_level, (view, zoom_increment));
+}
+
+/**
+ * fm_directory_view_zoom_to_level:
+ *
+ * Set the current zoom level by invoking the relevant subclass through the slot
+ *
+ **/
+void
+fm_directory_view_zoom_to_level (FMDirectoryView *view,
+ CajaZoomLevel zoom_level)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return;
+ }
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ zoom_to_level, (view, zoom_level));
+}
+
+
+CajaZoomLevel
+fm_directory_view_get_zoom_level (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), CAJA_ZOOM_LEVEL_STANDARD);
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return CAJA_ZOOM_LEVEL_STANDARD;
+ }
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_zoom_level, (view));
+}
+
+/**
+ * fm_directory_view_restore_default_zoom_level:
+ *
+ * restore to the default zoom level by invoking the relevant subclass through the slot
+ *
+ **/
+void
+fm_directory_view_restore_default_zoom_level (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return;
+ }
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ restore_default_zoom_level, (view));
+}
+
+/**
+ * fm_directory_view_can_zoom_in:
+ *
+ * Determine whether the view can be zoomed any closer.
+ * @view: The zoomable FMDirectoryView.
+ *
+ * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
+ *
+ **/
+gboolean
+fm_directory_view_can_zoom_in (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return FALSE;
+ }
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ can_zoom_in, (view));
+}
+
+/**
+ * fm_directory_view_can_rename_file
+ *
+ * Determine whether a file can be renamed.
+ * @file: A CajaFile
+ *
+ * Return value: TRUE if @file can be renamed, FALSE otherwise.
+ *
+ **/
+static gboolean
+fm_directory_view_can_rename_file (FMDirectoryView *view, CajaFile *file)
+{
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ can_rename_file, (view, file));
+}
+
+/**
+ * fm_directory_view_can_zoom_out:
+ *
+ * Determine whether the view can be zoomed any further away.
+ * @view: The zoomable FMDirectoryView.
+ *
+ * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
+ *
+ **/
+gboolean
+fm_directory_view_can_zoom_out (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ if (!fm_directory_view_supports_zooming (view)) {
+ return FALSE;
+ }
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ can_zoom_out, (view));
+}
+
+GtkWidget *
+fm_directory_view_get_background_widget (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_background_widget, (view));
+}
+
+EelBackground *
+fm_directory_view_get_background (FMDirectoryView *view)
+{
+ return eel_get_widget_background (fm_directory_view_get_background_widget (view));
+}
+
+static void
+real_set_is_active (FMDirectoryView *view,
+ gboolean is_active)
+{
+ EelBackground *bg;
+
+ bg = fm_directory_view_get_background (view);
+ eel_background_set_active (bg, is_active);
+}
+
+static void
+fm_directory_view_set_is_active (FMDirectoryView *view,
+ gboolean is_active)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
+ set_is_active, (view, is_active));
+}
+
+/**
+ * fm_directory_view_get_selection:
+ *
+ * Get a list of CajaFile pointers that represents the
+ * currently-selected items in this view. Subclasses must override
+ * the signal handler for the 'get_selection' signal. Callers are
+ * responsible for g_free-ing the list (but not its data).
+ * @view: FMDirectoryView whose selected items are of interest.
+ *
+ * Return value: GList of CajaFile pointers representing the selection.
+ *
+ **/
+GList *
+fm_directory_view_get_selection (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_selection, (view));
+}
+
+void
+fm_directory_view_invert_selection (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ invert_selection, (view));
+}
+
+GList *
+fm_directory_view_get_selection_for_file_transfer (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_selection_for_file_transfer, (view));
+}
+
+guint
+fm_directory_view_get_item_count (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), 0);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_item_count, (view));
+}
+
+GtkUIManager *
+fm_directory_view_get_ui_manager (FMDirectoryView *view)
+{
+ if (view->details->window == NULL) {
+ return NULL;
+ }
+ return caja_window_info_get_ui_manager (view->details->window);
+}
+
+/**
+ * fm_directory_view_get_model:
+ *
+ * Get the model for this FMDirectoryView.
+ * @view: FMDirectoryView of interest.
+ *
+ * Return value: CajaDirectory for this view.
+ *
+ **/
+CajaDirectory *
+fm_directory_view_get_model (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return view->details->model;
+}
+
+GdkAtom
+fm_directory_view_get_copied_files_atom (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), GDK_NONE);
+
+ return copied_files_atom;
+}
+
+static void
+prepend_uri_one (gpointer data, gpointer callback_data)
+{
+ CajaFile *file;
+ GList **result;
+
+ g_assert (CAJA_IS_FILE (data));
+ g_assert (callback_data != NULL);
+
+ result = (GList **) callback_data;
+ file = (CajaFile *) data;
+ *result = g_list_prepend (*result, caja_file_get_uri (file));
+}
+
+static void
+offset_drop_points (GArray *relative_item_points,
+ int x_offset, int y_offset)
+{
+ guint index;
+
+ if (relative_item_points == NULL) {
+ return;
+ }
+
+ for (index = 0; index < relative_item_points->len; index++) {
+ g_array_index (relative_item_points, GdkPoint, index).x += x_offset;
+ g_array_index (relative_item_points, GdkPoint, index).y += y_offset;
+ }
+}
+
+static void
+fm_directory_view_create_links_for_files (FMDirectoryView *view, GList *files,
+ GArray *relative_item_points)
+{
+ GList *uris;
+ char *dir_uri;
+ CopyMoveDoneData *copy_move_done_data;
+ g_assert (relative_item_points->len == 0
+ || g_list_length (files) == relative_item_points->len);
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (files != NULL);
+
+ /* create a list of URIs */
+ uris = NULL;
+ g_list_foreach (files, prepend_uri_one, &uris);
+ uris = g_list_reverse (uris);
+
+ g_assert (g_list_length (uris) == g_list_length (files));
+
+ /* offset the drop locations a bit so that we don't pile
+ * up the icons on top of each other
+ */
+ offset_drop_points (relative_item_points,
+ DUPLICATE_HORIZONTAL_ICON_OFFSET,
+ DUPLICATE_VERTICAL_ICON_OFFSET);
+
+ copy_move_done_data = pre_copy_move (view);
+ dir_uri = fm_directory_view_get_backing_uri (view);
+ caja_file_operations_copy_move (uris, relative_item_points, dir_uri, GDK_ACTION_LINK,
+ GTK_WIDGET (view), copy_move_done_callback, copy_move_done_data);
+ g_free (dir_uri);
+ eel_g_list_free_deep (uris);
+}
+
+static void
+fm_directory_view_duplicate_selection (FMDirectoryView *view, GList *files,
+ GArray *relative_item_points)
+{
+ GList *uris;
+ CopyMoveDoneData *copy_move_done_data;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (files != NULL);
+ g_assert (g_list_length (files) == relative_item_points->len
+ || relative_item_points->len == 0);
+
+ /* create a list of URIs */
+ uris = NULL;
+ g_list_foreach (files, prepend_uri_one, &uris);
+ uris = g_list_reverse (uris);
+
+ g_assert (g_list_length (uris) == g_list_length (files));
+
+ /* offset the drop locations a bit so that we don't pile
+ * up the icons on top of each other
+ */
+ offset_drop_points (relative_item_points,
+ DUPLICATE_HORIZONTAL_ICON_OFFSET,
+ DUPLICATE_VERTICAL_ICON_OFFSET);
+
+ copy_move_done_data = pre_copy_move (view);
+ caja_file_operations_copy_move (uris, relative_item_points, NULL, GDK_ACTION_COPY,
+ GTK_WIDGET (view), copy_move_done_callback, copy_move_done_data);
+ eel_g_list_free_deep (uris);
+}
+
+/* special_link_in_selection
+ *
+ * Return TRUE if one of our special links is in the selection.
+ * Special links include the following:
+ * CAJA_DESKTOP_LINK_TRASH, CAJA_DESKTOP_LINK_HOME, CAJA_DESKTOP_LINK_MOUNT
+ */
+
+static gboolean
+special_link_in_selection (FMDirectoryView *view)
+{
+ gboolean saw_link;
+ GList *selection, *node;
+ CajaFile *file;
+
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ saw_link = FALSE;
+
+ selection = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
+
+ for (node = selection; node != NULL; node = node->next) {
+ file = CAJA_FILE (node->data);
+
+ saw_link = CAJA_IS_DESKTOP_ICON_FILE (file);
+
+ if (saw_link) {
+ break;
+ }
+ }
+
+ caja_file_list_free (selection);
+
+ return saw_link;
+}
+
+/* desktop_or_home_dir_in_selection
+ *
+ * Return TRUE if either the desktop or the home directory is in the selection.
+ */
+
+static gboolean
+desktop_or_home_dir_in_selection (FMDirectoryView *view)
+{
+ gboolean saw_desktop_or_home_dir;
+ GList *selection, *node;
+ CajaFile *file;
+
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ saw_desktop_or_home_dir = FALSE;
+
+ selection = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
+
+ for (node = selection; node != NULL; node = node->next) {
+ file = CAJA_FILE (node->data);
+
+ saw_desktop_or_home_dir =
+ caja_file_is_home (file)
+ || caja_file_is_desktop_directory (file);
+
+ if (saw_desktop_or_home_dir) {
+ break;
+ }
+ }
+
+ caja_file_list_free (selection);
+
+ return saw_desktop_or_home_dir;
+}
+
+static void
+trash_or_delete_done_cb (GHashTable *debuting_uris,
+ gboolean user_cancel,
+ FMDirectoryView *view)
+{
+ if (user_cancel) {
+ view->details->selection_was_removed = FALSE;
+ }
+}
+
+static void
+trash_or_delete_files (GtkWindow *parent_window,
+ const GList *files,
+ gboolean delete_if_all_already_in_trash,
+ FMDirectoryView *view)
+{
+ GList *locations;
+ const GList *node;
+
+ locations = NULL;
+ for (node = files; node != NULL; node = node->next) {
+ locations = g_list_prepend (locations,
+ caja_file_get_location ((CajaFile *) node->data));
+ }
+
+ locations = g_list_reverse (locations);
+
+ caja_file_operations_trash_or_delete (locations,
+ parent_window,
+ (CajaDeleteCallback) trash_or_delete_done_cb,
+ view);
+ eel_g_object_list_free (locations);
+}
+
+static gboolean
+can_rename_file (FMDirectoryView *view, CajaFile *file)
+{
+ return caja_file_can_rename (file);
+}
+
+static void
+start_renaming_file (FMDirectoryView *view,
+ CajaFile *file,
+ gboolean select_all)
+{
+ if (file != NULL) {
+ fm_directory_view_select_file (view, file);
+ }
+}
+
+typedef struct {
+ FMDirectoryView *view;
+ CajaFile *new_file;
+} RenameData;
+
+static gboolean
+delayed_rename_file_hack_callback (RenameData *data)
+{
+ FMDirectoryView *view;
+ CajaFile *new_file;
+
+ view = data->view;
+ new_file = data->new_file;
+
+ if (view->details->window != NULL &&
+ view->details->active) {
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file, FALSE));
+ fm_directory_view_reveal_selection (view);
+ }
+
+ return FALSE;
+}
+
+static void
+delayed_rename_file_hack_removed (RenameData *data)
+{
+ g_object_unref (data->view);
+ caja_file_unref (data->new_file);
+ g_free (data);
+}
+
+
+static void
+rename_file (FMDirectoryView *view, CajaFile *new_file)
+{
+ RenameData *data;
+
+ /* HACK!!!!
+ This is a work around bug in listview. After the rename is
+ enabled we will get file changes due to info about the new
+ file being read, which will cause the model to change. When
+ the model changes GtkTreeView clears the editing. This hack just
+ delays editing for some time to try to avoid this problem.
+ A major problem is that the selection of the row causes us
+ to load the slow mimetype for the file, which leads to a
+ file_changed. So, before we delay we select the row.
+ */
+ if (FM_IS_LIST_VIEW (view)) {
+ fm_directory_view_select_file (view, new_file);
+
+ data = g_new (RenameData, 1);
+ data->view = g_object_ref (view);
+ data->new_file = caja_file_ref (new_file);
+ if (view->details->delayed_rename_file_id != 0) {
+ g_source_remove (view->details->delayed_rename_file_id);
+ }
+ view->details->delayed_rename_file_id =
+ g_timeout_add_full (G_PRIORITY_DEFAULT,
+ 100, (GSourceFunc)delayed_rename_file_hack_callback,
+ data, (GDestroyNotify) delayed_rename_file_hack_removed);
+
+ return;
+ }
+
+ /* no need to select because start_renaming_file selects
+ * fm_directory_view_select_file (view, new_file);
+ */
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file, FALSE));
+ fm_directory_view_reveal_selection (view);
+}
+
+static void
+reveal_newly_added_folder (FMDirectoryView *view, CajaFile *new_file,
+ CajaDirectory *directory, GFile *target_location)
+{
+ GFile *location;
+
+ location = caja_file_get_location (new_file);
+ if (g_file_equal (location, target_location)) {
+ g_signal_handlers_disconnect_by_func (view,
+ G_CALLBACK (reveal_newly_added_folder),
+ (void *) target_location);
+ rename_file (view, new_file);
+ }
+ g_object_unref (location);
+}
+
+typedef struct {
+ FMDirectoryView *directory_view;
+ GHashTable *added_locations;
+} NewFolderData;
+
+
+static void
+track_newly_added_locations (FMDirectoryView *view, CajaFile *new_file,
+ CajaDirectory *directory, gpointer user_data)
+{
+ NewFolderData *data;
+
+ data = user_data;
+
+ g_hash_table_insert (data->added_locations, caja_file_get_location (new_file), NULL);
+}
+
+static void
+new_folder_done (GFile *new_folder, gpointer user_data)
+{
+ FMDirectoryView *directory_view;
+ CajaFile *file;
+ char screen_string[32];
+ GdkScreen *screen;
+ NewFolderData *data;
+
+ data = (NewFolderData *)user_data;
+
+ directory_view = data->directory_view;
+
+ if (directory_view == NULL) {
+ goto fail;
+ }
+
+ g_signal_handlers_disconnect_by_func (directory_view,
+ G_CALLBACK (track_newly_added_locations),
+ (void *) data);
+
+ if (new_folder == NULL) {
+ goto fail;
+ }
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (directory_view));
+ g_snprintf (screen_string, sizeof (screen_string), "%d", gdk_screen_get_number (screen));
+
+
+ file = caja_file_get (new_folder);
+ caja_file_set_metadata
+ (file, CAJA_METADATA_KEY_SCREEN,
+ NULL,
+ screen_string);
+
+ if (g_hash_table_lookup_extended (data->added_locations, new_folder, NULL, NULL)) {
+ /* The file was already added */
+ rename_file (directory_view, file);
+ } else {
+ /* We need to run after the default handler adds the folder we want to
+ * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
+ * must use connect_after.
+ */
+ g_signal_connect_data (directory_view,
+ "add_file",
+ G_CALLBACK (reveal_newly_added_folder),
+ g_object_ref (new_folder),
+ (GClosureNotify)g_object_unref,
+ G_CONNECT_AFTER);
+ }
+ caja_file_unref (file);
+
+ fail:
+ g_hash_table_destroy (data->added_locations);
+ eel_remove_weak_pointer (&data->directory_view);
+ g_free (data);
+}
+
+
+static NewFolderData *
+new_folder_data_new (FMDirectoryView *directory_view)
+{
+ NewFolderData *data;
+
+ data = g_new (NewFolderData, 1);
+ data->directory_view = directory_view;
+ data->added_locations = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
+ g_object_unref, NULL);
+ eel_add_weak_pointer (&data->directory_view);
+
+ return data;
+}
+
+static GdkPoint *
+context_menu_to_file_operation_position (FMDirectoryView *directory_view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (directory_view), NULL);
+
+ if (fm_directory_view_using_manual_layout (directory_view)
+ && directory_view->details->context_menu_position.x >= 0
+ && directory_view->details->context_menu_position.y >= 0) {
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, directory_view,
+ widget_to_file_operation_position,
+ (directory_view, &directory_view->details->context_menu_position));
+ return &directory_view->details->context_menu_position;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+update_context_menu_position_from_event (FMDirectoryView *view,
+ GdkEventButton *event)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (event != NULL) {
+ view->details->context_menu_position.x = event->x;
+ view->details->context_menu_position.y = event->y;
+ } else {
+ view->details->context_menu_position.x = -1;
+ view->details->context_menu_position.y = -1;
+ }
+}
+
+void
+fm_directory_view_new_folder (FMDirectoryView *directory_view)
+{
+ char *parent_uri;
+ NewFolderData *data;
+ GdkPoint *pos;
+
+ data = new_folder_data_new (directory_view);
+
+ g_signal_connect_data (directory_view,
+ "add_file",
+ G_CALLBACK (track_newly_added_locations),
+ data,
+ (GClosureNotify)NULL,
+ G_CONNECT_AFTER);
+
+ pos = context_menu_to_file_operation_position (directory_view);
+
+ parent_uri = fm_directory_view_get_backing_uri (directory_view);
+ caja_file_operations_new_folder (GTK_WIDGET (directory_view),
+ pos, parent_uri,
+ new_folder_done, data);
+
+ g_free (parent_uri);
+}
+
+static NewFolderData *
+setup_new_folder_data (FMDirectoryView *directory_view)
+{
+ NewFolderData *data;
+
+ data = new_folder_data_new (directory_view);
+
+ g_signal_connect_data (directory_view,
+ "add_file",
+ G_CALLBACK (track_newly_added_locations),
+ data,
+ (GClosureNotify)NULL,
+ G_CONNECT_AFTER);
+
+ return data;
+}
+
+static void
+fm_directory_view_new_file_with_initial_contents (FMDirectoryView *directory_view,
+ const char *parent_uri,
+ const char *filename,
+ const char *initial_contents,
+ int length,
+ GdkPoint *pos)
+{
+ NewFolderData *data;
+
+ g_assert (parent_uri != NULL);
+
+ data = setup_new_folder_data (directory_view);
+
+ if (pos == NULL) {
+ pos = context_menu_to_file_operation_position (directory_view);
+ }
+
+ caja_file_operations_new_file (GTK_WIDGET (directory_view),
+ pos, parent_uri, filename,
+ initial_contents, length,
+ new_folder_done, data);
+}
+
+void
+fm_directory_view_new_file (FMDirectoryView *directory_view,
+ const char *parent_uri,
+ CajaFile *source)
+{
+ GdkPoint *pos;
+ NewFolderData *data;
+ char *source_uri;
+ char *container_uri;
+
+ container_uri = NULL;
+ if (parent_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (directory_view);
+ g_assert (container_uri != NULL);
+ }
+
+ if (source == NULL) {
+ fm_directory_view_new_file_with_initial_contents (directory_view,
+ parent_uri != NULL ? parent_uri : container_uri,
+ NULL,
+ NULL,
+ 0,
+ NULL);
+ g_free (container_uri);
+ return;
+ }
+
+ g_return_if_fail (caja_file_is_local (source));
+
+ pos = context_menu_to_file_operation_position (directory_view);
+
+ data = setup_new_folder_data (directory_view);
+
+ source_uri = caja_file_get_uri (source);
+
+ caja_file_operations_new_file_from_template (GTK_WIDGET (directory_view),
+ pos,
+ parent_uri != NULL ? parent_uri : container_uri,
+ NULL,
+ source_uri,
+ new_folder_done, data);
+
+ g_free (source_uri);
+ g_free (container_uri);
+}
+
+/* handle the open command */
+
+static void
+open_one_in_new_window (gpointer data, gpointer callback_data)
+{
+ g_assert (CAJA_IS_FILE (data));
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_activate_file (FM_DIRECTORY_VIEW (callback_data),
+ CAJA_FILE (data),
+ CAJA_WINDOW_OPEN_IN_NAVIGATION,
+ 0);
+}
+
+static void
+open_one_in_folder_window (gpointer data, gpointer callback_data)
+{
+ g_assert (CAJA_IS_FILE (data));
+ g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
+
+ fm_directory_view_activate_file (FM_DIRECTORY_VIEW (callback_data),
+ CAJA_FILE (data),
+ CAJA_WINDOW_OPEN_IN_SPATIAL,
+ 0);
+}
+
+CajaFile *
+fm_directory_view_get_directory_as_file (FMDirectoryView *view)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ return view->details->directory_as_file;
+}
+
+static void
+open_with_launch_application_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ ApplicationLaunchParameters *launch_parameters;
+
+ launch_parameters = (ApplicationLaunchParameters *) callback_data;
+ caja_launch_application
+ (launch_parameters->application,
+ launch_parameters->files,
+ fm_directory_view_get_containing_window (launch_parameters->directory_view));
+}
+
+static char *
+escape_action_name (const char *action_name,
+ const char *prefix)
+{
+ GString *s;
+
+ if (action_name == NULL) {
+ return NULL;
+ }
+
+ s = g_string_new (prefix);
+
+ while (*action_name != 0) {
+ switch (*action_name) {
+ case '\\':
+ g_string_append (s, "\\\\");
+ break;
+ case '/':
+ g_string_append (s, "\\s");
+ break;
+ case '&':
+ g_string_append (s, "\\a");
+ break;
+ case '"':
+ g_string_append (s, "\\q");
+ break;
+ default:
+ g_string_append_c (s, *action_name);
+ }
+
+ action_name ++;
+ }
+ return g_string_free (s, FALSE);
+}
+
+static char *
+escape_action_path (const char *action_path)
+{
+ GString *s;
+
+ if (action_path == NULL) {
+ return NULL;
+ }
+
+ s = g_string_sized_new (strlen (action_path) + 2);
+
+ while (*action_path != 0) {
+ switch (*action_path) {
+ case '\\':
+ g_string_append (s, "\\\\");
+ break;
+ case '&':
+ g_string_append (s, "\\a");
+ break;
+ case '"':
+ g_string_append (s, "\\q");
+ break;
+ default:
+ g_string_append_c (s, *action_path);
+ }
+
+ action_path ++;
+ }
+ return g_string_free (s, FALSE);
+}
+
+
+static void
+add_submenu (GtkUIManager *ui_manager,
+ GtkActionGroup *action_group,
+ guint merge_id,
+ const char *parent_path,
+ const char *uri,
+ const char *label,
+ GdkPixbuf *pixbuf,
+ gboolean add_action)
+{
+ char *escaped_label;
+ char *action_name;
+ char *submenu_name;
+ char *escaped_submenu_name;
+ GtkAction *action;
+
+ if (parent_path != NULL) {
+ action_name = escape_action_name (uri, "submenu_");
+ submenu_name = g_path_get_basename (uri);
+ escaped_submenu_name = escape_action_path (submenu_name);
+ escaped_label = eel_str_double_underscores (label);
+
+ if (add_action) {
+ action = gtk_action_new (action_name,
+ escaped_label,
+ NULL,
+ NULL);
+ if (pixbuf != NULL) {
+ g_object_set_data_full (G_OBJECT (action), "menu-icon",
+ g_object_ref (pixbuf),
+ g_object_unref);
+ }
+
+ g_object_set (action, "hide-if-empty", FALSE, NULL);
+
+ gtk_action_group_add_action (action_group,
+ action);
+ g_object_unref (action);
+ }
+
+ gtk_ui_manager_add_ui (ui_manager,
+ merge_id,
+ parent_path,
+ escaped_submenu_name,
+ action_name,
+ GTK_UI_MANAGER_MENU,
+ FALSE);
+ g_free (action_name);
+ g_free (escaped_label);
+ g_free (submenu_name);
+ g_free (escaped_submenu_name);
+ }
+}
+
+static void
+add_application_to_open_with_menu (FMDirectoryView *view,
+ GAppInfo *application,
+ GList *files,
+ int index,
+ const char *menu_placeholder,
+ const char *popup_placeholder,
+ const gboolean submenu)
+{
+ ApplicationLaunchParameters *launch_parameters;
+ char *tip;
+ char *label;
+ char *action_name;
+ char *escaped_app;
+ char *path;
+ GtkAction *action;
+ GIcon *app_icon;
+ GtkWidget *menuitem;
+
+ launch_parameters = application_launch_parameters_new
+ (application, files, view);
+ escaped_app = eel_str_double_underscores (g_app_info_get_display_name (application));
+ if (submenu)
+ label = g_strdup_printf ("%s", escaped_app);
+ else
+ label = g_strdup_printf (_("Open With %s"), escaped_app);
+
+ tip = g_strdup_printf (ngettext ("Use \"%s\" to open the selected item",
+ "Use \"%s\" to open the selected items",
+ g_list_length (files)),
+ escaped_app);
+ g_free (escaped_app);
+
+ action_name = g_strdup_printf ("open_with_%d", index);
+
+ action = gtk_action_new (action_name,
+ label,
+ tip,
+ NULL);
+
+ app_icon = g_app_info_get_icon (application);
+ if (app_icon != NULL) {
+ g_object_ref (app_icon);
+ } else {
+ app_icon = g_themed_icon_new ("application-x-executable");
+ }
+
+ gtk_action_set_gicon (action, app_icon);
+ g_object_unref (app_icon);
+
+ g_signal_connect_data (action, "activate",
+ G_CALLBACK (open_with_launch_application_callback),
+ launch_parameters,
+ (GClosureNotify)application_launch_parameters_free, 0);
+
+ gtk_action_group_add_action (view->details->open_with_action_group,
+ action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
+ view->details->open_with_merge_id,
+ menu_placeholder,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ path = g_strdup_printf ("%s/%s", menu_placeholder, action_name);
+ menuitem = gtk_ui_manager_get_widget (
+ caja_window_info_get_ui_manager (view->details->window),
+ path);
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+ g_free (path);
+
+ gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
+ view->details->open_with_merge_id,
+ popup_placeholder,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ path = g_strdup_printf ("%s/%s", popup_placeholder, action_name);
+ menuitem = gtk_ui_manager_get_widget (
+ caja_window_info_get_ui_manager (view->details->window),
+ path);
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ g_free (path);
+ g_free (action_name);
+ g_free (label);
+ g_free (tip);
+}
+
+static void
+get_x_content_async_callback (char **content,
+ gpointer user_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (user_data);
+
+ if (view->details->window != NULL) {
+ schedule_update_menus (view);
+ }
+ g_object_unref (view);
+}
+
+static void
+add_x_content_apps (FMDirectoryView *view, CajaFile *file, GList **applications)
+{
+ GMount *mount;
+ char **x_content_types;
+ unsigned int n;
+
+ g_return_if_fail (applications != NULL);
+
+ mount = caja_file_get_mount (file);
+
+ if (mount == NULL) {
+ return;
+ }
+
+ x_content_types = caja_autorun_get_cached_x_content_types_for_mount (mount);
+ if (x_content_types != NULL) {
+ for (n = 0; x_content_types[n] != NULL; n++) {
+ char *x_content_type = x_content_types[n];
+ GList *app_info_for_x_content_type;
+
+ app_info_for_x_content_type = g_app_info_get_all_for_type (x_content_type);
+ *applications = g_list_concat (*applications, app_info_for_x_content_type);
+ }
+ g_strfreev (x_content_types);
+ } else {
+ caja_autorun_get_x_content_types_for_mount_async (mount,
+ get_x_content_async_callback,
+ NULL,
+ g_object_ref (view));
+
+ }
+
+ g_object_unref (mount);
+}
+
+static void
+reset_open_with_menu (FMDirectoryView *view, GList *selection)
+{
+ GList *applications, *node;
+ CajaFile *file;
+ gboolean submenu_visible, filter_default;
+ int num_applications;
+ int index;
+ gboolean other_applications_visible;
+ gboolean open_with_chooser_visible;
+ GtkUIManager *ui_manager;
+ GtkAction *action;
+ GAppInfo *default_app;
+
+ /* Clear any previous inserted items in the applications and viewers placeholders */
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->open_with_merge_id,
+ &view->details->open_with_action_group);
+
+ caja_ui_prepare_merge_ui (ui_manager,
+ "OpenWithGroup",
+ &view->details->open_with_merge_id,
+ &view->details->open_with_action_group);
+
+ num_applications = 0;
+
+ other_applications_visible = (selection != NULL);
+ filter_default = (selection != NULL);
+
+ for (node = selection; node != NULL; node = node->next) {
+
+ file = CAJA_FILE (node->data);
+
+ other_applications_visible &=
+ (!caja_mime_file_opens_in_view (file) ||
+ caja_file_is_directory (file));
+ }
+
+ default_app = NULL;
+ if (filter_default) {
+ default_app = caja_mime_get_default_application_for_files (selection);
+ }
+
+ applications = NULL;
+ if (other_applications_visible) {
+ applications = caja_mime_get_applications_for_files (selection);
+ }
+
+ if (g_list_length (selection) == 1) {
+ add_x_content_apps (view, CAJA_FILE (selection->data), &applications);
+ }
+
+
+ num_applications = g_list_length (applications);
+
+ if (file_list_all_are_folders (selection)) {
+ submenu_visible = (num_applications > 2);
+ } else {
+ submenu_visible = (num_applications > 3);
+ }
+
+ for (node = applications, index = 0; node != NULL; node = node->next, index++) {
+ GAppInfo *application;
+ char *menu_path;
+ char *popup_path;
+
+ application = node->data;
+
+ if (default_app != NULL && g_app_info_equal (default_app, application)) {
+ continue;
+ }
+
+ if (submenu_visible) {
+ menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
+ popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
+ } else {
+ menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER;
+ popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER;
+ }
+
+ gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
+ view->details->open_with_merge_id,
+ menu_path,
+ "separator",
+ NULL,
+ GTK_UI_MANAGER_SEPARATOR,
+ FALSE);
+
+ add_application_to_open_with_menu (view,
+ node->data,
+ selection,
+ index,
+ menu_path, popup_path, submenu_visible);
+ }
+ eel_g_object_list_free (applications);
+ if (default_app != NULL) {
+ g_object_unref (default_app);
+ }
+
+ open_with_chooser_visible = other_applications_visible &&
+ g_list_length (selection) == 1;
+
+ if (submenu_visible) {
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OTHER_APPLICATION1);
+ gtk_action_set_visible (action, open_with_chooser_visible);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OTHER_APPLICATION2);
+ gtk_action_set_visible (action, FALSE);
+ } else {
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OTHER_APPLICATION1);
+ gtk_action_set_visible (action, FALSE);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OTHER_APPLICATION2);
+ gtk_action_set_visible (action, open_with_chooser_visible);
+ }
+}
+
+static GList *
+get_all_extension_menu_items (GtkWidget *window,
+ GList *selection)
+{
+ GList *items;
+ GList *providers;
+ GList *l;
+
+ providers = caja_module_get_extensions_for_type (CAJA_TYPE_MENU_PROVIDER);
+ items = NULL;
+
+ for (l = providers; l != NULL; l = l->next) {
+ CajaMenuProvider *provider;
+ GList *file_items;
+
+ provider = CAJA_MENU_PROVIDER (l->data);
+ file_items = caja_menu_provider_get_file_items (provider,
+ window,
+ selection);
+ items = g_list_concat (items, file_items);
+ }
+
+ caja_module_extension_list_free (providers);
+
+ return items;
+}
+
+typedef struct
+{
+ CajaMenuItem *item;
+ FMDirectoryView *view;
+ GList *selection;
+ GtkAction *action;
+} ExtensionActionCallbackData;
+
+
+static void
+extension_action_callback_data_free (ExtensionActionCallbackData *data)
+{
+ g_object_unref (data->item);
+ caja_file_list_free (data->selection);
+
+ g_free (data);
+}
+
+static gboolean
+search_in_menu_items (GList* items, const char *item_name)
+{
+ GList* list;
+
+ for (list = items; list != NULL; list = list->next) {
+ CajaMenu* menu;
+ char *name;
+
+ g_object_get (list->data, "name", &name, NULL);
+ if (strcmp (name, item_name) == 0) {
+ g_free (name);
+ return TRUE;
+ }
+ g_free (name);
+
+ menu = NULL;
+ g_object_get (list->data, "menu", &menu, NULL);
+ if (menu != NULL) {
+ gboolean ret;
+ GList* submenus;
+
+ submenus = caja_menu_get_items (menu);
+ ret = search_in_menu_items (submenus, item_name);
+ caja_menu_item_list_free (submenus);
+ g_object_unref (menu);
+ if (ret) {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static void
+extension_action_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ ExtensionActionCallbackData *data;
+ char *item_name;
+ gboolean is_valid;
+ GList *l;
+ GList *items;
+
+ data = callback_data;
+
+ /* Make sure the selected menu item is valid for the final sniffed
+ * mime type */
+ g_object_get (data->item, "name", &item_name, NULL);
+ items = get_all_extension_menu_items (gtk_widget_get_toplevel (GTK_WIDGET (data->view)),
+ data->selection);
+
+ is_valid = search_in_menu_items (items, item_name);
+
+ for (l = items; l != NULL; l = l->next) {
+ g_object_unref (l->data);
+ }
+ g_list_free (items);
+
+ g_free (item_name);
+
+ if (is_valid) {
+ caja_menu_item_activate (data->item);
+ }
+}
+
+static GdkPixbuf *
+get_menu_icon (const char *icon_name)
+{
+ CajaIconInfo *info;
+ GdkPixbuf *pixbuf;
+ int size;
+
+ size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
+
+ if (g_path_is_absolute (icon_name)) {
+ info = caja_icon_info_lookup_from_path (icon_name, size);
+ } else {
+ info = caja_icon_info_lookup_from_name (icon_name, size);
+ }
+ pixbuf = caja_icon_info_get_pixbuf_nodefault_at_size (info, size);
+ g_object_unref (info);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+get_menu_icon_for_file (CajaFile *file)
+{
+ CajaIconInfo *info;
+ GdkPixbuf *pixbuf;
+ int size;
+
+ size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
+
+ info = caja_file_get_icon (file, size, 0);
+ pixbuf = caja_icon_info_get_pixbuf_nodefault_at_size (info, size);
+ g_object_unref (info);
+
+ return pixbuf;
+}
+
+static GtkAction *
+add_extension_action_for_files (FMDirectoryView *view,
+ CajaMenuItem *item,
+ GList *files)
+{
+ char *name, *label, *tip, *icon;
+ gboolean sensitive, priority;
+ GtkAction *action;
+ GdkPixbuf *pixbuf;
+ ExtensionActionCallbackData *data;
+
+ g_object_get (G_OBJECT (item),
+ "name", &name, "label", &label,
+ "tip", &tip, "icon", &icon,
+ "sensitive", &sensitive,
+ "priority", &priority,
+ NULL);
+
+ action = gtk_action_new (name,
+ label,
+ tip,
+ icon);
+
+ if (icon != NULL) {
+ pixbuf = get_menu_icon (icon);
+ if (pixbuf != NULL) {
+ g_object_set_data_full (G_OBJECT (action), "menu-icon",
+ pixbuf,
+ g_object_unref);
+ }
+ }
+
+ gtk_action_set_sensitive (action, sensitive);
+ g_object_set (action, "is-important", priority, NULL);
+
+ data = g_new0 (ExtensionActionCallbackData, 1);
+ data->item = g_object_ref (item);
+ data->view = view;
+ data->selection = caja_file_list_copy (files);
+ data->action = action;
+
+ g_signal_connect_data (action, "activate",
+ G_CALLBACK (extension_action_callback),
+ data,
+ (GClosureNotify)extension_action_callback_data_free, 0);
+
+ gtk_action_group_add_action (view->details->extensions_menu_action_group,
+ GTK_ACTION (action));
+ g_object_unref (action);
+
+ g_free (name);
+ g_free (label);
+ g_free (tip);
+ g_free (icon);
+
+ return action;
+}
+
+static void
+add_extension_menu_items (FMDirectoryView *view,
+ GList *files,
+ GList *menu_items,
+ const char *subdirectory)
+{
+ GtkUIManager *ui_manager;
+ GList *l;
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+
+ for (l = menu_items; l; l = l->next) {
+ CajaMenuItem *item;
+ CajaMenu *menu;
+ GtkAction *action;
+ char *path;
+
+ item = CAJA_MENU_ITEM (l->data);
+
+ g_object_get (item, "menu", &menu, NULL);
+
+ action = add_extension_action_for_files (view, item, files);
+
+ path = g_build_path ("/", FM_DIRECTORY_VIEW_POPUP_PATH_EXTENSION_ACTIONS, subdirectory, NULL);
+ gtk_ui_manager_add_ui (ui_manager,
+ view->details->extensions_menu_merge_id,
+ path,
+ gtk_action_get_name (action),
+ gtk_action_get_name (action),
+ (menu != NULL) ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ g_free (path);
+
+ path = g_build_path ("/", FM_DIRECTORY_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER, subdirectory, NULL);
+ gtk_ui_manager_add_ui (ui_manager,
+ view->details->extensions_menu_merge_id,
+ path,
+ gtk_action_get_name (action),
+ gtk_action_get_name (action),
+ (menu != NULL) ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ g_free (path);
+
+ /* recursively fill the menu */
+ if (menu != NULL) {
+ char *subdir;
+ GList *children;
+
+ children = caja_menu_get_items (menu);
+
+ subdir = g_build_path ("/", subdirectory, gtk_action_get_name (action), NULL);
+ add_extension_menu_items (view,
+ files,
+ children,
+ subdir);
+
+ caja_menu_item_list_free (children);
+ g_free (subdir);
+ }
+ }
+}
+
+static void
+reset_extension_actions_menu (FMDirectoryView *view, GList *selection)
+{
+ GList *items;
+ GtkUIManager *ui_manager;
+
+ /* Clear any previous inserted items in the extension actions placeholder */
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->extensions_menu_merge_id,
+ &view->details->extensions_menu_action_group);
+
+ caja_ui_prepare_merge_ui (ui_manager,
+ "DirExtensionsMenuGroup",
+ &view->details->extensions_menu_merge_id,
+ &view->details->extensions_menu_action_group);
+
+ items = get_all_extension_menu_items (gtk_widget_get_toplevel (GTK_WIDGET (view)),
+ selection);
+ if (items != NULL) {
+ add_extension_menu_items (view, selection, items, "");
+
+ g_list_foreach (items, (GFunc) g_object_unref, NULL);
+ g_list_free (items);
+ }
+}
+
+static char *
+change_to_view_directory (FMDirectoryView *view)
+{
+ char *path;
+ char *old_path;
+
+ old_path = g_get_current_dir ();
+
+ path = get_view_directory (view);
+
+ /* FIXME: What to do about non-local directories? */
+ if (path != NULL) {
+ g_chdir (path);
+ }
+
+ g_free (path);
+
+ return old_path;
+}
+
+static char **
+get_file_names_as_parameter_array (GList *selection,
+ CajaDirectory *model)
+{
+ CajaFile *file;
+ char **parameters;
+ GList *node;
+ GFile *file_location;
+ GFile *model_location;
+ int i;
+
+ if (model == NULL) {
+ return NULL;
+ }
+
+ parameters = g_new (char *, g_list_length (selection) + 1);
+
+ model_location = caja_directory_get_location (model);
+
+ for (node = selection, i = 0; node != NULL; node = node->next, i++) {
+ file = CAJA_FILE (node->data);
+
+ if (!caja_file_is_local (file)) {
+ parameters[i] = NULL;
+ g_strfreev (parameters);
+ return NULL;
+ }
+
+ file_location = caja_file_get_location (CAJA_FILE (node->data));
+ parameters[i] = g_file_get_relative_path (model_location, file_location);
+ if (parameters[i] == NULL) {
+ parameters[i] = g_file_get_path (file_location);
+ }
+ g_object_unref (file_location);
+ }
+
+ g_object_unref (model_location);
+
+ parameters[i] = NULL;
+ return parameters;
+}
+
+static char *
+get_file_paths_or_uris_as_newline_delimited_string (GList *selection, gboolean get_paths)
+{
+ char *path;
+ char *uri;
+ char *result;
+ CajaDesktopLink *link;
+ GString *expanding_string;
+ GList *node;
+ GFile *location;
+
+ expanding_string = g_string_new ("");
+ for (node = selection; node != NULL; node = node->next) {
+ uri = NULL;
+ if (CAJA_IS_DESKTOP_ICON_FILE (node->data)) {
+ link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (node->data));
+ if (link != NULL) {
+ location = caja_desktop_link_get_activation_location (link);
+ uri = g_file_get_uri (location);
+ g_object_unref (location);
+ g_object_unref (G_OBJECT (link));
+ }
+ } else {
+ uri = caja_file_get_uri (CAJA_FILE (node->data));
+ }
+ if (uri == NULL) {
+ continue;
+ }
+
+ if (get_paths) {
+ path = g_filename_from_uri (uri, NULL, NULL);
+ if (path != NULL) {
+ g_string_append (expanding_string, path);
+ g_free (path);
+ g_string_append (expanding_string, "\n");
+ }
+ } else {
+ g_string_append (expanding_string, uri);
+ g_string_append (expanding_string, "\n");
+ }
+ g_free (uri);
+ }
+
+ result = expanding_string->str;
+ g_string_free (expanding_string, FALSE);
+
+ return result;
+}
+
+static char *
+get_file_paths_as_newline_delimited_string (GList *selection)
+{
+ return get_file_paths_or_uris_as_newline_delimited_string (selection, TRUE);
+}
+
+static char *
+get_file_uris_as_newline_delimited_string (GList *selection)
+{
+ return get_file_paths_or_uris_as_newline_delimited_string (selection, FALSE);
+}
+
+/* returns newly allocated strings for setting the environment variables */
+static void
+get_strings_for_environment_variables (FMDirectoryView *view, GList *selected_files,
+ char **file_paths, char **uris, char **uri)
+{
+ char *directory_uri;
+
+ /* We need to check that the directory uri starts with "file:" since
+ * caja_directory_is_local returns FALSE for nfs.
+ */
+ directory_uri = caja_directory_get_uri (view->details->model);
+ if (eel_str_has_prefix (directory_uri, "file:") ||
+ eel_uri_is_desktop (directory_uri) ||
+ eel_uri_is_trash (directory_uri)) {
+ *file_paths = get_file_paths_as_newline_delimited_string (selected_files);
+ } else {
+ *file_paths = g_strdup ("");
+ }
+ g_free (directory_uri);
+
+ *uris = get_file_uris_as_newline_delimited_string (selected_files);
+
+ *uri = caja_directory_get_uri (view->details->model);
+ if (eel_uri_is_desktop (*uri)) {
+ g_free (*uri);
+ *uri = caja_get_desktop_directory_uri ();
+ }
+}
+
+static FMDirectoryView *
+get_directory_view_of_extra_pane (FMDirectoryView *view)
+{
+ CajaWindowSlotInfo *slot;
+ CajaView *next_view;
+
+ slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
+ if (slot != NULL) {
+ next_view = caja_window_slot_info_get_current_view (slot);
+
+ if (FM_IS_DIRECTORY_VIEW (next_view)) {
+ return FM_DIRECTORY_VIEW (next_view);
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Set up some environment variables that scripts can use
+ * to take advantage of the current Caja state.
+ */
+static void set_script_environment_variables(FMDirectoryView* view, GList* selected_files)
+{
+ char* file_paths;
+ char* uris;
+ char* uri;
+ char* geometry_string;
+ FMDirectoryView* next_view;
+
+ get_strings_for_environment_variables(view, selected_files, &file_paths, &uris, &uri);
+
+ g_setenv("CAJA_SCRIPT_SELECTED_FILE_PATHS", file_paths, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS", file_paths, TRUE); // compatibilidad GNOME
+
+ g_free(file_paths);
+
+ g_setenv("CAJA_SCRIPT_SELECTED_URIS", uris, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_SELECTED_URIS", uris, TRUE); // compatibilidad GNOME
+
+ g_free(uris);
+
+ g_setenv("CAJA_SCRIPT_CURRENT_URI", uri, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_CURRENT_URI", uri, TRUE); // compatibilidad GNOME
+
+
+ g_free(uri);
+
+ geometry_string = eel_gtk_window_get_geometry_string(GTK_WINDOW (fm_directory_view_get_containing_window (view)));
+
+ g_setenv("CAJA_SCRIPT_WINDOW_GEOMETRY", geometry_string, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_WINDOW_GEOMETRY", geometry_string, TRUE); // compatibilidad GNOME
+
+ g_free(geometry_string);
+
+ /* next pane */
+ next_view = get_directory_view_of_extra_pane(view);
+
+ if (next_view)
+ {
+ GList* next_pane_selected_files = fm_directory_view_get_selection (next_view);
+
+ get_strings_for_environment_variables(next_view, next_pane_selected_files, &file_paths, &uris, &uri);
+
+ caja_file_list_free(next_pane_selected_files);
+ }
+ else
+ {
+ file_paths = g_strdup("");
+ uris = g_strdup("");
+ uri = g_strdup("");
+ }
+
+ g_setenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS", file_paths, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS", file_paths, TRUE); // compatibilidad GNOME
+ g_free(file_paths);
+
+ g_setenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS", uris, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_URIS", uris, TRUE); // compatibilidad GNOME
+ g_free(uris);
+
+ g_setenv("CAJA_SCRIPT_NEXT_PANE_CURRENT_URI", uri, TRUE);
+ g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_CURRENT_URI", uri, TRUE); // compatibilidad GNOME
+ g_free(uri);
+}
+
+/* Unset all the special script environment variables. */
+static void unset_script_environment_variables(void)
+{
+ g_unsetenv("CAJA_SCRIPT_SELECTED_FILE_PATHS");
+ g_unsetenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS");
+
+ g_unsetenv("CAJA_SCRIPT_SELECTED_URIS");
+ g_unsetenv("NAUTILUS_SCRIPT_SELECTED_URIS");
+
+ g_unsetenv("CAJA_SCRIPT_CURRENT_URI");
+ g_unsetenv("NAUTILUS_SCRIPT_CURRENT_URI");
+
+ g_unsetenv("CAJA_SCRIPT_WINDOW_GEOMETRY");
+ g_unsetenv("NAUTILUS_SCRIPT_WINDOW_GEOMETRY");
+
+ g_unsetenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS");
+ g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS");
+
+ g_unsetenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS");
+ g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_URIS");
+
+ g_unsetenv("CAJA_SCRIPT_NEXT_PANE_CURRENT_URI");
+ g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_CURRENT_URI");
+}
+
+static void
+run_script_callback (GtkAction *action, gpointer callback_data)
+{
+ ScriptLaunchParameters *launch_parameters;
+ GdkScreen *screen;
+ GList *selected_files;
+ char *file_uri;
+ char *local_file_path;
+ char *quoted_path;
+ char *old_working_dir;
+ char **parameters, *name;
+ GtkWindow *window;
+
+ launch_parameters = (ScriptLaunchParameters *) callback_data;
+
+ file_uri = caja_file_get_uri (launch_parameters->file);
+ local_file_path = g_filename_from_uri (file_uri, NULL, NULL);
+ g_assert (local_file_path != NULL);
+ g_free (file_uri);
+
+ quoted_path = g_shell_quote (local_file_path);
+ g_free (local_file_path);
+
+ old_working_dir = change_to_view_directory (launch_parameters->directory_view);
+
+ selected_files = fm_directory_view_get_selection (launch_parameters->directory_view);
+ set_script_environment_variables (launch_parameters->directory_view, selected_files);
+
+ parameters = get_file_names_as_parameter_array (selected_files,
+ launch_parameters->directory_view->details->model);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (launch_parameters->directory_view));
+
+ name = caja_file_get_name (launch_parameters->file);
+ /* FIXME: handle errors with dialog? Or leave up to each script? */
+ window = fm_directory_view_get_containing_window (launch_parameters->directory_view);
+ caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "directory view run_script_callback, window=%p, name=\"%s\", script_path=\"%s\" (omitting script parameters)",
+ window, name, local_file_path);
+ caja_launch_application_from_command_array (screen, name, quoted_path, FALSE,
+ (const char * const *) parameters);
+ g_free (name);
+ g_strfreev (parameters);
+
+ caja_file_list_free (selected_files);
+ unset_script_environment_variables ();
+ g_chdir (old_working_dir);
+ g_free (old_working_dir);
+ g_free (quoted_path);
+}
+
+static void
+add_script_to_scripts_menus (FMDirectoryView *directory_view,
+ CajaFile *file,
+ const char *menu_path,
+ const char *popup_path,
+ const char *popup_bg_path)
+{
+ ScriptLaunchParameters *launch_parameters;
+ char *tip;
+ char *name;
+ char *uri;
+ char *action_name;
+ char *escaped_label;
+ GdkPixbuf *pixbuf;
+ GtkUIManager *ui_manager;
+ GtkAction *action;
+
+ name = caja_file_get_display_name (file);
+ uri = caja_file_get_uri (file);
+ tip = g_strdup_printf (_("Run \"%s\" on any selected items"), name);
+
+ launch_parameters = script_launch_parameters_new (file, directory_view);
+
+ action_name = escape_action_name (uri, "script_");
+ escaped_label = eel_str_double_underscores (name);
+
+ action = gtk_action_new (action_name,
+ escaped_label,
+ tip,
+ NULL);
+
+ pixbuf = get_menu_icon_for_file (file);
+ if (pixbuf != NULL) {
+ g_object_set_data_full (G_OBJECT (action), "menu-icon",
+ pixbuf,
+ g_object_unref);
+ }
+
+ g_signal_connect_data (action, "activate",
+ G_CALLBACK (run_script_callback),
+ launch_parameters,
+ (GClosureNotify)script_launch_parameters_free, 0);
+
+ gtk_action_group_add_action_with_accel (directory_view->details->scripts_action_group,
+ action, NULL);
+ g_object_unref (action);
+
+ ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
+
+ gtk_ui_manager_add_ui (ui_manager,
+ directory_view->details->scripts_merge_id,
+ menu_path,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ gtk_ui_manager_add_ui (ui_manager,
+ directory_view->details->scripts_merge_id,
+ popup_path,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ gtk_ui_manager_add_ui (ui_manager,
+ directory_view->details->scripts_merge_id,
+ popup_bg_path,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ g_free (name);
+ g_free (uri);
+ g_free (tip);
+ g_free (action_name);
+ g_free (escaped_label);
+}
+
+static void
+add_submenu_to_directory_menus (FMDirectoryView *directory_view,
+ GtkActionGroup *action_group,
+ guint merge_id,
+ CajaFile *file,
+ const char *menu_path,
+ const char *popup_path,
+ const char *popup_bg_path)
+{
+ char *name;
+ GdkPixbuf *pixbuf;
+ char *uri;
+ GtkUIManager *ui_manager;
+
+ ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
+ uri = caja_file_get_uri (file);
+ name = caja_file_get_display_name (file);
+ pixbuf = get_menu_icon_for_file (file);
+ add_submenu (ui_manager, action_group, merge_id, menu_path, uri, name, pixbuf, TRUE);
+ add_submenu (ui_manager, action_group, merge_id, popup_path, uri, name, pixbuf, FALSE);
+ add_submenu (ui_manager, action_group, merge_id, popup_bg_path, uri, name, pixbuf, FALSE);
+ if (pixbuf) {
+ g_object_unref (pixbuf);
+ }
+ g_free (name);
+ g_free (uri);
+}
+
+static gboolean
+directory_belongs_in_scripts_menu (const char *uri)
+{
+ int num_levels;
+ int i;
+
+ if (!eel_str_has_prefix (uri, scripts_directory_uri)) {
+ return FALSE;
+ }
+
+ num_levels = 0;
+ for (i = scripts_directory_uri_length; uri[i] != '\0'; i++) {
+ if (uri[i] == '/') {
+ num_levels++;
+ }
+ }
+
+ if (num_levels > MAX_MENU_LEVELS) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+update_directory_in_scripts_menu (FMDirectoryView *view, CajaDirectory *directory)
+{
+ char *menu_path, *popup_path, *popup_bg_path;
+ GList *file_list, *filtered, *node;
+ gboolean any_scripts;
+ CajaFile *file;
+ CajaDirectory *dir;
+ char *uri;
+ char *escaped_path;
+
+ uri = caja_directory_get_uri (directory);
+ escaped_path = escape_action_path (uri + scripts_directory_uri_length);
+ g_free (uri);
+ menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER,
+ escaped_path,
+ NULL);
+ popup_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER,
+ escaped_path,
+ NULL);
+ popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER,
+ escaped_path,
+ NULL);
+ g_free (escaped_path);
+
+ file_list = caja_directory_get_file_list (directory);
+ filtered = caja_file_list_filter_hidden_and_backup (file_list, FALSE, FALSE);
+ caja_file_list_free (file_list);
+
+ file_list = caja_file_list_sort_by_display_name (filtered);
+
+ any_scripts = FALSE;
+ for (node = file_list; node != NULL; node = node->next) {
+ file = node->data;
+
+ if (caja_file_is_launchable (file)) {
+ add_script_to_scripts_menus (view, file, menu_path, popup_path, popup_bg_path);
+ any_scripts = TRUE;
+ } else if (caja_file_is_directory (file)) {
+ uri = caja_file_get_uri (file);
+ if (directory_belongs_in_scripts_menu (uri)) {
+ dir = caja_directory_get_by_uri (uri);
+ add_directory_to_scripts_directory_list (view, dir);
+ caja_directory_unref (dir);
+
+ add_submenu_to_directory_menus (view,
+ view->details->scripts_action_group,
+ view->details->scripts_merge_id,
+ file, menu_path, popup_path, popup_bg_path);
+
+ any_scripts = TRUE;
+ }
+ g_free (uri);
+ }
+ }
+
+ caja_file_list_free (file_list);
+
+ g_free (popup_path);
+ g_free (popup_bg_path);
+ g_free (menu_path);
+
+ return any_scripts;
+}
+
+static void
+update_scripts_menu (FMDirectoryView *view)
+{
+ gboolean any_scripts;
+ GList *sorted_copy, *node;
+ CajaDirectory *directory;
+ char *uri;
+ GtkUIManager *ui_manager;
+ GtkAction *action;
+
+ /* There is a race condition here. If we don't mark the scripts menu as
+ valid before we begin our task then we can lose script menu updates that
+ occur before we finish. */
+ view->details->scripts_invalid = FALSE;
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->scripts_merge_id,
+ &view->details->scripts_action_group);
+
+ caja_ui_prepare_merge_ui (ui_manager,
+ "ScriptsGroup",
+ &view->details->scripts_merge_id,
+ &view->details->scripts_action_group);
+
+ /* As we walk through the directories, remove any that no longer belong. */
+ any_scripts = FALSE;
+ sorted_copy = caja_directory_list_sort_by_uri
+ (caja_directory_list_copy (view->details->scripts_directory_list));
+ for (node = sorted_copy; node != NULL; node = node->next) {
+ directory = node->data;
+
+ uri = caja_directory_get_uri (directory);
+ if (!directory_belongs_in_scripts_menu (uri)) {
+ remove_directory_from_scripts_directory_list (view, directory);
+ } else if (update_directory_in_scripts_menu (view, directory)) {
+ any_scripts = TRUE;
+ }
+ g_free (uri);
+ }
+ caja_directory_list_free (sorted_copy);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_SCRIPTS);
+ gtk_action_set_visible (action, any_scripts);
+}
+
+static void
+create_template_callback (GtkAction *action, gpointer callback_data)
+{
+ CreateTemplateParameters *parameters;
+
+ parameters = callback_data;
+
+ fm_directory_view_new_file (parameters->directory_view, NULL, parameters->file);
+}
+
+static void
+add_template_to_templates_menus (FMDirectoryView *directory_view,
+ CajaFile *file,
+ const char *menu_path,
+ const char *popup_bg_path)
+{
+ char *tmp, *tip, *uri, *name;
+ char *escaped_label;
+ GdkPixbuf *pixbuf;
+ char *action_name;
+ CreateTemplateParameters *parameters;
+ GtkUIManager *ui_manager;
+ GtkAction *action;
+
+ tmp = caja_file_get_display_name (file);
+ name = eel_filename_strip_extension (tmp);
+ g_free (tmp);
+
+ uri = caja_file_get_uri (file);
+ tip = g_strdup_printf (_("Create Document from template \"%s\""), name);
+
+ action_name = escape_action_name (uri, "template_");
+ escaped_label = eel_str_double_underscores (name);
+
+ parameters = create_template_parameters_new (file, directory_view);
+
+ action = gtk_action_new (action_name,
+ escaped_label,
+ tip,
+ NULL);
+
+ pixbuf = get_menu_icon_for_file (file);
+ if (pixbuf != NULL) {
+ g_object_set_data_full (G_OBJECT (action), "menu-icon",
+ pixbuf,
+ g_object_unref);
+ }
+
+ g_signal_connect_data (action, "activate",
+ G_CALLBACK (create_template_callback),
+ parameters,
+ (GClosureNotify)create_templates_parameters_free, 0);
+
+ gtk_action_group_add_action (directory_view->details->templates_action_group,
+ action);
+ g_object_unref (action);
+
+ ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
+
+ gtk_ui_manager_add_ui (ui_manager,
+ directory_view->details->templates_merge_id,
+ menu_path,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ gtk_ui_manager_add_ui (ui_manager,
+ directory_view->details->templates_merge_id,
+ popup_bg_path,
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ g_free (escaped_label);
+ g_free (name);
+ g_free (tip);
+ g_free (uri);
+ g_free (action_name);
+}
+
+static void
+update_templates_directory (FMDirectoryView *view)
+{
+ CajaDirectory *templates_directory;
+ GList *node, *next;
+ char *templates_uri;
+
+ for (node = view->details->templates_directory_list; node != NULL; node = next) {
+ next = node->next;
+ remove_directory_from_templates_directory_list (view, node->data);
+ }
+
+ if (caja_should_use_templates_directory ()) {
+ templates_uri = caja_get_templates_directory_uri ();
+ templates_directory = caja_directory_get_by_uri (templates_uri);
+ g_free (templates_uri);
+ add_directory_to_templates_directory_list (view, templates_directory);
+ caja_directory_unref (templates_directory);
+ }
+}
+
+static void
+user_dirs_changed (FMDirectoryView *view)
+{
+ update_templates_directory (view);
+ view->details->templates_invalid = TRUE;
+ schedule_update_menus (view);
+}
+
+static gboolean
+directory_belongs_in_templates_menu (const char *templates_directory_uri,
+ const char *uri)
+{
+ int num_levels;
+ int i;
+
+ if (templates_directory_uri == NULL) {
+ return FALSE;
+ }
+
+ if (!g_str_has_prefix (uri, templates_directory_uri)) {
+ return FALSE;
+ }
+
+ num_levels = 0;
+ for (i = strlen (templates_directory_uri); uri[i] != '\0'; i++) {
+ if (uri[i] == '/') {
+ num_levels++;
+ }
+ }
+
+ if (num_levels > MAX_MENU_LEVELS) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+update_directory_in_templates_menu (FMDirectoryView *view,
+ const char *templates_directory_uri,
+ CajaDirectory *directory)
+{
+ char *menu_path, *popup_bg_path;
+ GList *file_list, *filtered, *node;
+ gboolean any_templates;
+ CajaFile *file;
+ CajaDirectory *dir;
+ char *escaped_path;
+ char *uri;
+ int num;
+
+ /* We know this directory belongs to the template dir, so it must exist */
+ g_assert (templates_directory_uri);
+
+ uri = caja_directory_get_uri (directory);
+ escaped_path = escape_action_path (uri + strlen (templates_directory_uri));
+ g_free (uri);
+ menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER,
+ escaped_path,
+ NULL);
+ popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER,
+ escaped_path,
+ NULL);
+ g_free (escaped_path);
+
+ file_list = caja_directory_get_file_list (directory);
+ filtered = caja_file_list_filter_hidden_and_backup (file_list, FALSE, FALSE);
+ caja_file_list_free (file_list);
+
+ file_list = caja_file_list_sort_by_display_name (filtered);
+
+ num = 0;
+ any_templates = FALSE;
+ for (node = file_list; num < TEMPLATE_LIMIT && node != NULL; node = node->next, num++) {
+ file = node->data;
+
+ if (caja_file_is_directory (file)) {
+ uri = caja_file_get_uri (file);
+ if (directory_belongs_in_templates_menu (templates_directory_uri, uri)) {
+ dir = caja_directory_get_by_uri (uri);
+ add_directory_to_templates_directory_list (view, dir);
+ caja_directory_unref (dir);
+
+ add_submenu_to_directory_menus (view,
+ view->details->templates_action_group,
+ view->details->templates_merge_id,
+ file, menu_path, NULL, popup_bg_path);
+
+ any_templates = TRUE;
+ }
+ g_free (uri);
+ } else if (caja_file_can_read (file)) {
+ add_template_to_templates_menus (view, file, menu_path, popup_bg_path);
+ any_templates = TRUE;
+ }
+ }
+
+ caja_file_list_free (file_list);
+
+ g_free (popup_bg_path);
+ g_free (menu_path);
+
+ return any_templates;
+}
+
+
+
+static void
+update_templates_menu (FMDirectoryView *view)
+{
+ gboolean any_templates;
+ GList *sorted_copy, *node;
+ CajaDirectory *directory;
+ GtkUIManager *ui_manager;
+ char *uri;
+ GtkAction *action;
+ char *templates_directory_uri;
+
+ if (caja_should_use_templates_directory ()) {
+ templates_directory_uri = caja_get_templates_directory_uri ();
+ } else {
+ templates_directory_uri = NULL;
+ }
+
+ /* There is a race condition here. If we don't mark the scripts menu as
+ valid before we begin our task then we can lose template menu updates that
+ occur before we finish. */
+ view->details->templates_invalid = FALSE;
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+ caja_ui_unmerge_ui (ui_manager,
+ &view->details->templates_merge_id,
+ &view->details->templates_action_group);
+
+ caja_ui_prepare_merge_ui (ui_manager,
+ "TemplatesGroup",
+ &view->details->templates_merge_id,
+ &view->details->templates_action_group);
+
+ /* As we walk through the directories, remove any that no longer belong. */
+ any_templates = FALSE;
+ sorted_copy = caja_directory_list_sort_by_uri
+ (caja_directory_list_copy (view->details->templates_directory_list));
+ for (node = sorted_copy; node != NULL; node = node->next) {
+ directory = node->data;
+
+ uri = caja_directory_get_uri (directory);
+ if (!directory_belongs_in_templates_menu (templates_directory_uri, uri)) {
+ remove_directory_from_templates_directory_list (view, directory);
+ } else if (update_directory_in_templates_menu (view,
+ templates_directory_uri,
+ directory)) {
+ any_templates = TRUE;
+ }
+ g_free (uri);
+ }
+ caja_directory_list_free (sorted_copy);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_NO_TEMPLATES);
+ gtk_action_set_visible (action, !any_templates);
+
+ g_free (templates_directory_uri);
+}
+
+
+static void
+action_open_scripts_folder_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ open_location (view, scripts_directory_uri, CAJA_WINDOW_OPEN_ACCORDING_TO_MODE, 0);
+
+ eel_show_info_dialog_with_details
+ (_("All executable files in this folder will appear in the "
+ "Scripts menu."),
+ _("Choosing a script from the menu will run "
+ "that script with any selected items as input."),
+ _("All executable files in this folder will appear in the "
+ "Scripts menu. Choosing a script from the menu will run "
+ "that script.\n\n"
+ "When executed from a local folder, scripts will be passed "
+ "the selected file names. When executed from a remote folder "
+ "(e.g. a folder showing web or ftp content), scripts will "
+ "be passed no parameters.\n\n"
+ "In all cases, the following environment variables will be "
+ "set by Caja, which the scripts may use:\n\n"
+ "CAJA_SCRIPT_SELECTED_FILE_PATHS: newline-delimited paths for selected files (only if local)\n\n"
+ "CAJA_SCRIPT_SELECTED_URIS: newline-delimited URIs for selected files\n\n"
+ "CAJA_SCRIPT_CURRENT_URI: URI for current location\n\n"
+ "CAJA_SCRIPT_WINDOW_GEOMETRY: position and size of current window\n\n"
+ "CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS: newline-delimited paths for selected files in the inactive pane of a split-view window (only if local)\n\n"
+ "CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS: newline-delimited URIs for selected files in the inactive pane of a split-view window\n\n"
+ "CAJA_SCRIPT_NEXT_PANE_CURRENT_URI: URI for current location in the inactive pane of a split-view window"),
+ fm_directory_view_get_containing_window (view));
+}
+
+static GtkMenu *
+create_popup_menu (FMDirectoryView *view, const char *popup_path)
+{
+ GtkWidget *menu;
+
+ menu = gtk_ui_manager_get_widget (caja_window_info_get_ui_manager (view->details->window),
+ popup_path);
+ gtk_menu_set_screen (GTK_MENU (menu),
+ gtk_widget_get_screen (GTK_WIDGET (view)));
+ gtk_widget_show (GTK_WIDGET (menu));
+
+ return GTK_MENU (menu);
+}
+
+static void
+copy_or_cut_files (FMDirectoryView *view,
+ GList *clipboard_contents,
+ gboolean cut)
+{
+ int count;
+ char *status_string, *name;
+ CajaClipboardInfo info;
+ GtkTargetList *target_list;
+ GtkTargetEntry *targets;
+ int n_targets;
+
+ info.files = clipboard_contents;
+ info.cut = cut;
+
+ target_list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add (target_list, copied_files_atom, 0, 0);
+ gtk_target_list_add_uri_targets (target_list, 0);
+ gtk_target_list_add_text_targets (target_list, 0);
+
+ targets = gtk_target_table_new_from_list (target_list, &n_targets);
+ gtk_target_list_unref (target_list);
+
+ gtk_clipboard_set_with_data (caja_clipboard_get (GTK_WIDGET (view)),
+ targets, n_targets,
+ caja_get_clipboard_callback, caja_clear_clipboard_callback,
+ NULL);
+ gtk_target_table_free (targets, n_targets);
+
+ caja_clipboard_monitor_set_clipboard_info (caja_clipboard_monitor_get (), &info);
+
+ count = g_list_length (clipboard_contents);
+ if (count == 1) {
+ name = caja_file_get_display_name (clipboard_contents->data);
+ if (cut) {
+ status_string = g_strdup_printf (_("\"%s\" will be moved "
+ "if you select the Paste command"),
+ name);
+ } else {
+ status_string = g_strdup_printf (_("\"%s\" will be copied "
+ "if you select the Paste command"),
+ name);
+ }
+ g_free (name);
+ } else {
+ if (cut) {
+ status_string = g_strdup_printf (ngettext("The %'d selected item will be moved "
+ "if you select the Paste command",
+ "The %'d selected items will be moved "
+ "if you select the Paste command",
+ count),
+ count);
+ } else {
+ status_string = g_strdup_printf (ngettext("The %'d selected item will be copied "
+ "if you select the Paste command",
+ "The %'d selected items will be copied "
+ "if you select the Paste command",
+ count),
+ count);
+ }
+ }
+
+ caja_window_slot_info_set_status (view->details->slot,
+ status_string);
+ g_free (status_string);
+}
+
+static void
+action_copy_files_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ copy_or_cut_files (view, selection, FALSE);
+ caja_file_list_free (selection);
+}
+
+static void
+move_copy_selection_to_location (FMDirectoryView *view,
+ int copy_action,
+ char *target_uri)
+{
+ GList *selection, *uris, *l;
+
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ if (selection == NULL) {
+ return;
+ }
+
+ uris = NULL;
+ for (l = selection; l != NULL; l = l->next) {
+ uris = g_list_prepend (uris,
+ caja_file_get_uri ((CajaFile *) l->data));
+ }
+ uris = g_list_reverse (uris);
+
+ fm_directory_view_move_copy_items (uris, NULL, target_uri,
+ copy_action,
+ 0, 0,
+ view);
+
+ eel_g_list_free_deep (uris);
+ caja_file_list_free (selection);
+}
+
+static void
+move_copy_selection_to_next_pane (FMDirectoryView *view,
+ int copy_action)
+{
+ CajaWindowSlotInfo *slot;
+ char *dest_location;
+
+ slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
+ g_return_if_fail (slot != NULL);
+
+ dest_location = caja_window_slot_info_get_current_location (slot);
+ g_return_if_fail (dest_location != NULL);
+
+ move_copy_selection_to_location (view, copy_action, dest_location);
+}
+
+static void
+action_copy_to_next_pane_callback (GtkAction *action, gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ move_copy_selection_to_next_pane (view,
+ GDK_ACTION_COPY);
+}
+
+static void
+action_move_to_next_pane_callback (GtkAction *action, gpointer callback_data)
+{
+ CajaWindowSlotInfo *slot;
+ char *dest_location;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
+ g_return_if_fail (slot != NULL);
+
+ dest_location = caja_window_slot_info_get_current_location (slot);
+ g_return_if_fail (dest_location != NULL);
+
+ move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
+}
+
+static void
+action_copy_to_home_callback (GtkAction *action, gpointer callback_data)
+{
+ FMDirectoryView *view;
+ char *dest_location;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ dest_location = caja_get_home_directory_uri ();
+ move_copy_selection_to_location (view, GDK_ACTION_COPY, dest_location);
+ g_free (dest_location);
+}
+
+static void
+action_move_to_home_callback (GtkAction *action, gpointer callback_data)
+{
+ FMDirectoryView *view;
+ char *dest_location;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ dest_location = caja_get_home_directory_uri ();
+ move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
+ g_free (dest_location);
+}
+
+static void
+action_copy_to_desktop_callback (GtkAction *action, gpointer callback_data)
+{
+ FMDirectoryView *view;
+ char *dest_location;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ dest_location = caja_get_desktop_directory_uri ();
+ move_copy_selection_to_location (view, GDK_ACTION_COPY, dest_location);
+ g_free (dest_location);
+}
+
+static void
+action_move_to_desktop_callback (GtkAction *action, gpointer callback_data)
+{
+ FMDirectoryView *view;
+ char *dest_location;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ dest_location = caja_get_desktop_directory_uri ();
+ move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
+ g_free (dest_location);
+}
+
+static void
+action_cut_files_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ selection = fm_directory_view_get_selection_for_file_transfer (view);
+ copy_or_cut_files (view, selection, TRUE);
+ caja_file_list_free (selection);
+}
+
+static void
+paste_clipboard_data (FMDirectoryView *view,
+ GtkSelectionData *selection_data,
+ char *destination_uri)
+{
+ gboolean cut;
+ GList *item_uris;
+
+ cut = FALSE;
+ item_uris = caja_clipboard_get_uri_list_from_selection_data (selection_data, &cut,
+ copied_files_atom);
+
+ if (item_uris == NULL|| destination_uri == NULL) {
+ caja_window_slot_info_set_status (view->details->slot,
+ _("There is nothing on the clipboard to paste."));
+ } else {
+ fm_directory_view_move_copy_items (item_uris, NULL, destination_uri,
+ cut ? GDK_ACTION_MOVE : GDK_ACTION_COPY,
+ 0, 0,
+ view);
+
+ /* If items are cut then remove from clipboard */
+ if (cut) {
+ gtk_clipboard_clear (caja_clipboard_get (GTK_WIDGET (view)));
+ }
+
+ eel_g_list_free_deep (item_uris);
+ }
+}
+
+static void
+paste_clipboard_received_callback (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ gpointer data)
+{
+ FMDirectoryView *view;
+ char *view_uri;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ view_uri = fm_directory_view_get_backing_uri (view);
+
+ if (view->details->window != NULL) {
+ paste_clipboard_data (view, selection_data, view_uri);
+ }
+
+ g_free (view_uri);
+
+ g_object_unref (view);
+}
+
+typedef struct {
+ FMDirectoryView *view;
+ CajaFile *target;
+} PasteIntoData;
+
+static void
+paste_into_clipboard_received_callback (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ gpointer callback_data)
+{
+ PasteIntoData *data;
+ FMDirectoryView *view;
+ char *directory_uri;
+
+ data = (PasteIntoData *) callback_data;
+
+ view = FM_DIRECTORY_VIEW (data->view);
+
+ if (view->details->window != NULL) {
+ directory_uri = caja_file_get_activation_uri (data->target);
+
+ paste_clipboard_data (view, selection_data, directory_uri);
+
+ g_free (directory_uri);
+ }
+
+ g_object_unref (view);
+ caja_file_unref (data->target);
+ g_free (data);
+}
+
+static void
+action_paste_files_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ g_object_ref (view);
+ gtk_clipboard_request_contents (caja_clipboard_get (GTK_WIDGET (view)),
+ copied_files_atom,
+ paste_clipboard_received_callback,
+ view);
+}
+
+static void
+paste_into (FMDirectoryView *view,
+ CajaFile *target)
+{
+ PasteIntoData *data;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (CAJA_IS_FILE (target));
+
+ data = g_new (PasteIntoData, 1);
+
+ data->view = g_object_ref (view);
+ data->target = caja_file_ref (target);
+
+ gtk_clipboard_request_contents (caja_clipboard_get (GTK_WIDGET (view)),
+ copied_files_atom,
+ paste_into_clipboard_received_callback,
+ data);
+}
+
+static void
+action_paste_files_into_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ GList *selection;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ selection = fm_directory_view_get_selection (view);
+ if (selection != NULL) {
+ paste_into (view, CAJA_FILE (selection->data));
+ caja_file_list_free (selection);
+ }
+
+}
+
+static void
+real_action_rename (FMDirectoryView *view,
+ gboolean select_all)
+{
+ CajaFile *file;
+ GList *selection;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ selection = fm_directory_view_get_selection (view);
+
+ if (selection_not_empty_in_menu_callback (view, selection)) {
+ file = CAJA_FILE (selection->data);
+ if (!select_all) {
+ /* directories don't have a file extension, so
+ * they are always pre-selected as a whole */
+ select_all = caja_file_is_directory (file);
+ }
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, file, select_all));
+ }
+
+ caja_file_list_free (selection);
+}
+
+static void
+action_rename_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ real_action_rename (FM_DIRECTORY_VIEW (callback_data), FALSE);
+}
+
+static void
+action_rename_select_all_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ real_action_rename (FM_DIRECTORY_VIEW (callback_data), TRUE);
+}
+
+static void
+file_mount_callback (CajaFile *file,
+ GFile *result_location,
+ GError *error,
+ gpointer callback_data)
+{
+ if (error != NULL &&
+ (error->domain != G_IO_ERROR ||
+ (error->code != G_IO_ERROR_CANCELLED &&
+ error->code != G_IO_ERROR_FAILED_HANDLED &&
+ error->code != G_IO_ERROR_ALREADY_MOUNTED))) {
+ eel_show_error_dialog (_("Unable to mount location"),
+ error->message, NULL);
+ }
+}
+
+static void
+file_unmount_callback (CajaFile *file,
+ GFile *result_location,
+ GError *error,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ fm_directory_view_set_initiated_unmount (view, FALSE);
+ g_object_unref (view);
+
+ if (error != NULL &&
+ (error->domain != G_IO_ERROR ||
+ (error->code != G_IO_ERROR_CANCELLED &&
+ error->code != G_IO_ERROR_FAILED_HANDLED))) {
+ eel_show_error_dialog (_("Unable to unmount location"),
+ error->message, NULL);
+ }
+}
+
+static void
+file_eject_callback (CajaFile *file,
+ GFile *result_location,
+ GError *error,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ fm_directory_view_set_initiated_unmount (view, FALSE);
+ g_object_unref (view);
+
+ if (error != NULL &&
+ (error->domain != G_IO_ERROR ||
+ (error->code != G_IO_ERROR_CANCELLED &&
+ error->code != G_IO_ERROR_FAILED_HANDLED))) {
+ eel_show_error_dialog (_("Unable to eject location"),
+ error->message, NULL);
+ }
+}
+
+static void
+file_stop_callback (CajaFile *file,
+ GFile *result_location,
+ GError *error,
+ gpointer callback_data)
+{
+ if (error != NULL &&
+ (error->domain != G_IO_ERROR ||
+ (error->code != G_IO_ERROR_CANCELLED &&
+ error->code != G_IO_ERROR_FAILED_HANDLED))) {
+ eel_show_error_dialog (_("Unable to stop drive"),
+ error->message, NULL);
+ }
+}
+
+static void
+action_mount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (caja_file_can_mount (file)) {
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
+ caja_file_mount (file, mount_op, NULL,
+ file_mount_callback, NULL);
+ g_object_unref (mount_op);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_unmount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+ if (caja_file_can_unmount (file)) {
+ GMountOperation *mount_op;
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_unmount (file, mount_op, NULL,
+ file_unmount_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_format_volume_callback (GtkAction *action,
+ gpointer data)
+{
+#ifdef TODO_GIO
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (something) {
+ g_spawn_command_line_async ("gfloppy", NULL);
+ }
+ }
+ caja_file_list_free (selection);
+#endif
+}
+
+static void
+action_eject_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (caja_file_can_eject (file)) {
+ GMountOperation *mount_op;
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_eject (file, mount_op, NULL,
+ file_eject_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+file_start_callback (CajaFile *file,
+ GFile *result_location,
+ GError *error,
+ gpointer callback_data)
+{
+ if (error != NULL &&
+ (error->domain != G_IO_ERROR ||
+ (error->code != G_IO_ERROR_CANCELLED &&
+ error->code != G_IO_ERROR_FAILED_HANDLED &&
+ error->code != G_IO_ERROR_ALREADY_MOUNTED))) {
+ eel_show_error_dialog (_("Unable to start location"),
+ error->message, NULL);
+ }
+}
+
+static void
+action_start_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_start (file, mount_op, NULL,
+ file_start_callback, NULL);
+ g_object_unref (mount_op);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_stop_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (caja_file_can_stop (file)) {
+ GMountOperation *mount_op;
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_stop (file, mount_op, NULL,
+ file_stop_callback, NULL);
+ g_object_unref (mount_op);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_detect_media_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection, *l;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+ for (l = selection; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+
+ if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
+ caja_file_poll_for_media (file);
+ }
+ }
+ caja_file_list_free (selection);
+}
+
+static void
+action_self_mount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
+ caja_file_mount (file, mount_op, NULL, file_mount_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_self_unmount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_unmount (file, mount_op, NULL, file_unmount_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+}
+
+static void
+action_self_eject_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_eject (file, mount_op, NULL, file_eject_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+}
+
+static void
+action_self_format_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+#ifdef TODO_GIO
+ if (something) {
+ g_spawn_command_line_async ("gfloppy", NULL);
+ }
+#endif
+}
+
+static void
+action_self_start_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_start (file, mount_op, NULL, file_start_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_self_stop_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_stop (file, mount_op, NULL,
+ file_stop_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_self_detect_media_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file == NULL) {
+ return;
+ }
+
+ caja_file_poll_for_media (file);
+}
+
+static void
+action_location_mount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
+ caja_file_mount (file, mount_op, NULL, file_mount_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_location_unmount_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_unmount (file, mount_op, NULL,
+ file_unmount_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+}
+
+static void
+action_location_eject_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ fm_directory_view_set_initiated_unmount (view, TRUE);
+ caja_file_eject (file, mount_op, NULL,
+ file_eject_callback, g_object_ref (view));
+ g_object_unref (mount_op);
+}
+
+static void
+action_location_format_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+#ifdef TODO_GIO
+ if (something) {
+ g_spawn_command_line_async ("gfloppy", NULL);
+ }
+#endif
+}
+
+static void
+action_location_start_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_start (file, mount_op, NULL, file_start_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_location_stop_volume_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+ GMountOperation *mount_op;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
+ caja_file_stop (file, mount_op, NULL,
+ file_stop_callback, NULL);
+ g_object_unref (mount_op);
+}
+
+static void
+action_location_detect_media_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ caja_file_poll_for_media (file);
+}
+
+static void
+connect_to_server_response_callback (GtkDialog *dialog,
+ int response_id,
+ gpointer data)
+{
+ GtkEntry *entry;
+ char *uri;
+ const char *name;
+ char *icon;
+
+ entry = GTK_ENTRY (data);
+
+ switch (response_id) {
+ case GTK_RESPONSE_OK:
+ uri = g_object_get_data (G_OBJECT (dialog), "link-uri");
+ icon = g_object_get_data (G_OBJECT (dialog), "link-icon");
+ name = gtk_entry_get_text (entry);
+#ifdef GIO_CONVERSION_DONE
+ mate_vfs_connect_to_server (uri, (char *)name, icon);
+#endif
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ break;
+ default :
+ g_assert_not_reached ();
+ }
+}
+
+static void
+entry_activate_callback (GtkEntry *entry,
+ gpointer user_data)
+{
+ GtkDialog *dialog;
+
+ dialog = GTK_DIALOG (user_data);
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+static void
+action_connect_to_server_link_callback (GtkAction *action,
+ gpointer data)
+{
+ CajaFile *file;
+ GList *selection;
+ FMDirectoryView *view;
+ char *uri;
+ CajaIconInfo *icon;
+ const char *icon_name;
+ char *name;
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *box;
+ char *title;
+
+ view = FM_DIRECTORY_VIEW (data);
+
+ selection = fm_directory_view_get_selection (view);
+
+ if (!eel_g_list_exactly_one_item (selection)) {
+ caja_file_list_free (selection);
+ return;
+ }
+
+ file = CAJA_FILE (selection->data);
+
+ uri = caja_file_get_activation_uri (file);
+ icon = caja_file_get_icon (file, CAJA_ICON_SIZE_STANDARD, 0);
+ icon_name = caja_icon_info_get_used_name (icon);
+ name = caja_file_get_display_name (file);
+
+ if (uri != NULL) {
+ title = g_strdup_printf (_("Connect to Server %s"), name);
+ dialog = gtk_dialog_new_with_buttons (title,
+ fm_directory_view_get_containing_window (view),
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Connect"), GTK_RESPONSE_OK,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (dialog), "link-uri", g_strdup (uri), g_free);
+ g_object_set_data_full (G_OBJECT (dialog), "link-icon", g_strdup (icon_name), g_free);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+
+ box = gtk_hbox_new (FALSE, 12);
+ gtk_widget_show (box);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ box, TRUE, TRUE, 0);
+
+ label = gtk_label_new_with_mnemonic (_("Link _name:"));
+ gtk_widget_show (label);
+
+ gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 12);
+
+ entry = gtk_entry_new ();
+ if (name) {
+ gtk_entry_set_text (GTK_ENTRY (entry), name);
+ }
+ g_signal_connect (entry,
+ "activate",
+ G_CALLBACK (entry_activate_callback),
+ dialog);
+
+ gtk_widget_show (entry);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+
+ gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 12);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (connect_to_server_response_callback),
+ entry);
+ gtk_widget_show (dialog);
+ }
+
+ g_free (uri);
+ g_object_unref (icon);
+ g_free (name);
+}
+
+static void
+action_location_open_alternate_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ fm_directory_view_activate_file (view,
+ file,
+ CAJA_WINDOW_OPEN_IN_NAVIGATION,
+ 0);
+}
+
+static void
+action_location_open_in_new_tab_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ if (file == NULL) {
+ return;
+ }
+
+ fm_directory_view_activate_file (view,
+ file,
+ CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
+ CAJA_WINDOW_OPEN_FLAG_NEW_TAB);
+}
+
+static void
+action_location_open_folder_window_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ fm_directory_view_activate_file (view,
+ file,
+ CAJA_WINDOW_OPEN_IN_SPATIAL,
+ 0);
+}
+
+static void
+action_location_cut_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+ GList *files;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ files = g_list_append (NULL, file);
+ copy_or_cut_files (view, files, TRUE);
+ g_list_free (files);
+}
+
+static void
+action_location_copy_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+ GList *files;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ files = g_list_append (NULL, file);
+ copy_or_cut_files (view, files, FALSE);
+ g_list_free (files);
+}
+
+static void
+action_location_paste_files_into_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ paste_into (view, file);
+}
+
+static void
+action_location_trash_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+ GList *files;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ files = g_list_append (NULL, file);
+ trash_or_delete_files (fm_directory_view_get_containing_window (view),
+ files, TRUE,
+ view);
+ g_list_free (files);
+}
+
+static void
+action_location_delete_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+ GFile *location;
+ GList *files;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+
+ file = view->details->location_popup_directory_as_file;
+ g_return_if_fail (file != NULL);
+
+ location = caja_file_get_location (file);
+
+ files = g_list_append (NULL, location);
+ caja_file_operations_delete (files, fm_directory_view_get_containing_window (view),
+ NULL, NULL);
+
+ eel_g_object_list_free (files);
+}
+
+static void
+action_location_restore_from_trash_callback (GtkAction *action,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+ CajaFile *file;
+ GList l;
+
+ view = FM_DIRECTORY_VIEW (callback_data);
+ file = view->details->location_popup_directory_as_file;
+
+ l.prev = NULL;
+ l.next = NULL;
+ l.data = file;
+ caja_restore_files_from_trash (&l,
+ fm_directory_view_get_containing_window (view));
+}
+
+static void
+fm_directory_view_init_show_hidden_files (FMDirectoryView *view)
+{
+ CajaWindowShowHiddenFilesMode mode;
+ gboolean show_hidden_changed;
+ gboolean show_hidden_default_setting;
+
+ if (view->details->ignore_hidden_file_preferences) {
+ return;
+ }
+
+ show_hidden_changed = FALSE;
+ mode = caja_window_info_get_hidden_files_mode (view->details->window);
+
+ if (mode == CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT) {
+ show_hidden_default_setting = eel_preferences_get_boolean (CAJA_PREFERENCES_SHOW_HIDDEN_FILES);
+ if (show_hidden_default_setting != view->details->show_hidden_files) {
+ view->details->show_hidden_files = show_hidden_default_setting;
+ view->details->show_backup_files = show_hidden_default_setting;
+ show_hidden_changed = TRUE;
+ }
+ } else {
+ if (mode == CAJA_WINDOW_SHOW_HIDDEN_FILES_ENABLE) {
+ show_hidden_changed = !view->details->show_hidden_files;
+ view->details->show_hidden_files = TRUE;
+ view->details->show_backup_files = TRUE;
+ } else {
+ show_hidden_changed = view->details->show_hidden_files;
+ view->details->show_hidden_files = FALSE;
+ view->details->show_backup_files = FALSE;
+ }
+ }
+
+ if (show_hidden_changed && (view->details->model != NULL)) {
+ load_directory (view, view->details->model);
+ }
+
+}
+
+static const GtkActionEntry directory_view_entries[] = {
+ /* name, stock id, label */ { "New Documents", "document-new", N_("Create _Document") },
+ /* name, stock id, label */ { "Open With", NULL, N_("Open Wit_h"),
+ NULL, N_("Choose a program with which to open the selected item") },
+ /* name, stock id */ { "Properties", GTK_STOCK_PROPERTIES,
+ /* label, accelerator */ N_("_Properties"), "<alt>Return",
+ /* tooltip */ N_("View or modify the properties of each selected item"),
+ G_CALLBACK (action_properties_callback) },
+ /* name, stock id */ { "PropertiesAccel", NULL,
+ /* label, accelerator */ "PropertiesAccel", "<control>I",
+ /* tooltip */ NULL,
+ G_CALLBACK (action_properties_callback) },
+ /* name, stock id */ { "New Folder", "folder-new",
+ /* label, accelerator */ N_("Create _Folder"), "<control><shift>N",
+ /* tooltip */ N_("Create a new empty folder inside this folder"),
+ G_CALLBACK (action_new_folder_callback) },
+ /* name, stock id, label */ { "No Templates", NULL, N_("No templates installed") },
+ /* name, stock id */ { "New Empty File", NULL,
+ /* translators: this is used to indicate that a file doesn't contain anything */
+ /* label, accelerator */ N_("_Empty File"), NULL,
+ /* tooltip */ N_("Create a new empty file inside this folder"),
+ G_CALLBACK (action_new_empty_file_callback) },
+ /* name, stock id */ { "New Launcher", NULL,
+ /* label, accelerator */ N_("Create L_auncher..."), NULL,
+ /* tooltip */ N_("Create a new launcher"),
+ G_CALLBACK (action_new_launcher_callback) },
+ /* name, stock id */ { "Open", NULL,
+ /* label, accelerator */ N_("_Open"), "<control>o",
+ /* tooltip */ N_("Open the selected item in this window"),
+ G_CALLBACK (action_open_callback) },
+ /* name, stock id */ { "OpenAccel", NULL,
+ /* label, accelerator */ "OpenAccel", "<alt>Down",
+ /* tooltip */ NULL,
+ G_CALLBACK (action_open_callback) },
+ /* name, stock id */ { "OpenAlternate", NULL,
+ /* label, accelerator */ N_("Open in Navigation Window"), "<control><shift>o",
+ /* tooltip */ N_("Open each selected item in a navigation window"),
+ G_CALLBACK (action_open_alternate_callback) },
+ /* name, stock id */ { "OpenInNewTab", NULL,
+ /* label, accelerator */ N_("Open in New _Tab"), "<control><shift>o",
+ /* tooltip */ N_("Open each selected item in a new tab"),
+ G_CALLBACK (action_open_new_tab_callback) },
+ /* name, stock id */ { "OpenFolderWindow", NULL,
+ /* label, accelerator */ N_("Open in _Folder Window"), NULL,
+ /* tooltip */ N_("Open each selected item in a folder window"),
+ G_CALLBACK (action_open_folder_window_callback) },
+ /* name, stock id */ { "OtherApplication1", NULL,
+ /* label, accelerator */ N_("Other _Application..."), NULL,
+ /* tooltip */ N_("Choose another application with which to open the selected item"),
+ G_CALLBACK (action_other_application_callback) },
+ /* name, stock id */ { "OtherApplication2", NULL,
+ /* label, accelerator */ N_("Open With Other _Application..."), NULL,
+ /* tooltip */ N_("Choose another application with which to open the selected item"),
+ G_CALLBACK (action_other_application_callback) },
+ /* name, stock id */ { "Open Scripts Folder", NULL,
+ /* label, accelerator */ N_("_Open Scripts Folder"), NULL,
+ /* tooltip */ N_("Show the folder containing the scripts that appear in this menu"),
+ G_CALLBACK (action_open_scripts_folder_callback) },
+ /* name, stock id */ { "Empty Trash", NULL,
+ /* label, accelerator */ N_("E_mpty Trash"), NULL,
+ /* tooltip */ N_("Delete all items in the Trash"),
+ G_CALLBACK (action_empty_trash_callback) },
+ /* name, stock id */ { "Cut", GTK_STOCK_CUT,
+ /* label, accelerator */ NULL, NULL,
+ /* tooltip */ N_("Prepare the selected files to be moved with a Paste command"),
+ G_CALLBACK (action_cut_files_callback) },
+ /* name, stock id */ { "Copy", GTK_STOCK_COPY,
+ /* label, accelerator */ NULL, NULL,
+ /* tooltip */ N_("Prepare the selected files to be copied with a Paste command"),
+ G_CALLBACK (action_copy_files_callback) },
+ /* name, stock id */ { "Paste", GTK_STOCK_PASTE,
+ /* label, accelerator */ NULL, NULL,
+ /* tooltip */ N_("Move or copy files previously selected by a Cut or Copy command"),
+ G_CALLBACK (action_paste_files_callback) },
+ /* We make accelerator "" instead of null here to not inherit the stock
+ accelerator for paste */
+ /* name, stock id */ { "Paste Files Into", GTK_STOCK_PASTE,
+ /* label, accelerator */ N_("_Paste Into Folder"), "",
+ /* tooltip */ N_("Move or copy files previously selected by a Cut or Copy command into the selected folder"),
+ G_CALLBACK (action_paste_files_into_callback) },
+ /* name, stock id, label */ { "CopyToMenu", NULL, N_("Cop_y to") },
+ /* name, stock id, label */ { "MoveToMenu", NULL, N_("M_ove to") },
+ /* name, stock id */ { "Select All", NULL,
+ /* label, accelerator */ N_("Select _All"), "<control>A",
+ /* tooltip */ N_("Select all items in this window"),
+ G_CALLBACK (action_select_all_callback) },
+ /* name, stock id */ { "Select Pattern", NULL,
+ /* label, accelerator */ N_("Select I_tems Matching..."), "<control>S",
+ /* tooltip */ N_("Select items in this window matching a given pattern"),
+ G_CALLBACK (action_select_pattern_callback) },
+ /* name, stock id */ { "Invert Selection", NULL,
+ /* label, accelerator */ N_("_Invert Selection"), "<control><shift>I",
+ /* tooltip */ N_("Select all and only the items that are not currently selected"),
+ G_CALLBACK (action_invert_selection_callback) },
+ /* name, stock id */ { "Duplicate", NULL,
+ /* label, accelerator */ N_("D_uplicate"), NULL,
+ /* tooltip */ N_("Duplicate each selected item"),
+ G_CALLBACK (action_duplicate_callback) },
+ /* name, stock id */ { "Create Link", NULL,
+ /* label, accelerator */ N_("Ma_ke Link"), "<control>M",
+ /* tooltip */ N_("Create a symbolic link for each selected item"),
+ G_CALLBACK (action_create_link_callback) },
+ /* name, stock id */ { "Rename", NULL,
+ /* label, accelerator */ N_("_Rename..."), "F2",
+ /* tooltip */ N_("Rename selected item"),
+ G_CALLBACK (action_rename_callback) },
+ /* name, stock id */ { "RenameSelectAll", NULL,
+ /* label, accelerator */ "RenameSelectAll", "<shift>F2",
+ /* tooltip */ NULL,
+ G_CALLBACK (action_rename_select_all_callback) },
+ /* name, stock id */ { "Trash", NULL,
+ /* label, accelerator */ N_("Mo_ve to Trash"), NULL,
+ /* tooltip */ N_("Move each selected item to the Trash"),
+ G_CALLBACK (action_trash_callback) },
+ /* name, stock id */ { "Delete", NULL,
+ /* label, accelerator */ N_("_Delete"), "<shift>Delete",
+ /* tooltip */ N_("Delete each selected item, without moving to the Trash"),
+ G_CALLBACK (action_delete_callback) },
+ /* name, stock id */ { "Restore From Trash", NULL,
+ /* label, accelerator */ N_("_Restore"), NULL,
+ NULL,
+ G_CALLBACK (action_restore_from_trash_callback) },
+ /*
+ * multiview-TODO: decide whether "Reset to Defaults" should
+ * be window-wide, and not just view-wide.
+ * Since this also resets the "Show hidden files" mode,
+ * it is a mixture of both ATM.
+ */
+ /* name, stock id */ { "Reset to Defaults", NULL,
+ /* label, accelerator */ N_("Reset View to _Defaults"), NULL,
+ /* tooltip */ N_("Reset sorting order and zoom level to match preferences for this view"),
+ G_CALLBACK (action_reset_to_defaults_callback) },
+ /* name, stock id */ { "Connect To Server Link", NULL,
+ /* label, accelerator */ N_("Connect To This Server"), NULL,
+ /* tooltip */ N_("Make a permanent connection to this server"),
+ G_CALLBACK (action_connect_to_server_link_callback) },
+ /* name, stock id */ { "Mount Volume", NULL,
+ /* label, accelerator */ N_("_Mount"), NULL,
+ /* tooltip */ N_("Mount the selected volume"),
+ G_CALLBACK (action_mount_volume_callback) },
+ /* name, stock id */ { "Unmount Volume", NULL,
+ /* label, accelerator */ N_("_Unmount"), NULL,
+ /* tooltip */ N_("Unmount the selected volume"),
+ G_CALLBACK (action_unmount_volume_callback) },
+ /* name, stock id */ { "Eject Volume", NULL,
+ /* label, accelerator */ N_("_Eject"), NULL,
+ /* tooltip */ N_("Eject the selected volume"),
+ G_CALLBACK (action_eject_volume_callback) },
+ /* name, stock id */ { "Format Volume", NULL,
+ /* label, accelerator */ N_("_Format"), NULL,
+ /* tooltip */ N_("Format the selected volume"),
+ G_CALLBACK (action_format_volume_callback) },
+ /* name, stock id */ { "Start Volume", NULL,
+ /* label, accelerator */ N_("_Start"), NULL,
+ /* tooltip */ N_("Start the selected volume"),
+ G_CALLBACK (action_start_volume_callback) },
+ /* name, stock id */ { "Stop Volume", NULL,
+ /* label, accelerator */ N_("_Stop"), NULL,
+ /* tooltip */ N_("Stop the selected volume"),
+ G_CALLBACK (action_stop_volume_callback) },
+ /* name, stock id */ { "Poll", NULL,
+ /* label, accelerator */ N_("_Detect Media"), NULL,
+ /* tooltip */ N_("Detect media in the selected drive"),
+ G_CALLBACK (action_detect_media_callback) },
+ /* name, stock id */ { "Self Mount Volume", NULL,
+ /* label, accelerator */ N_("_Mount"), NULL,
+ /* tooltip */ N_("Mount the volume associated with the open folder"),
+ G_CALLBACK (action_self_mount_volume_callback) },
+ /* name, stock id */ { "Self Unmount Volume", NULL,
+ /* label, accelerator */ N_("_Unmount"), NULL,
+ /* tooltip */ N_("Unmount the volume associated with the open folder"),
+ G_CALLBACK (action_self_unmount_volume_callback) },
+ /* name, stock id */ { "Self Eject Volume", NULL,
+ /* label, accelerator */ N_("_Eject"), NULL,
+ /* tooltip */ N_("Eject the volume associated with the open folder"),
+ G_CALLBACK (action_self_eject_volume_callback) },
+ /* name, stock id */ { "Self Format Volume", NULL,
+ /* label, accelerator */ N_("_Format"), NULL,
+ /* tooltip */ N_("Format the volume associated with the open folder"),
+ G_CALLBACK (action_self_format_volume_callback) },
+ /* name, stock id */ { "Self Start Volume", NULL,
+ /* label, accelerator */ N_("_Start"), NULL,
+ /* tooltip */ N_("Start the volume associated with the open folder"),
+ G_CALLBACK (action_self_start_volume_callback) },
+ /* name, stock id */ { "Self Stop Volume", NULL,
+ /* label, accelerator */ N_("_Stop"), NULL,
+ /* tooltip */ N_("Stop the volume associated with the open folder"),
+ G_CALLBACK (action_self_stop_volume_callback) },
+ /* name, stock id */ { "Self Poll", NULL,
+ /* label, accelerator */ N_("_Detect Media"), NULL,
+ /* tooltip */ N_("Detect media in the selected drive"),
+ G_CALLBACK (action_self_detect_media_callback) },
+ /* name, stock id */ { "OpenCloseParent", NULL,
+ /* label, accelerator */ N_("Open File and Close window"), "<alt><shift>Down",
+ /* tooltip */ NULL,
+ G_CALLBACK (action_open_close_parent_callback) },
+ /* name, stock id */ { "Save Search", NULL,
+ /* label, accelerator */ N_("Sa_ve Search"), NULL,
+ /* tooltip */ N_("Save the edited search"),
+ G_CALLBACK (action_save_search_callback) },
+ /* name, stock id */ { "Save Search As", NULL,
+ /* label, accelerator */ N_("Sa_ve Search As..."), NULL,
+ /* tooltip */ N_("Save the current search as a file"),
+ G_CALLBACK (action_save_search_as_callback) },
+
+ /* Location-specific actions */
+ /* name, stock id */ { FM_ACTION_LOCATION_OPEN_ALTERNATE, NULL,
+ /* label, accelerator */ N_("Open in Navigation Window"), "",
+ /* tooltip */ N_("Open this folder in a navigation window"),
+ G_CALLBACK (action_location_open_alternate_callback) },
+ /* name, stock id */ { FM_ACTION_LOCATION_OPEN_IN_NEW_TAB, NULL,
+ /* label, accelerator */ N_("Open in New _Tab"), "",
+ /* tooltip */ N_("Open this folder in a new tab"),
+ G_CALLBACK (action_location_open_in_new_tab_callback) },
+
+ /* name, stock id */ { FM_ACTION_LOCATION_OPEN_FOLDER_WINDOW, NULL,
+ /* label, accelerator */ N_("Open in _Folder Window"), "",
+ /* tooltip */ N_("Open this folder in a folder window"),
+ G_CALLBACK (action_location_open_folder_window_callback) },
+
+ /* name, stock id */ { FM_ACTION_LOCATION_CUT, GTK_STOCK_CUT,
+ /* label, accelerator */ NULL, "",
+ /* tooltip */ N_("Prepare this folder to be moved with a Paste command"),
+ G_CALLBACK (action_location_cut_callback) },
+ /* name, stock id */ { FM_ACTION_LOCATION_COPY, GTK_STOCK_COPY,
+ /* label, accelerator */ NULL, "",
+ /* tooltip */ N_("Prepare this folder to be copied with a Paste command"),
+ G_CALLBACK (action_location_copy_callback) },
+ /* name, stock id */ { FM_ACTION_LOCATION_PASTE_FILES_INTO, GTK_STOCK_PASTE,
+ /* label, accelerator */ N_("_Paste Into Folder"), "",
+ /* tooltip */ N_("Move or copy files previously selected by a Cut or Copy command into this folder"),
+ G_CALLBACK (action_location_paste_files_into_callback) },
+
+ /* name, stock id */ { FM_ACTION_LOCATION_TRASH, NULL,
+ /* label, accelerator */ N_("Mo_ve to Trash"), "",
+ /* tooltip */ N_("Move this folder to the Trash"),
+ G_CALLBACK (action_location_trash_callback) },
+ /* name, stock id */ { FM_ACTION_LOCATION_DELETE, CAJA_ICON_DELETE,
+ /* label, accelerator */ N_("_Delete"), "",
+ /* tooltip */ N_("Delete this folder, without moving to the Trash"),
+ G_CALLBACK (action_location_delete_callback) },
+ /* name, stock id */ { FM_ACTION_LOCATION_RESTORE_FROM_TRASH, NULL,
+ /* label, accelerator */ N_("_Restore"), NULL, NULL,
+ G_CALLBACK (action_location_restore_from_trash_callback) },
+
+ /* name, stock id */ { "Location Mount Volume", NULL,
+ /* label, accelerator */ N_("_Mount"), NULL,
+ /* tooltip */ N_("Mount the volume associated with this folder"),
+ G_CALLBACK (action_location_mount_volume_callback) },
+ /* name, stock id */ { "Location Unmount Volume", NULL,
+ /* label, accelerator */ N_("_Unmount"), NULL,
+ /* tooltip */ N_("Unmount the volume associated with this folder"),
+ G_CALLBACK (action_location_unmount_volume_callback) },
+ /* name, stock id */ { "Location Eject Volume", NULL,
+ /* label, accelerator */ N_("_Eject"), NULL,
+ /* tooltip */ N_("Eject the volume associated with this folder"),
+ G_CALLBACK (action_location_eject_volume_callback) },
+ /* name, stock id */ { "Location Format Volume", NULL,
+ /* label, accelerator */ N_("_Format"), NULL,
+ /* tooltip */ N_("Format the volume associated with this folder"),
+ G_CALLBACK (action_location_format_volume_callback) },
+ /* name, stock id */ { "Location Start Volume", NULL,
+ /* label, accelerator */ N_("_Start"), NULL,
+ /* tooltip */ N_("Start the volume associated with this folder"),
+ G_CALLBACK (action_location_start_volume_callback) },
+ /* name, stock id */ { "Location Stop Volume", NULL,
+ /* label, accelerator */ N_("_Stop"), NULL,
+ /* tooltip */ N_("Stop the volume associated with this folder"),
+ G_CALLBACK (action_location_stop_volume_callback) },
+ /* name, stock id */ { "Location Poll", NULL,
+ /* label, accelerator */ N_("_Detect Media"), NULL,
+ /* tooltip */ N_("Detect media in the selected drive"),
+ G_CALLBACK (action_location_detect_media_callback) },
+
+ /* name, stock id */ { "LocationProperties", GTK_STOCK_PROPERTIES,
+ /* label, accelerator */ N_("_Properties"), NULL,
+ /* tooltip */ N_("View or modify the properties of this folder"),
+ G_CALLBACK (action_location_properties_callback) },
+
+ /* name, stock id, label */ {FM_ACTION_COPY_TO_NEXT_PANE, NULL, N_("_Other pane"),
+ NULL, N_("Copy the current selection to the other pane in the window"),
+ G_CALLBACK (action_copy_to_next_pane_callback) },
+ /* name, stock id, label */ {FM_ACTION_MOVE_TO_NEXT_PANE, NULL, N_("_Other pane"),
+ NULL, N_("Move the current selection to the other pane in the window"),
+ G_CALLBACK (action_move_to_next_pane_callback) },
+ /* name, stock id, label */ {FM_ACTION_COPY_TO_HOME, CAJA_ICON_HOME,
+ N_("_Home Folder"), NULL,
+ N_("Copy the current selection to the home folder"),
+ G_CALLBACK (action_copy_to_home_callback) },
+ /* name, stock id, label */ {FM_ACTION_MOVE_TO_HOME, CAJA_ICON_HOME,
+ N_("_Home Folder"), NULL,
+ N_("Move the current selection to the home folder"),
+ G_CALLBACK (action_move_to_home_callback) },
+ /* name, stock id, label */ {FM_ACTION_COPY_TO_DESKTOP, CAJA_ICON_DESKTOP,
+ N_("_Desktop"), NULL,
+ N_("Copy the current selection to the desktop"),
+ G_CALLBACK (action_copy_to_desktop_callback) },
+ /* name, stock id, label */ {FM_ACTION_MOVE_TO_DESKTOP, CAJA_ICON_DESKTOP,
+ N_("_Desktop"), NULL,
+ N_("Move the current selection to the desktop"),
+ G_CALLBACK (action_move_to_desktop_callback) },
+};
+
+static void
+connect_proxy (FMDirectoryView *view,
+ GtkAction *action,
+ GtkWidget *proxy,
+ GtkActionGroup *action_group)
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *image;
+
+ if (strcmp (gtk_action_get_name (action), FM_ACTION_NEW_EMPTY_FILE) == 0 &&
+ GTK_IS_IMAGE_MENU_ITEM (proxy)) {
+ pixbuf = get_menu_icon ("text-x-generic");
+ if (pixbuf != NULL) {
+ image = gtk_image_new_from_pixbuf (pixbuf);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy), image);
+
+ g_object_unref (pixbuf);
+ }
+ }
+}
+
+static void
+pre_activate (FMDirectoryView *view,
+ GtkAction *action,
+ GtkActionGroup *action_group)
+{
+ GdkEvent *event;
+ GtkWidget *proxy;
+ gboolean activated_from_popup;
+
+ /* check whether action was activated through a popup menu.
+ * If not, unset the last stored context menu popup position */
+ activated_from_popup = FALSE;
+
+ event = gtk_get_current_event ();
+ proxy = gtk_get_event_widget (event);
+
+ if (proxy != NULL) {
+ GtkWidget *toplevel;
+ GdkWindowTypeHint hint;
+
+ toplevel = gtk_widget_get_toplevel (proxy);
+
+ if (GTK_IS_WINDOW (toplevel)) {
+ hint = gtk_window_get_type_hint (GTK_WINDOW (toplevel));
+
+ if (hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU) {
+ activated_from_popup = TRUE;
+ }
+ }
+ }
+
+ if (!activated_from_popup) {
+ update_context_menu_position_from_event (view, NULL);
+ }
+}
+
+static void
+real_merge_menus (FMDirectoryView *view)
+{
+ GtkActionGroup *action_group;
+ GtkUIManager *ui_manager;
+ GtkAction *action;
+ const char *ui;
+ char *tooltip;
+
+ ui_manager = caja_window_info_get_ui_manager (view->details->window);
+
+ action_group = gtk_action_group_new ("DirViewActions");
+ gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+ view->details->dir_action_group = action_group;
+ gtk_action_group_add_actions (action_group,
+ directory_view_entries, G_N_ELEMENTS (directory_view_entries),
+ view);
+
+ /* Translators: %s is a directory */
+ tooltip = g_strdup_printf(_("Run or manage scripts from %s"), "~/.config/caja/scripts");
+ /* Create a script action here specially because its tooltip is dynamic */
+ action = gtk_action_new ("Scripts", _("_Scripts"), tooltip, NULL);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+ g_free (tooltip);
+
+ action = gtk_action_group_get_action (action_group, FM_ACTION_NO_TEMPLATES);
+ gtk_action_set_sensitive (action, FALSE);
+
+ g_signal_connect_object (action_group, "connect-proxy",
+ G_CALLBACK (connect_proxy), G_OBJECT (view),
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (action_group, "pre-activate",
+ G_CALLBACK (pre_activate), G_OBJECT (view),
+ G_CONNECT_SWAPPED);
+
+ /* Insert action group at end so clipboard action group ends up before it */
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, -1);
+ g_object_unref (action_group); /* owned by ui manager */
+
+ ui = caja_ui_string_get ("caja-directory-view-ui.xml");
+ view->details->dir_merge_id = gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
+ g_signal_connect_object (fm_directory_view_get_background (view), "settings_changed",
+ G_CALLBACK (schedule_update_menus), G_OBJECT (view),
+ G_CONNECT_SWAPPED);
+
+ view->details->scripts_invalid = TRUE;
+ view->details->templates_invalid = TRUE;
+}
+
+
+static gboolean
+can_paste_into_file (CajaFile *file)
+{
+ if (caja_file_is_directory (file) &&
+ caja_file_can_write (file)) {
+ return TRUE;
+ }
+ if (caja_file_has_activation_uri (file)) {
+ GFile *location;
+ CajaFile *activation_file;
+ gboolean res;
+
+ location = caja_file_get_activation_location (file);
+ activation_file = caja_file_get (location);
+ g_object_unref (location);
+
+ /* The target location might not have data for it read yet,
+ and we can't want to do sync I/O, so treat the unknown
+ case as can-write */
+ res = (caja_file_get_file_type (activation_file) == G_FILE_TYPE_UNKNOWN) ||
+ (caja_file_get_file_type (activation_file) == G_FILE_TYPE_DIRECTORY &&
+ caja_file_can_write (activation_file));
+
+ caja_file_unref (activation_file);
+
+ return res;
+ }
+
+ return FALSE;
+}
+
+static void
+clipboard_targets_received (GtkClipboard *clipboard,
+ GdkAtom *targets,
+ int n_targets,
+ gpointer user_data)
+{
+ FMDirectoryView *view;
+ gboolean can_paste;
+ int i;
+ GList *selection;
+ int count;
+ GtkAction *action;
+
+ view = FM_DIRECTORY_VIEW (user_data);
+ can_paste = FALSE;
+
+ if (view->details->window == NULL ||
+ !view->details->active) {
+ /* We've been destroyed or became inactive since call */
+ g_object_unref (view);
+ return;
+ }
+
+ if (targets) {
+ for (i=0; i < n_targets; i++) {
+ if (targets[i] == copied_files_atom) {
+ can_paste = TRUE;
+ }
+ }
+ }
+
+
+ selection = fm_directory_view_get_selection (view);
+ count = g_list_length (selection);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PASTE);
+ gtk_action_set_sensitive (action,
+ can_paste && !fm_directory_view_is_read_only (view));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PASTE_FILES_INTO);
+ gtk_action_set_sensitive (action,
+ can_paste && count == 1 &&
+ can_paste_into_file (CAJA_FILE (selection->data)));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_PASTE_FILES_INTO);
+ g_object_set_data (G_OBJECT (action),
+ "can-paste-according-to-clipboard",
+ GINT_TO_POINTER (can_paste));
+ gtk_action_set_sensitive (action,
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
+ "can-paste-according-to-clipboard")) &&
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
+ "can-paste-according-to-destination")));
+
+ caja_file_list_free (selection);
+
+ g_object_unref (view);
+}
+
+static gboolean
+showing_trash_directory (FMDirectoryView *view)
+{
+ CajaFile *file;
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file != NULL) {
+ return caja_file_is_in_trash (file);
+ }
+ return FALSE;
+}
+
+static gboolean
+should_show_empty_trash (FMDirectoryView *view)
+{
+ return (showing_trash_directory (view) || caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION);
+}
+
+static gboolean
+file_list_all_are_folders (GList *file_list)
+{
+ GList *l;
+ CajaFile *file, *linked_file;
+ char *activation_uri;
+ gboolean is_dir;
+
+ for (l = file_list; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+ if (caja_file_is_caja_link (file) &&
+ !CAJA_IS_DESKTOP_ICON_FILE (file)) {
+ if (caja_file_is_launcher (file)) {
+ return FALSE;
+ }
+
+ activation_uri = caja_file_get_activation_uri (file);
+
+ if (activation_uri == NULL) {
+ g_free (activation_uri);
+ return FALSE;
+ }
+
+ linked_file = caja_file_get_existing_by_uri (activation_uri);
+
+ /* We might not actually know the type of the linked file yet,
+ * however we don't want to schedule a read, since that might do things
+ * like ask for password etc. This is a bit unfortunate, but I don't
+ * know any way around it, so we do various heuristics here
+ * to get things mostly right
+ */
+ is_dir =
+ (linked_file != NULL &&
+ caja_file_is_directory (linked_file)) ||
+ (activation_uri != NULL &&
+ activation_uri[strlen (activation_uri) - 1] == '/');
+
+ caja_file_unref (linked_file);
+ g_free (activation_uri);
+
+ if (!is_dir) {
+ return FALSE;
+ }
+ } else if (!(caja_file_is_directory (file) ||
+ CAJA_IS_DESKTOP_ICON_FILE (file))) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void
+file_should_show_foreach (CajaFile *file,
+ gboolean *show_mount,
+ gboolean *show_unmount,
+ gboolean *show_eject,
+ gboolean *show_connect,
+ gboolean *show_format,
+ gboolean *show_start,
+ gboolean *show_stop,
+ gboolean *show_poll,
+ GDriveStartStopType *start_stop_type)
+{
+ char *uri;
+
+ *show_mount = FALSE;
+ *show_unmount = FALSE;
+ *show_eject = FALSE;
+ *show_connect = FALSE;
+ *show_format = FALSE;
+ *show_start = FALSE;
+ *show_stop = FALSE;
+ *show_poll = FALSE;
+
+ if (caja_file_can_eject (file)) {
+ *show_eject = TRUE;
+ }
+
+ if (caja_file_can_mount (file)) {
+ *show_mount = TRUE;
+
+#ifdef TODO_GIO
+ if (something &&
+ g_find_program_in_path ("gfloppy")) {
+ *show_format = TRUE;
+ }
+#endif
+ }
+
+ if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
+ *show_start = TRUE;
+ }
+
+ if (caja_file_can_stop (file)) {
+ *show_stop = TRUE;
+ }
+
+ /* Dot not show both Unmount and Eject/Safe Removal; too confusing to
+ * have too many menu entries */
+ if (caja_file_can_unmount (file) && !*show_eject && !*show_stop) {
+ *show_unmount = TRUE;
+ }
+
+ if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
+ *show_poll = TRUE;
+ }
+
+ *start_stop_type = caja_file_get_start_stop_type (file);
+
+ if (caja_file_is_caja_link (file)) {
+ uri = caja_file_get_activation_uri (file);
+ if (uri != NULL &&
+ (eel_istr_has_prefix (uri, "ftp:") ||
+ eel_istr_has_prefix (uri, "ssh:") ||
+ eel_istr_has_prefix (uri, "sftp:") ||
+ eel_istr_has_prefix (uri, "dav:") ||
+ eel_istr_has_prefix (uri, "davs:"))) {
+ *show_connect = TRUE;
+ }
+ g_free (uri);
+ }
+}
+
+static void
+file_should_show_self (CajaFile *file,
+ gboolean *show_mount,
+ gboolean *show_unmount,
+ gboolean *show_eject,
+ gboolean *show_format,
+ gboolean *show_start,
+ gboolean *show_stop,
+ gboolean *show_poll,
+ GDriveStartStopType *start_stop_type)
+{
+ *show_mount = FALSE;
+ *show_unmount = FALSE;
+ *show_eject = FALSE;
+ *show_format = FALSE;
+ *show_start = FALSE;
+ *show_stop = FALSE;
+ *show_poll = FALSE;
+
+ if (file == NULL) {
+ return;
+ }
+
+ if (caja_file_can_eject (file)) {
+ *show_eject = TRUE;
+ }
+
+ if (caja_file_can_mount (file)) {
+ *show_mount = TRUE;
+ }
+
+#ifdef TODO_GIO
+ if (something && g_find_program_in_path ("gfloppy")) {
+ *show_format = TRUE;
+ }
+#endif
+
+ if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
+ *show_start = TRUE;
+ }
+
+ if (caja_file_can_stop (file)) {
+ *show_stop = TRUE;
+ }
+
+ /* Dot not show both Unmount and Eject/Safe Removal; too confusing to
+ * have too many menu entries */
+ if (caja_file_can_unmount (file) && !*show_eject && !*show_stop) {
+ *show_unmount = TRUE;
+ }
+
+ if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
+ *show_poll = TRUE;
+ }
+
+ *start_stop_type = caja_file_get_start_stop_type (file);
+
+}
+
+static gboolean
+files_are_all_directories (GList *files)
+{
+ CajaFile *file;
+ GList *l;
+ gboolean all_directories;
+
+ all_directories = TRUE;
+
+ for (l = files; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+ all_directories &= caja_file_is_directory (file);
+ }
+
+ return all_directories;
+}
+
+static gboolean
+files_is_none_directory (GList *files)
+{
+ CajaFile *file;
+ GList *l;
+ gboolean no_directory;
+
+ no_directory = TRUE;
+
+ for (l = files; l != NULL; l = l->next) {
+ file = CAJA_FILE (l->data);
+ no_directory &= !caja_file_is_directory (file);
+ }
+
+ return no_directory;
+}
+
+static void
+update_restore_from_trash_action (GtkAction *action,
+ GList *files,
+ gboolean is_self)
+{
+ CajaFile *original_file;
+ CajaFile *original_dir;
+ GHashTable *original_dirs_hash;
+ GList *original_dirs;
+ GFile *original_location;
+ char *tooltip, *original_name;
+
+ original_file = NULL;
+ original_dir = NULL;
+ original_dirs = NULL;
+ original_dirs_hash = NULL;
+ original_location = NULL;
+ original_name = NULL;
+
+ if (files != NULL) {
+ if (g_list_length (files) == 1) {
+ original_file = caja_file_get_trash_original_file (files->data);
+ } else {
+ original_dirs_hash = caja_trashed_files_get_original_directories (files, NULL);
+ if (original_dirs_hash != NULL) {
+ original_dirs = g_hash_table_get_keys (original_dirs_hash);
+ if (g_list_length (original_dirs) == 1) {
+ original_dir = caja_file_ref (CAJA_FILE (original_dirs->data));
+ }
+ }
+ }
+ }
+
+ if (original_file != NULL || original_dirs != NULL) {
+ gtk_action_set_visible (action, TRUE);
+
+ if (original_file != NULL) {
+ original_location = caja_file_get_location (original_file);
+ } else if (original_dir != NULL) {
+ original_location = caja_file_get_location (original_dir);
+ }
+
+ if (original_location != NULL) {
+ original_name = g_file_get_parse_name (original_location);
+ }
+
+ if (is_self) {
+ g_assert (g_list_length (files) == 1);
+ g_assert (original_location != NULL);
+ tooltip = g_strdup_printf (_("Move the open folder out of the trash to \"%s\""), original_name);
+ } else if (files_are_all_directories (files)) {
+ if (original_name != NULL) {
+ tooltip = g_strdup_printf (ngettext ("Move the selected folder out of the trash to \"%s\"",
+ "Move the selected folders out of the trash to \"%s\"",
+ g_list_length (files)), original_name);
+ } else {
+ tooltip = g_strdup_printf (ngettext ("Move the selected folder out of the trash",
+ "Move the selected folders out of the trash",
+ g_list_length (files)));
+ }
+ } else if (files_is_none_directory (files)) {
+ if (original_name != NULL) {
+ tooltip = g_strdup_printf (ngettext ("Move the selected file out of the trash to \"%s\"",
+ "Move the selected files out of the trash to \"%s\"",
+ g_list_length (files)), original_name);
+ } else {
+ tooltip = g_strdup_printf (ngettext ("Move the selected file out of the trash",
+ "Move the selected files out of the trash",
+ g_list_length (files)));
+ }
+ } else {
+ if (original_name != NULL) {
+ tooltip = g_strdup_printf (ngettext ("Move the selected item out of the trash to \"%s\"",
+ "Move the selected items out of the trash to \"%s\"",
+ g_list_length (files)), original_name);
+ } else {
+ tooltip = g_strdup_printf (ngettext ("Move the selected item out of the trash",
+ "Move the selected items out of the trash",
+ g_list_length (files)));
+ }
+ }
+ g_free (original_name);
+
+ g_object_set (action, "tooltip", tooltip, NULL);
+
+ if (original_location != NULL) {
+ g_object_unref (original_location);
+ }
+ } else {
+ gtk_action_set_visible (action, FALSE);
+ }
+
+ caja_file_unref (original_file);
+ caja_file_unref (original_dir);
+ g_list_free (original_dirs);
+
+ if (original_dirs_hash != NULL) {
+ g_hash_table_destroy (original_dirs_hash);
+ }
+}
+
+static void
+real_update_menus_volumes (FMDirectoryView *view,
+ GList *selection,
+ gint selection_count)
+{
+ GList *l;
+ CajaFile *file;
+ gboolean show_mount;
+ gboolean show_unmount;
+ gboolean show_eject;
+ gboolean show_connect;
+ gboolean show_format;
+ gboolean show_start;
+ gboolean show_stop;
+ gboolean show_poll;
+ GDriveStartStopType start_stop_type;
+ gboolean show_self_mount;
+ gboolean show_self_unmount;
+ gboolean show_self_eject;
+ gboolean show_self_format;
+ gboolean show_self_start;
+ gboolean show_self_stop;
+ gboolean show_self_poll;
+ GDriveStartStopType self_start_stop_type;
+ GtkAction *action;
+
+ show_mount = (selection != NULL);
+ show_unmount = (selection != NULL);
+ show_eject = (selection != NULL);
+ show_connect = (selection != NULL && selection_count == 1);
+ show_format = (selection != NULL && selection_count == 1);
+ show_start = (selection != NULL && selection_count == 1);
+ show_stop = (selection != NULL && selection_count == 1);
+ show_poll = (selection != NULL && selection_count == 1);
+ start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
+ self_start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
+
+ for (l = selection; l != NULL && (show_mount || show_unmount
+ || show_eject || show_connect
+ || show_format || show_start
+ || show_stop || show_poll);
+ l = l->next) {
+ gboolean show_mount_one;
+ gboolean show_unmount_one;
+ gboolean show_eject_one;
+ gboolean show_connect_one;
+ gboolean show_format_one;
+ gboolean show_start_one;
+ gboolean show_stop_one;
+ gboolean show_poll_one;
+
+ file = CAJA_FILE (l->data);
+ file_should_show_foreach (file,
+ &show_mount_one,
+ &show_unmount_one,
+ &show_eject_one,
+ &show_connect_one,
+ &show_format_one,
+ &show_start_one,
+ &show_stop_one,
+ &show_poll_one,
+ &start_stop_type);
+
+ show_mount &= show_mount_one;
+ show_unmount &= show_unmount_one;
+ show_eject &= show_eject_one;
+ show_connect &= show_connect_one;
+ show_format &= show_format_one;
+ show_start &= show_start_one;
+ show_stop &= show_stop_one;
+ show_poll &= show_poll_one;
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_CONNECT_TO_SERVER_LINK);
+ gtk_action_set_visible (action, show_connect);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_MOUNT_VOLUME);
+ gtk_action_set_visible (action, show_mount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_UNMOUNT_VOLUME);
+ gtk_action_set_visible (action, show_unmount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_EJECT_VOLUME);
+ gtk_action_set_visible (action, show_eject);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_FORMAT_VOLUME);
+ gtk_action_set_visible (action, show_format);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_START_VOLUME);
+ gtk_action_set_visible (action, show_start);
+ if (show_start) {
+ switch (start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Connect"));
+ gtk_action_set_tooltip (action, _("Connect to the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Start Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Start the selected multi-disk drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("U_nlock Drive"));
+ gtk_action_set_tooltip (action, _("Unlock the selected drive"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_STOP_VOLUME);
+ gtk_action_set_visible (action, show_stop);
+ if (show_stop) {
+ switch (start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Stop"));
+ gtk_action_set_tooltip (action, _("Stop the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Safely Remove Drive"));
+ gtk_action_set_tooltip (action, _("Safely remove the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Disconnect"));
+ gtk_action_set_tooltip (action, _("Disconnect the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Stop the selected multi-disk drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("_Lock Drive"));
+ gtk_action_set_tooltip (action, _("Lock the selected drive"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_POLL);
+ gtk_action_set_visible (action, show_poll);
+
+ show_self_mount = show_self_unmount = show_self_eject =
+ show_self_format = show_self_start = show_self_stop = show_self_poll = FALSE;
+
+ file = fm_directory_view_get_directory_as_file (view);
+ file_should_show_self (file,
+ &show_self_mount,
+ &show_self_unmount,
+ &show_self_eject,
+ &show_self_format,
+ &show_self_start,
+ &show_self_stop,
+ &show_self_poll,
+ &self_start_stop_type);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_MOUNT_VOLUME);
+ gtk_action_set_visible (action, show_self_mount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_UNMOUNT_VOLUME);
+ gtk_action_set_visible (action, show_self_unmount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_EJECT_VOLUME);
+ gtk_action_set_visible (action, show_self_eject);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_FORMAT_VOLUME);
+ gtk_action_set_visible (action, show_self_format);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_START_VOLUME);
+ gtk_action_set_visible (action, show_self_start);
+ if (show_self_start) {
+ switch (self_start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Connect"));
+ gtk_action_set_tooltip (action, _("Connect to the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Start Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Start the multi-disk drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("_Unlock Drive"));
+ gtk_action_set_tooltip (action, _("Unlock the drive associated with the open folder"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_STOP_VOLUME);
+ gtk_action_set_visible (action, show_self_stop);
+ if (show_self_stop) {
+ switch (self_start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Stop"));
+ gtk_action_set_tooltip (action, _("_Stop the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Safely Remove Drive"));
+ gtk_action_set_tooltip (action, _("Safely remove the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Disconnect"));
+ gtk_action_set_tooltip (action, _("Disconnect the drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Stop the multi-disk drive associated with the open folder"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("_Lock Drive"));
+ gtk_action_set_tooltip (action, _("Lock the drive associated with the open folder"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELF_POLL);
+ gtk_action_set_visible (action, show_self_poll);
+
+}
+
+static void
+real_update_location_menu_volumes (FMDirectoryView *view)
+{
+ GtkAction *action;
+ CajaFile *file;
+ gboolean show_mount;
+ gboolean show_unmount;
+ gboolean show_eject;
+ gboolean show_connect;
+ gboolean show_format;
+ gboolean show_start;
+ gboolean show_stop;
+ gboolean show_poll;
+ GDriveStartStopType start_stop_type;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
+
+ file = CAJA_FILE (view->details->location_popup_directory_as_file);
+ file_should_show_foreach (file,
+ &show_mount,
+ &show_unmount,
+ &show_eject,
+ &show_connect,
+ &show_format,
+ &show_start,
+ &show_stop,
+ &show_poll,
+ &start_stop_type);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_MOUNT_VOLUME);
+ gtk_action_set_visible (action, show_mount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_UNMOUNT_VOLUME);
+ gtk_action_set_visible (action, show_unmount);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_EJECT_VOLUME);
+ gtk_action_set_visible (action, show_eject);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_FORMAT_VOLUME);
+ gtk_action_set_visible (action, show_format);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_START_VOLUME);
+ gtk_action_set_visible (action, show_start);
+ if (show_start) {
+ switch (start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Start"));
+ gtk_action_set_tooltip (action, _("Start the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Connect"));
+ gtk_action_set_tooltip (action, _("Connect to the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Start Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Start the selected multi-disk drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("_Unlock Drive"));
+ gtk_action_set_tooltip (action, _("Unlock the selected drive"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_STOP_VOLUME);
+ gtk_action_set_visible (action, show_stop);
+ if (show_stop) {
+ switch (start_stop_type) {
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ gtk_action_set_label (action, _("_Stop"));
+ gtk_action_set_tooltip (action, _("Stop the selected volume"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ gtk_action_set_label (action, _("_Safely Remove Drive"));
+ gtk_action_set_tooltip (action, _("Safely remove the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ gtk_action_set_label (action, _("_Disconnect"));
+ gtk_action_set_tooltip (action, _("Disconnect the selected drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
+ gtk_action_set_tooltip (action, _("Stop the selected multi-disk drive"));
+ break;
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ gtk_action_set_label (action, _("_Lock Drive"));
+ gtk_action_set_tooltip (action, _("Lock the selected drive"));
+ break;
+ }
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_POLL);
+ gtk_action_set_visible (action, show_poll);
+}
+
+/* TODO: we should split out this routine into two functions:
+ * Update on clipboard changes
+ * Update on selection changes
+ */
+static void
+real_update_paste_menu (FMDirectoryView *view,
+ GList *selection,
+ gint selection_count)
+{
+ gboolean can_paste_files_into;
+ gboolean selection_is_read_only;
+ gboolean is_read_only;
+ GtkAction *action;
+
+ selection_is_read_only = selection_count == 1 &&
+ (!caja_file_can_write (CAJA_FILE (selection->data)) &&
+ !caja_file_has_activation_uri (CAJA_FILE (selection->data)));
+
+ is_read_only = fm_directory_view_is_read_only (view);
+
+ can_paste_files_into = (selection_count == 1 &&
+ can_paste_into_file (CAJA_FILE (selection->data)));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PASTE);
+ gtk_action_set_sensitive (action, !is_read_only);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PASTE_FILES_INTO);
+ gtk_action_set_visible (action, can_paste_files_into);
+ gtk_action_set_sensitive (action, !selection_is_read_only);
+
+ /* Ask the clipboard */
+ g_object_ref (view); /* Need to keep the object alive until we get the reply */
+ gtk_clipboard_request_targets (caja_clipboard_get (GTK_WIDGET (view)),
+ clipboard_targets_received,
+ view);
+}
+
+static void
+real_update_location_menu (FMDirectoryView *view)
+{
+ GtkAction *action;
+ CajaFile *file;
+ gboolean is_special_link;
+ gboolean is_desktop_or_home_dir;
+ gboolean can_delete_file, show_delete;
+ gboolean show_separate_delete_command;
+ gboolean show_open_folder_window;
+ gboolean show_open_in_new_tab;
+ GList l;
+ char *label;
+ char *tip;
+
+ show_open_folder_window = FALSE;
+ show_open_in_new_tab = FALSE;
+
+ if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
+ label = _("Open in New _Window");
+ } else {
+ label = _("Browse in New _Window");
+ show_open_folder_window = TRUE;
+ }
+
+ show_open_in_new_tab = TRUE;
+ } else {
+ label = g_strdup (ngettext ("_Browse Folder",
+ "_Browse Folders", 1));
+ }
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_OPEN_ALTERNATE);
+ g_object_set (action,
+ "label", label,
+ NULL);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_OPEN_IN_NEW_TAB);
+ gtk_action_set_visible (action, show_open_in_new_tab);
+
+ if (show_open_in_new_tab) {
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
+ label = _("Open in New _Tab");
+ } else {
+ label = _("Browse in New _Tab");
+ }
+ g_object_set (action,
+ "label", label,
+ NULL);
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_OPEN_FOLDER_WINDOW);
+ gtk_action_set_visible (action, show_open_folder_window);
+
+ file = view->details->location_popup_directory_as_file;
+ g_assert (CAJA_IS_FILE (file));
+ g_assert (caja_file_check_if_ready (file, CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO));
+
+ is_special_link = CAJA_IS_DESKTOP_ICON_FILE (file);
+ is_desktop_or_home_dir = caja_file_is_home (file)
+ || caja_file_is_desktop_directory (file);
+
+ can_delete_file =
+ caja_file_can_delete (file) &&
+ !is_special_link &&
+ !is_desktop_or_home_dir;
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_CUT);
+ gtk_action_set_sensitive (action, can_delete_file);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_PASTE_FILES_INTO);
+ g_object_set_data (G_OBJECT (action),
+ "can-paste-according-to-destination",
+ GINT_TO_POINTER (can_paste_into_file (file)));
+ gtk_action_set_sensitive (action,
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
+ "can-paste-according-to-clipboard")) &&
+ GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
+ "can-paste-according-to-destination")));
+
+ show_delete = TRUE;
+
+ if (file != NULL &&
+ caja_file_is_in_trash (file)) {
+ if (caja_file_is_self_owned (file)) {
+ show_delete = FALSE;
+ }
+
+ label = _("_Delete Permanently");
+ tip = _("Delete the open folder permanently");
+ show_separate_delete_command = FALSE;
+ } else {
+ label = _("Mo_ve to Trash");
+ tip = _("Move the open folder to the Trash");
+ show_separate_delete_command = show_delete_command_auto_value;
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_TRASH);
+ g_object_set (action,
+ "label", label,
+ "tooltip", tip,
+ "icon-name", (file != NULL &&
+ caja_file_is_in_trash (file)) ?
+ CAJA_ICON_DELETE : CAJA_ICON_TRASH_FULL,
+ NULL);
+ gtk_action_set_sensitive (action, can_delete_file);
+ gtk_action_set_visible (action, show_delete);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_DELETE);
+ gtk_action_set_visible (action, show_separate_delete_command);
+ if (show_separate_delete_command) {
+ gtk_action_set_sensitive (action, can_delete_file);
+ g_object_set (action,
+ "icon-name", CAJA_ICON_DELETE,
+ "sensitive", can_delete_file,
+ NULL);
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_LOCATION_RESTORE_FROM_TRASH);
+ l.prev = NULL;
+ l.next = NULL;
+ l.data = file;
+ update_restore_from_trash_action (action, &l, TRUE);
+
+ real_update_location_menu_volumes (view);
+
+ /* we silently assume that fm_directory_view_supports_properties always returns the same value.
+ * Therefore, we don't update the sensitivity of FM_ACTION_LOCATION_PROPERTIES */
+}
+
+static void
+clipboard_changed_callback (CajaClipboardMonitor *monitor, FMDirectoryView *view)
+{
+ GList *selection;
+ gint selection_count;
+
+ if (!view->details->active) {
+ return;
+ }
+
+ selection = fm_directory_view_get_selection (view);
+ selection_count = g_list_length (selection);
+
+ real_update_paste_menu (view, selection, selection_count);
+
+ caja_file_list_free (selection);
+
+}
+
+static gboolean
+can_delete_all (GList *files)
+{
+ CajaFile *file;
+ GList *l;
+
+ for (l = files; l != NULL; l = l->next) {
+ file = l->data;
+ if (!caja_file_can_delete (file)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static gboolean
+has_writable_extra_pane (FMDirectoryView *view)
+{
+ FMDirectoryView *other_view;
+
+ other_view = get_directory_view_of_extra_pane (view);
+ if (other_view != NULL) {
+ return !fm_directory_view_is_read_only (other_view);
+ }
+ return FALSE;
+}
+
+static void
+real_update_menus (FMDirectoryView *view)
+{
+ GList *selection, *l;
+ gint selection_count;
+ const char *tip, *label;
+ char *label_with_underscore;
+ gboolean selection_contains_special_link;
+ gboolean selection_contains_desktop_or_home_dir;
+ gboolean can_create_files;
+ gboolean can_delete_files;
+ gboolean can_copy_files;
+ gboolean can_link_files;
+ gboolean can_duplicate_files;
+ gboolean show_separate_delete_command;
+ gboolean vfolder_directory;
+ gboolean disable_command_line;
+ gboolean show_open_alternate;
+ gboolean can_open;
+ gboolean show_app;
+ gboolean show_save_search;
+ gboolean save_search_sensitive;
+ gboolean show_save_search_as;
+ gboolean show_open_folder_window;
+ GtkAction *action;
+ GAppInfo *app;
+ GIcon *app_icon;
+ GtkWidget *menuitem;
+ gboolean next_pane_is_writable;
+ gboolean show_properties;
+
+ selection = fm_directory_view_get_selection (view);
+ selection_count = g_list_length (selection);
+
+ selection_contains_special_link = special_link_in_selection (view);
+ selection_contains_desktop_or_home_dir = desktop_or_home_dir_in_selection (view);
+
+ can_create_files = fm_directory_view_supports_creating_files (view);
+ can_delete_files =
+ can_delete_all (selection) &&
+ selection_count != 0 &&
+ !selection_contains_special_link &&
+ !selection_contains_desktop_or_home_dir;
+ can_copy_files = selection_count != 0
+ && !selection_contains_special_link;
+
+ can_duplicate_files = can_create_files && can_copy_files;
+ can_link_files = can_create_files && can_copy_files;
+
+ vfolder_directory = we_are_in_vfolder_desktop_dir (view);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_RENAME);
+ gtk_action_set_sensitive (action,
+ selection_count == 1 &&
+ fm_directory_view_can_rename_file (view, selection->data));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_NEW_FOLDER);
+ gtk_action_set_sensitive (action, can_create_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OPEN);
+ gtk_action_set_sensitive (action, selection_count != 0);
+
+ can_open = show_app = selection_count != 0;
+
+ for (l = selection; l != NULL; l = l->next) {
+ CajaFile *file;
+
+ file = CAJA_FILE (selection->data);
+
+ if (!caja_mime_file_opens_in_external_app (file)) {
+ show_app = FALSE;
+ }
+
+ if (!show_app) {
+ break;
+ }
+ }
+
+ label_with_underscore = NULL;
+
+ app = NULL;
+ app_icon = NULL;
+
+ if (can_open && show_app) {
+ app = caja_mime_get_default_application_for_files (selection);
+ }
+
+ if (app != NULL) {
+ char *escaped_app;
+
+ escaped_app = eel_str_double_underscores (g_app_info_get_display_name (app));
+ label_with_underscore = g_strdup_printf (_("_Open With %s"),
+ escaped_app);
+
+ app_icon = g_app_info_get_icon (app);
+ if (app_icon != NULL) {
+ g_object_ref (app_icon);
+ }
+
+ g_free (escaped_app);
+ g_object_unref (app);
+ }
+
+ g_object_set (action, "label",
+ label_with_underscore ? label_with_underscore : _("_Open"),
+ NULL);
+
+ menuitem = gtk_ui_manager_get_widget (
+ caja_window_info_get_ui_manager (view->details->window),
+ FM_DIRECTORY_VIEW_MENU_PATH_OPEN);
+
+ /* Only force displaying the icon if it is an application icon */
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
+
+ menuitem = gtk_ui_manager_get_widget (
+ caja_window_info_get_ui_manager (view->details->window),
+ FM_DIRECTORY_VIEW_POPUP_PATH_OPEN);
+
+ /* Only force displaying the icon if it is an application icon */
+ gtk_image_menu_item_set_always_show_image (
+ GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
+
+ if (app_icon == NULL) {
+ app_icon = g_themed_icon_new (GTK_STOCK_OPEN);
+ }
+
+ gtk_action_set_gicon (action, app_icon);
+ g_object_unref (app_icon);
+
+ gtk_action_set_visible (action, can_open);
+
+ g_free (label_with_underscore);
+
+ show_open_alternate = file_list_all_are_folders (selection) &&
+ selection_count > 0 &&
+ !(caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_DESKTOP &&
+ eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER));
+ show_open_folder_window = FALSE;
+ if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
+ if (selection_count == 0 || selection_count == 1) {
+ label_with_underscore = g_strdup (_("Open in New _Window"));
+ } else {
+ label_with_underscore = g_strdup_printf (ngettext("Open in %'d New _Window",
+ "Open in %'d New _Windows",
+ selection_count),
+ selection_count);
+ }
+ } else {
+ if (selection_count == 0 || selection_count == 1) {
+ label_with_underscore = g_strdup (_("Browse in New _Window"));
+ } else {
+ label_with_underscore = g_strdup_printf (ngettext("Browse in %'d New _Window",
+ "Browse in %'d New _Windows",
+ selection_count),
+ selection_count);
+ }
+ show_open_folder_window = show_open_alternate;
+ }
+ } else {
+ label_with_underscore = g_strdup (ngettext ("_Browse Folder",
+ "_Browse Folders",
+ selection_count));
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OPEN_ALTERNATE);
+ g_object_set (action, "label",
+ label_with_underscore,
+ NULL);
+ g_free (label_with_underscore);
+
+ gtk_action_set_sensitive (action, selection_count != 0);
+ gtk_action_set_visible (action, show_open_alternate);
+
+ /* Open in New Tab action */
+ if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
+
+ if (eel_preferences_get_boolean (CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
+ if (selection_count == 0 || selection_count == 1) {
+ label_with_underscore = g_strdup (_("Open in New _Tab"));
+ } else {
+ label_with_underscore = g_strdup_printf (ngettext("Open in %'d New _Tab",
+ "Open in %'d New _Tabs",
+ selection_count),
+ selection_count);
+ }
+ } else {
+ if (selection_count == 0 || selection_count == 1) {
+ label_with_underscore = g_strdup (_("Browse in New _Tab"));
+ } else {
+ label_with_underscore = g_strdup_printf (ngettext("Browse in %'d New _Tab",
+ "Browse in %'d New _Tabs",
+ selection_count),
+ selection_count);
+ }
+ }
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OPEN_IN_NEW_TAB);
+ gtk_action_set_sensitive (action, selection_count != 0);
+ gtk_action_set_visible (action, show_open_alternate);
+ g_object_set (action, "label",
+ label_with_underscore,
+ NULL);
+ g_free (label_with_underscore);
+ } else {
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OPEN_IN_NEW_TAB);
+ gtk_action_set_visible (action, FALSE);
+ }
+
+ /* next pane actions, only in navigation mode */
+ if (caja_window_info_get_window_type (view->details->window) != CAJA_WINDOW_NAVIGATION) {
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_COPY_TO_NEXT_PANE);
+ gtk_action_set_visible (action, FALSE);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_MOVE_TO_NEXT_PANE);
+ gtk_action_set_visible (action, FALSE);
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_OPEN_FOLDER_WINDOW);
+ gtk_action_set_visible (action, show_open_folder_window);
+
+ /* Broken into its own function just for convenience */
+ reset_open_with_menu (view, selection);
+ reset_extension_actions_menu (view, selection);
+
+ if (all_selected_items_in_trash (view)) {
+ label = _("_Delete Permanently");
+ tip = _("Delete all selected items permanently");
+ show_separate_delete_command = FALSE;
+ } else {
+ label = _("Mo_ve to Trash");
+ tip = _("Move each selected item to the Trash");
+ show_separate_delete_command = show_delete_command_auto_value;
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_TRASH);
+ g_object_set (action,
+ "label", label,
+ "tooltip", tip,
+ "icon-name", all_selected_items_in_trash (view) ?
+ CAJA_ICON_DELETE : CAJA_ICON_TRASH_FULL,
+ NULL);
+ gtk_action_set_sensitive (action, can_delete_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_DELETE);
+ gtk_action_set_visible (action, show_separate_delete_command);
+
+ if (show_separate_delete_command) {
+ g_object_set (action,
+ "label", _("_Delete"),
+ "icon-name", CAJA_ICON_DELETE,
+ NULL);
+ }
+ gtk_action_set_sensitive (action, can_delete_files);
+
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_RESTORE_FROM_TRASH);
+ update_restore_from_trash_action (action, selection, FALSE);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_DUPLICATE);
+ gtk_action_set_sensitive (action, can_duplicate_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_CREATE_LINK);
+ gtk_action_set_sensitive (action, can_link_files);
+ g_object_set (action, "label",
+ ngettext ("Ma_ke Link",
+ "Ma_ke Links",
+ selection_count),
+ NULL);
+
+ show_properties = (!FM_IS_DESKTOP_ICON_VIEW (view) || selection_count > 0) &&
+ fm_directory_view_supports_properties (view);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PROPERTIES);
+
+ gtk_action_set_sensitive (action, show_properties);
+
+ if (selection_count == 0) {
+ gtk_action_set_tooltip (action, _("View or modify the properties of the open folder"));
+ } else {
+ gtk_action_set_tooltip (action, _("View or modify the properties of each selected item"));
+ }
+
+ gtk_action_set_visible (action, show_properties);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_PROPERTIES_ACCEL);
+
+ gtk_action_set_sensitive (action, show_properties);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_EMPTY_TRASH);
+ g_object_set (action,
+ "label", _("E_mpty Trash"),
+ NULL);
+ gtk_action_set_sensitive (action, !caja_trash_monitor_is_empty ());
+ gtk_action_set_visible (action, should_show_empty_trash (view));
+
+ show_save_search = FALSE;
+ save_search_sensitive = FALSE;
+ show_save_search_as = FALSE;
+ if (view->details->model &&
+ CAJA_IS_SEARCH_DIRECTORY (view->details->model)) {
+ CajaSearchDirectory *search;
+
+ search = CAJA_SEARCH_DIRECTORY (view->details->model);
+ if (caja_search_directory_is_saved_search (search)) {
+ show_save_search = TRUE;
+ save_search_sensitive = caja_search_directory_is_modified (search);
+ } else {
+ show_save_search_as = TRUE;
+ }
+ }
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SAVE_SEARCH);
+ gtk_action_set_visible (action, show_save_search);
+ gtk_action_set_sensitive (action, save_search_sensitive);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SAVE_SEARCH_AS);
+ gtk_action_set_visible (action, show_save_search_as);
+
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELECT_ALL);
+ gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_SELECT_PATTERN);
+ gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_INVERT_SELECTION);
+ gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_CUT);
+ gtk_action_set_sensitive (action, can_delete_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_COPY);
+ gtk_action_set_sensitive (action, can_copy_files);
+
+ real_update_paste_menu (view, selection, selection_count);
+
+ disable_command_line = eel_preferences_get_boolean (CAJA_PREFERENCES_LOCKDOWN_COMMAND_LINE);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_NEW_LAUNCHER);
+ gtk_action_set_visible (action, vfolder_directory && !disable_command_line);
+ gtk_action_set_sensitive (action, can_create_files);
+
+ real_update_menus_volumes (view, selection, selection_count);
+
+ caja_file_list_free (selection);
+
+ if (view->details->scripts_invalid) {
+ update_scripts_menu (view);
+ }
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_NEW_DOCUMENTS);
+ gtk_action_set_sensitive (action, can_create_files);
+
+ if (can_create_files && view->details->templates_invalid) {
+ update_templates_menu (view);
+ }
+
+ next_pane_is_writable = has_writable_extra_pane (view);
+
+ /* next pane: works if file is copyable, and next pane is writable */
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_COPY_TO_NEXT_PANE);
+ gtk_action_set_sensitive (action, can_copy_files && next_pane_is_writable);
+
+ /* move to next pane: works if file is cuttable, and next pane is writable */
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_MOVE_TO_NEXT_PANE);
+ gtk_action_set_sensitive (action, can_delete_files && next_pane_is_writable);
+
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_COPY_TO_HOME);
+ gtk_action_set_sensitive (action, can_copy_files);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_COPY_TO_DESKTOP);
+ gtk_action_set_sensitive (action, can_copy_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_MOVE_TO_HOME);
+ gtk_action_set_sensitive (action, can_delete_files);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ FM_ACTION_MOVE_TO_DESKTOP);
+ gtk_action_set_sensitive (action, can_delete_files);
+
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ "CopyToMenu");
+ gtk_action_set_sensitive (action, can_copy_files);
+ action = gtk_action_group_get_action (view->details->dir_action_group,
+ "MoveToMenu");
+ gtk_action_set_sensitive (action, can_delete_files);
+}
+
+/**
+ * fm_directory_view_pop_up_selection_context_menu
+ *
+ * Pop up a context menu appropriate to the selected items.
+ * @view: FMDirectoryView of interest.
+ * @event: The event that triggered this context menu.
+ *
+ * Return value: CajaDirectory for this view.
+ *
+ **/
+void
+fm_directory_view_pop_up_selection_context_menu (FMDirectoryView *view,
+ GdkEventButton *event)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ /* Make the context menu items not flash as they update to proper disabled,
+ * etc. states by forcing menus to update now.
+ */
+ update_menus_if_pending (view);
+
+ update_context_menu_position_from_event (view, event);
+
+ eel_pop_up_context_menu (create_popup_menu
+ (view, FM_DIRECTORY_VIEW_POPUP_PATH_SELECTION),
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ event);
+}
+
+/**
+ * fm_directory_view_pop_up_background_context_menu
+ *
+ * Pop up a context menu appropriate to the view globally at the last right click location.
+ * @view: FMDirectoryView of interest.
+ *
+ * Return value: CajaDirectory for this view.
+ *
+ **/
+void
+fm_directory_view_pop_up_background_context_menu (FMDirectoryView *view,
+ GdkEventButton *event)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ /* Make the context menu items not flash as they update to proper disabled,
+ * etc. states by forcing menus to update now.
+ */
+ update_menus_if_pending (view);
+
+ update_context_menu_position_from_event (view, event);
+
+
+ eel_pop_up_context_menu (create_popup_menu
+ (view, FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND),
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ event);
+}
+
+static void
+real_pop_up_location_context_menu (FMDirectoryView *view)
+{
+ /* always update the menu before showing it. Shouldn't be too expensive. */
+ real_update_location_menu (view);
+
+ update_context_menu_position_from_event (view, view->details->location_popup_event);
+
+ eel_pop_up_context_menu (create_popup_menu
+ (view, FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION),
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
+ view->details->location_popup_event);
+}
+
+static void
+location_popup_file_attributes_ready (CajaFile *file,
+ gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ g_assert (file == view->details->location_popup_directory_as_file);
+
+ real_pop_up_location_context_menu (view);
+}
+
+static void
+unschedule_pop_up_location_context_menu (FMDirectoryView *view)
+{
+ if (view->details->location_popup_directory_as_file != NULL) {
+ g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
+ caja_file_cancel_call_when_ready (view->details->location_popup_directory_as_file,
+ location_popup_file_attributes_ready,
+ view);
+ caja_file_unref (view->details->location_popup_directory_as_file);
+ view->details->location_popup_directory_as_file = NULL;
+ }
+}
+
+static void
+schedule_pop_up_location_context_menu (FMDirectoryView *view,
+ GdkEventButton *event,
+ CajaFile *file)
+{
+ g_assert (CAJA_IS_FILE (file));
+
+ if (view->details->location_popup_event != NULL) {
+ gdk_event_free ((GdkEvent *) view->details->location_popup_event);
+ }
+ view->details->location_popup_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *)event);
+
+ if (file == view->details->location_popup_directory_as_file) {
+ if (caja_file_check_if_ready (file, CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO)) {
+ real_pop_up_location_context_menu (view);
+ }
+ } else {
+ unschedule_pop_up_location_context_menu (view);
+
+ view->details->location_popup_directory_as_file = caja_file_ref (file);
+ caja_file_call_when_ready (view->details->location_popup_directory_as_file,
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO,
+ location_popup_file_attributes_ready,
+ view);
+ }
+}
+
+/**
+ * fm_directory_view_pop_up_location_context_menu
+ *
+ * Pop up a context menu appropriate to the view globally.
+ * @view: FMDirectoryView of interest.
+ * @event: GdkEventButton triggering the popup.
+ * @location: The location the popup-menu should be created for,
+ * or NULL for the currently displayed location.
+ *
+ **/
+void
+fm_directory_view_pop_up_location_context_menu (FMDirectoryView *view,
+ GdkEventButton *event,
+ const char *location)
+{
+ CajaFile *file;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ if (location != NULL) {
+ file = caja_file_get_by_uri (location);
+ } else {
+ file = caja_file_ref (view->details->directory_as_file);
+ }
+
+ if (file != NULL) {
+ schedule_pop_up_location_context_menu (view, event, file);
+ caja_file_unref (file);
+ }
+}
+
+static void
+fm_directory_view_drop_proxy_received_uris (FMDirectoryView *view,
+ const GList *source_uri_list,
+ const char *target_uri,
+ GdkDragAction action)
+{
+ char *container_uri;
+
+ container_uri = NULL;
+ if (target_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (view);
+ g_assert (container_uri != NULL);
+ }
+
+ if (action == GDK_ACTION_ASK) {
+ action = caja_drag_drop_action_ask
+ (GTK_WIDGET (view),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+ if (action == 0) {
+ return;
+ }
+ }
+
+ caja_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
+ source_uri_list,
+ fm_directory_view_get_copied_files_atom (view));
+
+ fm_directory_view_move_copy_items (source_uri_list, NULL,
+ target_uri != NULL ? target_uri : container_uri,
+ action, 0, 0, view);
+
+ g_free (container_uri);
+}
+
+static void
+fm_directory_view_drop_proxy_received_netscape_url (FMDirectoryView *view,
+ const char *netscape_url,
+ const char *target_uri,
+ GdkDragAction action)
+{
+ fm_directory_view_handle_netscape_url_drop (view,
+ netscape_url,
+ target_uri,
+ action, 0, 0);
+}
+
+static void
+schedule_update_menus (FMDirectoryView *view)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ /* Don't schedule updates after destroy (#349551),
+ * or if we are not active.
+ */
+ if (view->details->window == NULL ||
+ !view->details->active) {
+ return;
+ }
+
+ view->details->menu_states_untrustworthy = TRUE;
+
+ /* Schedule a menu update with the current update interval */
+ if (view->details->update_menus_timeout_id == 0) {
+ view->details->update_menus_timeout_id
+ = g_timeout_add (view->details->update_interval, update_menus_timeout_callback, view);
+ }
+}
+
+static void
+remove_update_status_idle_callback (FMDirectoryView *view)
+{
+ if (view->details->update_status_idle_id != 0) {
+ g_source_remove (view->details->update_status_idle_id);
+ view->details->update_status_idle_id = 0;
+ }
+}
+
+static gboolean
+update_status_idle_callback (gpointer data)
+{
+ FMDirectoryView *view;
+
+ view = FM_DIRECTORY_VIEW (data);
+ fm_directory_view_display_selection_info (view);
+ view->details->update_status_idle_id = 0;
+ return FALSE;
+}
+
+static void
+schedule_update_status (FMDirectoryView *view)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ /* Make sure we haven't already destroyed it */
+ if (view->details->window == NULL) {
+ return;
+ }
+
+ if (view->details->loading) {
+ /* Don't update status bar while loading the dir */
+ return;
+ }
+
+ if (view->details->update_status_idle_id == 0) {
+ view->details->update_status_idle_id =
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
+ update_status_idle_callback, view, NULL);
+ }
+}
+
+/**
+ * fm_directory_view_notify_selection_changed:
+ *
+ * Notify this view that the selection has changed. This is normally
+ * called only by subclasses.
+ * @view: FMDirectoryView whose selection has changed.
+ *
+ **/
+void
+fm_directory_view_notify_selection_changed (FMDirectoryView *view)
+{
+ GList *selection;
+ GtkWindow *window;
+
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (caja_debug_log_is_domain_enabled (CAJA_DEBUG_LOG_DOMAIN_USER)) {
+ selection = fm_directory_view_get_selection (view);
+
+ window = fm_directory_view_get_containing_window (view);
+ caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER, selection,
+ "selection changed in window %p",
+ window);
+ caja_file_list_free (selection);
+ }
+
+ view->details->selection_was_removed = FALSE;
+
+ if (!view->details->selection_change_is_due_to_shell) {
+ view->details->send_selection_change_to_shell = TRUE;
+ }
+
+ /* Schedule a display of the new selection. */
+ if (view->details->display_selection_idle_id == 0) {
+ view->details->display_selection_idle_id
+ = g_idle_add (display_selection_info_idle_callback,
+ view);
+ }
+
+ if (view->details->batching_selection_level != 0) {
+ view->details->selection_changed_while_batched = TRUE;
+ } else {
+ /* Here is the work we do only when we're not
+ * batching selection changes. In other words, it's the slower
+ * stuff that we don't want to slow down selection techniques
+ * such as rubberband-selecting in icon view.
+ */
+
+ /* Schedule an update of menu item states to match selection */
+ schedule_update_menus (view);
+ }
+}
+
+static void
+file_changed_callback (CajaFile *file, gpointer callback_data)
+{
+ FMDirectoryView *view = FM_DIRECTORY_VIEW (callback_data);
+
+ schedule_changes (view);
+
+ schedule_update_menus (view);
+ schedule_update_status (view);
+
+ /* We might have different capabilities, so we need to update
+ * relative icon emblems . (Writeable etc).
+ * Don't do this for trash, as it never changes writability
+ * but does change a lot for the file count attribute.
+ */
+ if (!caja_file_is_in_trash (file)) {
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view, emblems_changed, (view));
+ }
+}
+
+/**
+ * load_directory:
+ *
+ * Switch the displayed location to a new uri. If the uri is not valid,
+ * the location will not be switched; user feedback will be provided instead.
+ * @view: FMDirectoryView whose location will be changed.
+ * @uri: A string representing the uri to switch to.
+ *
+ **/
+static void
+load_directory (FMDirectoryView *view,
+ CajaDirectory *directory)
+{
+ CajaDirectory *old_directory;
+ CajaFile *old_file;
+ CajaFileAttributes attributes;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (CAJA_IS_DIRECTORY (directory));
+
+ fm_directory_view_stop (view);
+ fm_directory_view_clear (view);
+
+ view->details->loading = TRUE;
+
+ /* Update menus when directory is empty, before going to new
+ * location, so they won't have any false lingering knowledge
+ * of old selection.
+ */
+ schedule_update_menus (view);
+
+ while (view->details->subdirectory_list != NULL) {
+ fm_directory_view_remove_subdirectory (view,
+ view->details->subdirectory_list->data);
+ }
+
+ disconnect_model_handlers (view);
+
+ old_directory = view->details->model;
+ caja_directory_ref (directory);
+ view->details->model = directory;
+ caja_directory_unref (old_directory);
+
+ old_file = view->details->directory_as_file;
+ view->details->directory_as_file =
+ caja_directory_get_corresponding_file (directory);
+ caja_file_unref (old_file);
+
+ view->details->reported_load_error = FALSE;
+
+ /* FIXME bugzilla.gnome.org 45062: In theory, we also need to monitor metadata here (as
+ * well as doing a call when ready), in case external forces
+ * change the directory's file metadata.
+ */
+ attributes =
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO;
+ view->details->metadata_for_directory_as_file_pending = TRUE;
+ view->details->metadata_for_files_in_directory_pending = TRUE;
+ caja_file_call_when_ready
+ (view->details->directory_as_file,
+ attributes,
+ metadata_for_directory_as_file_ready_callback, view);
+ caja_directory_call_when_ready
+ (view->details->model,
+ attributes,
+ FALSE,
+ metadata_for_files_in_directory_ready_callback, view);
+
+ /* If capabilities change, then we need to update the menus
+ * because of New Folder, and relative emblems.
+ */
+ attributes =
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO;
+ caja_file_monitor_add (view->details->directory_as_file,
+ &view->details->directory_as_file,
+ attributes);
+
+ view->details->file_changed_handler_id = g_signal_connect
+ (view->details->directory_as_file, "changed",
+ G_CALLBACK (file_changed_callback), view);
+}
+
+static void
+finish_loading (FMDirectoryView *view)
+{
+ CajaFileAttributes attributes;
+
+ caja_window_info_report_load_underway (view->details->window,
+ CAJA_VIEW (view));
+
+ /* Tell interested parties that we've begun loading this directory now.
+ * Subclasses use this to know that the new metadata is now available.
+ */
+ fm_directory_view_begin_loading (view);
+
+ /* Assume we have now all information to show window */
+ caja_window_info_view_visible (view->details->window, CAJA_VIEW (view));
+
+ if (caja_directory_are_all_files_seen (view->details->model)) {
+ /* Unschedule a pending update and schedule a new one with the minimal
+ * update interval. This gives the view a short chance at gathering the
+ * (cached) deep counts.
+ */
+ unschedule_display_of_pending_files (view);
+ schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
+ }
+
+ /* Start loading. */
+
+ /* Connect handlers to learn about loading progress. */
+ view->details->done_loading_handler_id = g_signal_connect
+ (view->details->model, "done_loading",
+ G_CALLBACK (done_loading_callback), view);
+ view->details->load_error_handler_id = g_signal_connect
+ (view->details->model, "load_error",
+ G_CALLBACK (load_error_callback), view);
+
+ /* Monitor the things needed to get the right icon. Also
+ * monitor a directory's item count because the "size"
+ * attribute is based on that, and the file's metadata
+ * and possible custom name.
+ */
+ attributes =
+ CAJA_FILE_ATTRIBUTES_FOR_ICON |
+ CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
+ CAJA_FILE_ATTRIBUTE_INFO |
+ CAJA_FILE_ATTRIBUTE_LINK_INFO |
+ CAJA_FILE_ATTRIBUTE_MOUNT |
+ CAJA_FILE_ATTRIBUTE_EXTENSION_INFO;
+
+ caja_directory_file_monitor_add (view->details->model,
+ &view->details->model,
+ view->details->show_hidden_files,
+ view->details->show_backup_files,
+ attributes,
+ files_added_callback, view);
+
+ view->details->files_added_handler_id = g_signal_connect
+ (view->details->model, "files_added",
+ G_CALLBACK (files_added_callback), view);
+ view->details->files_changed_handler_id = g_signal_connect
+ (view->details->model, "files_changed",
+ G_CALLBACK (files_changed_callback), view);
+}
+
+static void
+finish_loading_if_all_metadata_loaded (FMDirectoryView *view)
+{
+ if (!view->details->metadata_for_directory_as_file_pending &&
+ !view->details->metadata_for_files_in_directory_pending) {
+ finish_loading (view);
+ }
+}
+
+static void
+metadata_for_directory_as_file_ready_callback (CajaFile *file,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = callback_data;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (view->details->directory_as_file == file);
+ g_assert (view->details->metadata_for_directory_as_file_pending);
+
+ view->details->metadata_for_directory_as_file_pending = FALSE;
+
+ finish_loading_if_all_metadata_loaded (view);
+}
+
+static void
+metadata_for_files_in_directory_ready_callback (CajaDirectory *directory,
+ GList *files,
+ gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = callback_data;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ g_assert (view->details->model == directory);
+ g_assert (view->details->metadata_for_files_in_directory_pending);
+
+ view->details->metadata_for_files_in_directory_pending = FALSE;
+
+ finish_loading_if_all_metadata_loaded (view);
+}
+
+char **
+fm_directory_view_get_emblem_names_to_exclude (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_emblem_names_to_exclude, (view));
+}
+
+static char **
+real_get_emblem_names_to_exclude (FMDirectoryView *view)
+{
+ char **excludes;
+ int i;
+
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ excludes = g_new (char *, 3);
+
+ i = 0;
+ excludes[i++] = g_strdup (CAJA_FILE_EMBLEM_NAME_TRASH);
+
+ if (!caja_file_can_write (view->details->directory_as_file)) {
+ excludes[i++] = g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_WRITE);
+ }
+
+ excludes[i++] = NULL;
+
+ return excludes;
+}
+
+/**
+ * fm_directory_view_merge_menus:
+ *
+ * Add this view's menus to the window's menu bar.
+ * @view: FMDirectoryView in question.
+ */
+static void
+fm_directory_view_merge_menus (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ merge_menus, (view));
+}
+
+static void
+fm_directory_view_unmerge_menus (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ unmerge_menus, (view));
+}
+
+static void
+disconnect_handler (GObject *object, int *id)
+{
+ if (*id != 0) {
+ g_signal_handler_disconnect (object, *id);
+ *id = 0;
+ }
+}
+
+static void
+disconnect_directory_handler (FMDirectoryView *view, int *id)
+{
+ disconnect_handler (G_OBJECT (view->details->model), id);
+}
+
+static void
+disconnect_directory_as_file_handler (FMDirectoryView *view, int *id)
+{
+ disconnect_handler (G_OBJECT (view->details->directory_as_file), id);
+}
+
+static void
+disconnect_model_handlers (FMDirectoryView *view)
+{
+ if (view->details->model == NULL) {
+ return;
+ }
+ disconnect_directory_handler (view, &view->details->files_added_handler_id);
+ disconnect_directory_handler (view, &view->details->files_changed_handler_id);
+ disconnect_directory_handler (view, &view->details->done_loading_handler_id);
+ disconnect_directory_handler (view, &view->details->load_error_handler_id);
+ disconnect_directory_as_file_handler (view, &view->details->file_changed_handler_id);
+ caja_file_cancel_call_when_ready (view->details->directory_as_file,
+ metadata_for_directory_as_file_ready_callback,
+ view);
+ caja_directory_cancel_callback (view->details->model,
+ metadata_for_files_in_directory_ready_callback,
+ view);
+ caja_directory_file_monitor_remove (view->details->model,
+ &view->details->model);
+ caja_file_monitor_remove (view->details->directory_as_file,
+ &view->details->directory_as_file);
+}
+
+/**
+ * fm_directory_view_reset_to_defaults:
+ *
+ * set sorting order, zoom level, etc. to match defaults
+ *
+ **/
+void
+fm_directory_view_reset_to_defaults (FMDirectoryView *view)
+{
+ CajaWindowShowHiddenFilesMode mode;
+
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ reset_to_defaults, (view));
+ mode = caja_window_info_get_hidden_files_mode (view->details->window);
+ if (mode != CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT) {
+ caja_window_info_set_hidden_files_mode (view->details->window,
+ CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT);
+ }
+}
+
+/**
+ * fm_directory_view_select_all:
+ *
+ * select all the items in the view
+ *
+ **/
+void
+fm_directory_view_select_all (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ select_all, (view));
+}
+
+/**
+ * fm_directory_view_set_selection:
+ *
+ * set the selection to the items identified in @selection. @selection
+ * should be a list of CajaFiles
+ *
+ **/
+void
+fm_directory_view_set_selection (FMDirectoryView *view, GList *selection)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ set_selection, (view, selection));
+}
+
+static void
+fm_directory_view_select_file (FMDirectoryView *view, CajaFile *file)
+{
+ GList file_list;
+
+ file_list.data = file;
+ file_list.next = NULL;
+ file_list.prev = NULL;
+ fm_directory_view_set_selection (view, &file_list);
+}
+
+/**
+ * fm_directory_view_get_selected_icon_locations:
+ *
+ * return an array of locations of selected icons if available
+ * Return value: GArray of GdkPoints
+ *
+ **/
+GArray *
+fm_directory_view_get_selected_icon_locations (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ get_selected_icon_locations, (view));
+}
+
+/**
+ * fm_directory_view_reveal_selection:
+ *
+ * Scroll as necessary to reveal the selected items.
+ **/
+void
+fm_directory_view_reveal_selection (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ reveal_selection, (view));
+}
+
+static gboolean
+remove_all (gpointer key, gpointer value, gpointer callback_data)
+{
+ return TRUE;
+}
+
+/**
+ * fm_directory_view_stop:
+ *
+ * Stop the current ongoing process, such as switching to a new uri.
+ * @view: FMDirectoryView in question.
+ *
+ **/
+void
+fm_directory_view_stop (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ unschedule_display_of_pending_files (view);
+ reset_update_interval (view);
+
+ /* Free extra undisplayed files */
+ file_and_directory_list_free (view->details->new_added_files);
+ view->details->new_added_files = NULL;
+ file_and_directory_list_free (view->details->new_changed_files);
+ view->details->new_changed_files = NULL;
+ g_hash_table_foreach_remove (view->details->non_ready_files, remove_all, NULL);
+ file_and_directory_list_free (view->details->old_added_files);
+ view->details->old_added_files = NULL;
+ file_and_directory_list_free (view->details->old_changed_files);
+ view->details->old_changed_files = NULL;
+ eel_g_object_list_free (view->details->pending_locations_selected);
+ view->details->pending_locations_selected = NULL;
+
+ if (view->details->model != NULL) {
+ caja_directory_file_monitor_remove (view->details->model, view);
+ }
+ done_loading (view, FALSE);
+}
+
+gboolean
+fm_directory_view_is_read_only (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ is_read_only, (view));
+}
+
+gboolean
+fm_directory_view_is_empty (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ is_empty, (view));
+}
+
+gboolean
+fm_directory_view_is_editable (FMDirectoryView *view)
+{
+ CajaDirectory *directory;
+
+ directory = fm_directory_view_get_model (view);
+
+ if (directory != NULL) {
+ return caja_directory_is_editable (directory);
+ }
+
+ return TRUE;
+}
+
+void
+fm_directory_view_set_initiated_unmount (FMDirectoryView *view,
+ gboolean initiated_unmount)
+{
+ if (view->details->window != NULL) {
+ caja_window_info_set_initiated_unmount(view->details->window,
+ initiated_unmount);
+ }
+}
+
+static gboolean
+real_is_read_only (FMDirectoryView *view)
+{
+ CajaFile *file;
+
+ if (!fm_directory_view_is_editable (view)) {
+ return TRUE;
+ }
+
+ file = fm_directory_view_get_directory_as_file (view);
+ if (file != NULL) {
+ return !caja_file_can_write (file);
+ }
+ return FALSE;
+}
+
+gboolean
+fm_directory_view_supports_creating_files (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ supports_creating_files, (view));
+}
+
+gboolean
+fm_directory_view_accepts_dragged_files (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ accepts_dragged_files, (view));
+}
+
+/**
+ * fm_directory_view_should_show_file
+ *
+ * Returns whether or not this file should be displayed based on
+ * current filtering options.
+ */
+gboolean
+fm_directory_view_should_show_file (FMDirectoryView *view, CajaFile *file)
+{
+ return caja_file_should_show (file,
+ view->details->show_hidden_files,
+ view->details->show_backup_files,
+ view->details->show_foreign_files);
+}
+
+static gboolean
+real_supports_creating_files (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return !fm_directory_view_is_read_only (view) && !showing_trash_directory (view);
+}
+
+static gboolean
+real_accepts_dragged_files (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return !fm_directory_view_is_read_only (view);
+}
+
+gboolean
+fm_directory_view_supports_properties (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ supports_properties, (view));
+}
+
+static gboolean
+real_supports_properties (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return TRUE;
+}
+
+gboolean
+fm_directory_view_supports_zooming (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ supports_zooming, (view));
+}
+
+static gboolean
+real_supports_zooming (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return TRUE;
+}
+
+gboolean
+fm_directory_view_using_manual_layout (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return EEL_CALL_METHOD_WITH_RETURN_VALUE
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ using_manual_layout, (view));
+}
+
+static gboolean
+real_using_manual_layout (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return FALSE;
+}
+
+/**
+ * fm_directory_view_update_menus:
+ *
+ * Update the sensitivity and wording of dynamic menu items.
+ * @view: FMDirectoryView in question.
+ */
+void
+fm_directory_view_update_menus (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ if (!view->details->active) {
+ return;
+ }
+
+
+ EEL_CALL_METHOD
+ (FM_DIRECTORY_VIEW_CLASS, view,
+ update_menus, (view));
+
+ view->details->menu_states_untrustworthy = FALSE;
+}
+
+static void
+schedule_update_menus_callback (gpointer callback_data)
+{
+ schedule_update_menus (FM_DIRECTORY_VIEW (callback_data));
+}
+
+void
+fm_directory_view_ignore_hidden_file_preferences (FMDirectoryView *view)
+{
+ g_return_if_fail (view->details->model == NULL);
+
+ if (view->details->ignore_hidden_file_preferences) {
+ return;
+ }
+
+ view->details->show_hidden_files = FALSE;
+ view->details->show_backup_files = FALSE;
+ view->details->ignore_hidden_file_preferences = TRUE;
+}
+
+void
+fm_directory_view_set_show_foreign (FMDirectoryView *view,
+ gboolean show_foreign)
+{
+ view->details->show_foreign_files = show_foreign;
+}
+
+char *
+fm_directory_view_get_uri (FMDirectoryView *view)
+{
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+ if (view->details->model == NULL) {
+ return NULL;
+ }
+ return caja_directory_get_uri (view->details->model);
+}
+
+/* Get the real directory where files will be stored and created */
+char *
+fm_directory_view_get_backing_uri (FMDirectoryView *view)
+{
+ CajaDirectory *directory;
+ char *uri;
+
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
+
+ if (view->details->model == NULL) {
+ return NULL;
+ }
+
+ directory = view->details->model;
+
+ if (CAJA_IS_DESKTOP_DIRECTORY (directory)) {
+ directory = caja_desktop_directory_get_real_directory (CAJA_DESKTOP_DIRECTORY (directory));
+ } else {
+ caja_directory_ref (directory);
+ }
+
+ uri = caja_directory_get_uri (directory);
+
+ caja_directory_unref (directory);
+
+ return uri;
+}
+
+void
+fm_directory_view_move_copy_items (const GList *item_uris,
+ GArray *relative_item_points,
+ const char *target_uri,
+ int copy_action,
+ int x, int y,
+ FMDirectoryView *view)
+{
+ CajaFile *target_file;
+
+ g_assert (relative_item_points == NULL
+ || relative_item_points->len == 0
+ || g_list_length ((GList *)item_uris) == relative_item_points->len);
+
+ /* add the drop location to the icon offsets */
+ offset_drop_points (relative_item_points, x, y);
+
+ target_file = caja_file_get_existing_by_uri (target_uri);
+ /* special-case "command:" here instead of starting a move/copy */
+ if (target_file != NULL && caja_file_is_launcher (target_file)) {
+ caja_file_unref (target_file);
+ caja_launch_desktop_file (
+ gtk_widget_get_screen (GTK_WIDGET (view)),
+ target_uri, item_uris,
+ fm_directory_view_get_containing_window (view));
+ return;
+ } else if (copy_action == GDK_ACTION_COPY &&
+ caja_is_file_roller_installed () &&
+ target_file != NULL &&
+ caja_file_is_archive (target_file)) {
+ char *command, *quoted_uri, *tmp;
+ const GList *l;
+ GdkScreen *screen;
+
+ /* Handle dropping onto a file-roller archiver file, instead of starting a move/copy */
+
+ caja_file_unref (target_file);
+
+ quoted_uri = g_shell_quote (target_uri);
+ command = g_strconcat ("file-roller -a ", quoted_uri, NULL);
+ g_free (quoted_uri);
+
+ for (l = item_uris; l != NULL; l = l->next) {
+ quoted_uri = g_shell_quote ((char *) l->data);
+
+ tmp = g_strconcat (command, " ", quoted_uri, NULL);
+ g_free (command);
+ command = tmp;
+
+ g_free (quoted_uri);
+ }
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (view));
+ if (screen == NULL) {
+ screen = gdk_screen_get_default ();
+ }
+ gdk_spawn_command_line_on_screen (screen, command, NULL);
+ g_free (command);
+
+ return;
+ }
+ caja_file_unref (target_file);
+
+ caja_file_operations_copy_move
+ (item_uris, relative_item_points,
+ target_uri, copy_action, GTK_WIDGET (view),
+ copy_move_done_callback, pre_copy_move (view));
+}
+
+gboolean
+fm_directory_view_can_accept_item (CajaFile *target_item,
+ const char *item_uri,
+ FMDirectoryView *view)
+{
+ g_return_val_if_fail (CAJA_IS_FILE (target_item), FALSE);
+ g_return_val_if_fail (item_uri != NULL, FALSE);
+ g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
+
+ return caja_drag_can_accept_item (target_item, item_uri);
+}
+
+static void
+fm_directory_view_trash_state_changed_callback (CajaTrashMonitor *trash_monitor,
+ gboolean state, gpointer callback_data)
+{
+ FMDirectoryView *view;
+
+ view = (FMDirectoryView *) callback_data;
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+
+ schedule_update_menus (view);
+}
+
+void
+fm_directory_view_start_batching_selection_changes (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+
+ ++view->details->batching_selection_level;
+ view->details->selection_changed_while_batched = FALSE;
+}
+
+void
+fm_directory_view_stop_batching_selection_changes (FMDirectoryView *view)
+{
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
+ g_return_if_fail (view->details->batching_selection_level > 0);
+
+ if (--view->details->batching_selection_level == 0) {
+ if (view->details->selection_changed_while_batched) {
+ fm_directory_view_notify_selection_changed (view);
+ }
+ }
+}
+
+static void
+revert_slashes (char *string)
+{
+ while (*string != 0) {
+ if (*string == '/') {
+ *string = '\\';
+ }
+ string++;
+ }
+}
+
+
+static GdkDragAction
+ask_link_action (FMDirectoryView *view)
+{
+ int button_pressed;
+ GdkDragAction result;
+ GtkWindow *parent_window;
+ GtkWidget *dialog;
+
+ parent_window = NULL;
+
+ /* Don't use desktop window as parent, since that means
+ we show up an all desktops etc */
+ if (! FM_IS_DESKTOP_ICON_VIEW (view)) {
+ parent_window = GTK_WINDOW (fm_directory_view_get_containing_window (view));
+ }
+
+ dialog = gtk_message_dialog_new (parent_window,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("Download location?"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("You can download it or make a link to it."));
+
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ _("Make a _Link"), 0);
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ GTK_STOCK_CANCEL, 1);
+ gtk_dialog_add_button (GTK_DIALOG (dialog),
+ _("_Download"), 2);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), ""); /* as per HIG */
+ gtk_window_set_focus_on_map (GTK_WINDOW (dialog), TRUE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), 2);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ button_pressed = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+
+ switch (button_pressed) {
+ case 0:
+ result = GDK_ACTION_LINK;
+ break;
+ case 1:
+ case GTK_RESPONSE_DELETE_EVENT:
+ result = 0;
+ break;
+ case 2:
+ result = GDK_ACTION_COPY;
+ break;
+ default:
+ g_assert_not_reached ();
+ result = 0;
+ }
+
+ return result;
+}
+
+typedef struct {
+ FMDirectoryView *view;
+ GCancellable *cancellable;
+ char *encoded_url;
+ char *target_uri;
+ int x;
+ int y;
+ guint timeout;
+} NetscapeUrlDropAsk;
+
+static void
+handle_netscape_url_drop_ask_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NetscapeUrlDropAsk *data;
+ GdkDragAction action;
+ GFileInfo *info;
+ GFile *f;
+ const char *mime_type;
+
+ data = user_data;
+ f = G_FILE (source_object);
+
+ info = g_file_query_info_finish (f, res, NULL);
+ mime_type = NULL;
+
+ if (info) {
+ mime_type = g_file_info_get_content_type (info);
+ }
+
+ if (mime_type != NULL &&
+ (g_content_type_equals (mime_type, "text/html") ||
+ g_content_type_equals (mime_type, "text/xml") ||
+ g_content_type_equals (mime_type, "application/xhtml+xml"))) {
+ action = GDK_ACTION_LINK;
+ } else if (mime_type != NULL &&
+ g_content_type_equals (mime_type, "text/plain")) {
+ action = ask_link_action (data->view);
+ } else {
+ action = GDK_ACTION_COPY;
+ }
+ if (info) {
+ g_object_unref (info);
+ }
+
+ if (action != 0) {
+ fm_directory_view_handle_netscape_url_drop (data->view,
+ data->encoded_url,
+ data->target_uri,
+ action,
+ data->x, data->y);
+ }
+
+ g_object_unref (data->view);
+ g_object_unref (data->cancellable);
+ if (data->timeout != 0) {
+ g_source_remove (data->timeout);
+ }
+ g_free (data->encoded_url);
+ g_free (data->target_uri);
+ g_free (data);
+}
+
+static gboolean
+handle_netscape_url_drop_timeout (gpointer user_data)
+{
+ NetscapeUrlDropAsk *data;
+
+ data = user_data;
+
+ g_cancellable_cancel (data->cancellable);
+ data->timeout = 0;
+
+ return FALSE;
+}
+
+static inline void
+fm_directory_view_widget_to_file_operation_position (FMDirectoryView *view,
+ GdkPoint *position)
+{
+ EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
+ widget_to_file_operation_position,
+ (view, position));
+}
+
+static void
+fm_directory_view_widget_to_file_operation_position_xy (FMDirectoryView *view,
+ int *x, int *y)
+{
+ GdkPoint position;
+
+ position.x = *x;
+ position.y = *y;
+ fm_directory_view_widget_to_file_operation_position (view, &position);
+ *x = position.x;
+ *y = position.y;
+}
+
+void
+fm_directory_view_handle_netscape_url_drop (FMDirectoryView *view,
+ const char *encoded_url,
+ const char *target_uri,
+ GdkDragAction action,
+ int x,
+ int y)
+{
+ GdkPoint point;
+ GdkScreen *screen;
+ int screen_num;
+ char *url, *title;
+ char *link_name, *link_display_name;
+ char *container_uri;
+ GArray *points;
+ char **bits;
+ GList *uri_list = NULL;
+ GFile *f;
+
+ if (encoded_url == NULL) {
+ return;
+ }
+
+ container_uri = NULL;
+ if (target_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (view);
+ g_assert (container_uri != NULL);
+ }
+
+ f = g_file_new_for_uri (target_uri != NULL ? target_uri : container_uri);
+ if (!g_file_is_native (f)) {
+ eel_show_warning_dialog (_("Drag and drop is not supported."),
+ _("Drag and drop is only supported on local file systems."),
+ fm_directory_view_get_containing_window (view));
+ g_object_unref (f);
+ g_free (container_uri);
+ return;
+ }
+ g_object_unref (f);
+
+ /* _NETSCAPE_URL_ works like this: $URL\n$TITLE */
+ bits = g_strsplit (encoded_url, "\n", 0);
+ switch (g_strv_length (bits)) {
+ case 0:
+ g_strfreev (bits);
+ g_free (container_uri);
+ return;
+ case 1:
+ url = bits[0];
+ title = NULL;
+ break;
+ default:
+ url = bits[0];
+ title = bits[1];
+ }
+
+ if (action == GDK_ACTION_ASK) {
+ NetscapeUrlDropAsk *data;
+
+ f = g_file_new_for_uri (url);
+ data = g_new0 (NetscapeUrlDropAsk, 1);
+ data->view = g_object_ref (view);
+ data->cancellable = g_cancellable_new ();
+ data->encoded_url = g_strdup (encoded_url);
+ data->target_uri = g_strdup (target_uri);
+ data->x = x;
+ data->y = y;
+ /* Ensure we wait at most 1 second for mimetype */
+ data->timeout = g_timeout_add (1000,
+ handle_netscape_url_drop_timeout,
+ data);
+ g_file_query_info_async (f,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0,
+ 0, data->cancellable,
+ handle_netscape_url_drop_ask_cb,
+ data);
+
+ g_free (container_uri);
+ return;
+ }
+
+ fm_directory_view_widget_to_file_operation_position_xy (view, &x, &y);
+
+ /* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
+ * and we don't support combinations either. */
+ if ((action != GDK_ACTION_DEFAULT) &&
+ (action != GDK_ACTION_COPY) &&
+ (action != GDK_ACTION_MOVE) &&
+ (action != GDK_ACTION_LINK)) {
+ eel_show_warning_dialog (_("Drag and drop is not supported."),
+ _("An invalid drag type was used."),
+ fm_directory_view_get_containing_window (view));
+ g_free (container_uri);
+ return;
+ }
+
+ if (action == GDK_ACTION_LINK) {
+ if (eel_str_is_empty (title)) {
+ GFile *f;
+
+ f = g_file_new_for_uri (url);
+ link_name = g_file_get_basename (f);
+ g_object_unref (f);
+ } else {
+ link_name = g_strdup (title);
+ }
+
+ if (!eel_str_is_empty (link_name)) {
+ link_display_name = g_strdup_printf (_("Link to %s"), link_name);
+
+ /* The filename can't contain slashes, strip em.
+ (the basename of http://foo/ is http://foo/) */
+ revert_slashes (link_name);
+
+ point.x = x;
+ point.y = y;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (view));
+ screen_num = gdk_screen_get_number (screen);
+
+ caja_link_local_create (target_uri != NULL ? target_uri : container_uri,
+ link_name,
+ link_display_name,
+ "mate-fs-bookmark",
+ url,
+ &point,
+ screen_num,
+ TRUE);
+
+ g_free (link_display_name);
+ }
+ g_free (link_name);
+ } else {
+ GdkPoint tmp_point = { 0, 0 };
+
+ /* pass in a 1-item array of icon positions, relative to x, y */
+ points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
+ g_array_append_val (points, tmp_point);
+
+ uri_list = g_list_append (uri_list, url);
+
+ fm_directory_view_move_copy_items (uri_list, points,
+ target_uri != NULL ? target_uri : container_uri,
+ action, x, y, view);
+
+ g_list_free (uri_list);
+ g_array_free (points, TRUE);
+ }
+
+ g_strfreev (bits);
+
+ g_free (container_uri);
+}
+
+void
+fm_directory_view_handle_uri_list_drop (FMDirectoryView *view,
+ const char *item_uris,
+ const char *target_uri,
+ GdkDragAction action,
+ int x,
+ int y)
+{
+ gchar **uri_list;
+ GList *real_uri_list = NULL;
+ char *container_uri;
+ int n_uris, i;
+ GArray *points;
+
+ if (item_uris == NULL) {
+ return;
+ }
+
+ container_uri = NULL;
+ if (target_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (view);
+ g_assert (container_uri != NULL);
+ }
+
+ if (action == GDK_ACTION_ASK) {
+ action = caja_drag_drop_action_ask
+ (GTK_WIDGET (view),
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+ if (action == 0) {
+ g_free (container_uri);
+ return;
+ }
+ }
+
+ /* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
+ * and we don't support combinations either. */
+ if ((action != GDK_ACTION_DEFAULT) &&
+ (action != GDK_ACTION_COPY) &&
+ (action != GDK_ACTION_MOVE) &&
+ (action != GDK_ACTION_LINK)) {
+ eel_show_warning_dialog (_("Drag and drop is not supported."),
+ _("An invalid drag type was used."),
+ fm_directory_view_get_containing_window (view));
+ g_free (container_uri);
+ return;
+ }
+
+ n_uris = 0;
+ uri_list = g_uri_list_extract_uris (item_uris);
+ for (i = 0; uri_list[i] != NULL; i++) {
+ real_uri_list = g_list_append (real_uri_list, uri_list[i]);
+ n_uris++;
+ }
+ g_free (uri_list);
+
+ /* do nothing if no real uris are left */
+ if (n_uris == 0) {
+ g_free (container_uri);
+ return;
+ }
+
+ if (n_uris == 1) {
+ GdkPoint tmp_point = { 0, 0 };
+
+ /* pass in a 1-item array of icon positions, relative to x, y */
+ points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
+ g_array_append_val (points, tmp_point);
+ } else {
+ points = NULL;
+ }
+
+ fm_directory_view_widget_to_file_operation_position_xy (view, &x, &y);
+
+ fm_directory_view_move_copy_items (real_uri_list, points,
+ target_uri != NULL ? target_uri : container_uri,
+ action, x, y, view);
+
+ eel_g_list_free_deep (real_uri_list);
+
+ if (points != NULL)
+ g_array_free (points, TRUE);
+
+ g_free (container_uri);
+}
+
+void
+fm_directory_view_handle_text_drop (FMDirectoryView *view,
+ const char *text,
+ const char *target_uri,
+ GdkDragAction action,
+ int x,
+ int y)
+{
+ int length;
+ char *container_uri;
+ GdkPoint pos;
+
+ if (text == NULL) {
+ return;
+ }
+
+ g_return_if_fail (action == GDK_ACTION_COPY);
+
+ container_uri = NULL;
+ if (target_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (view);
+ g_assert (container_uri != NULL);
+ }
+
+ length = strlen (text);
+
+ pos.x = x;
+ pos.y = y;
+ fm_directory_view_widget_to_file_operation_position (view, &pos);
+
+ fm_directory_view_new_file_with_initial_contents (
+ view, target_uri != NULL ? target_uri : container_uri,
+ /* Translator: This is the filename used for when you dnd text to a directory */
+ _("dropped text.txt"),
+ text, length, &pos);
+
+ g_free (container_uri);
+}
+
+void
+fm_directory_view_handle_raw_drop (FMDirectoryView *view,
+ const char *raw_data,
+ int length,
+ const char *target_uri,
+ const char *direct_save_uri,
+ GdkDragAction action,
+ int x,
+ int y)
+{
+ char *container_uri, *filename;
+ GFile *direct_save_full;
+ GdkPoint pos;
+
+ if (raw_data == NULL) {
+ return;
+ }
+
+ g_return_if_fail (action == GDK_ACTION_COPY);
+
+ container_uri = NULL;
+ if (target_uri == NULL) {
+ container_uri = fm_directory_view_get_backing_uri (view);
+ g_assert (container_uri != NULL);
+ }
+
+ pos.x = x;
+ pos.y = y;
+ fm_directory_view_widget_to_file_operation_position (view, &pos);
+
+ filename = NULL;
+ if (direct_save_uri != NULL) {
+ direct_save_full = g_file_new_for_uri (direct_save_uri);
+ filename = g_file_get_basename (direct_save_full);
+ }
+ if (filename == NULL) {
+ /* Translator: This is the filename used for when you dnd raw
+ * data to a directory, if the source didn't supply a name.
+ */
+ filename = _("dropped data");
+ }
+
+ fm_directory_view_new_file_with_initial_contents (
+ view, target_uri != NULL ? target_uri : container_uri,
+ filename, raw_data, length, &pos);
+
+ g_free (container_uri);
+}
+
+gboolean
+fm_directory_view_get_active (FMDirectoryView *view)
+{
+ g_assert (FM_IS_DIRECTORY_VIEW (view));
+ return view->details->active;
+}
+
+static GArray *
+real_get_selected_icon_locations (FMDirectoryView *view)
+{
+ /* By default, just return an empty list. */
+ return g_array_new (FALSE, TRUE, sizeof (GdkPoint));
+}
+
+static void
+fm_directory_view_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FMDirectoryView *directory_view;
+ CajaWindowSlotInfo *slot;
+ CajaWindowInfo *window;
+
+ directory_view = FM_DIRECTORY_VIEW (object);
+
+ switch (prop_id) {
+ case PROP_WINDOW_SLOT:
+ g_assert (directory_view->details->slot == NULL);
+
+ slot = CAJA_WINDOW_SLOT_INFO (g_value_get_object (value));
+ window = caja_window_slot_info_get_window (slot);
+
+ directory_view->details->slot = slot;
+ directory_view->details->window = window;
+
+ g_signal_connect_object (directory_view->details->slot,
+ "active", G_CALLBACK (slot_active),
+ directory_view, 0);
+ g_signal_connect_object (directory_view->details->slot,
+ "inactive", G_CALLBACK (slot_inactive),
+ directory_view, 0);
+
+ g_signal_connect_object (directory_view->details->window,
+ "hidden-files-mode-changed", G_CALLBACK (hidden_files_mode_changed),
+ directory_view, 0);
+ fm_directory_view_init_show_hidden_files (directory_view);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+gboolean
+fm_directory_view_handle_scroll_event (FMDirectoryView *directory_view,
+ GdkEventScroll *event)
+{
+ if (event->state & GDK_CONTROL_MASK) {
+ switch (event->direction) {
+ case GDK_SCROLL_UP:
+ /* Zoom In */
+ fm_directory_view_bump_zoom_level (directory_view, 1);
+ return TRUE;
+
+ case GDK_SCROLL_DOWN:
+ /* Zoom Out */
+ fm_directory_view_bump_zoom_level (directory_view, -1);
+ return TRUE;
+
+ case GDK_SCROLL_LEFT:
+ case GDK_SCROLL_RIGHT:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ return FALSE;
+}
+
+/* handle Shift+Scroll, which will cause a zoom-in/out */
+static gboolean
+fm_directory_view_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ FMDirectoryView *directory_view;
+
+ directory_view = FM_DIRECTORY_VIEW (widget);
+ if (fm_directory_view_handle_scroll_event (directory_view, event)) {
+ return TRUE;
+ }
+
+ return GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, event);
+}
+
+
+static void
+fm_directory_view_parent_set (GtkWidget *widget,
+ GtkWidget *old_parent)
+{
+ FMDirectoryView *view;
+ GtkWidget *parent;
+
+ view = FM_DIRECTORY_VIEW (widget);
+
+ parent = gtk_widget_get_parent (widget);
+ g_assert (parent == NULL || old_parent == NULL);
+
+ if (GTK_WIDGET_CLASS (parent_class)->parent_set != NULL) {
+ GTK_WIDGET_CLASS (parent_class)->parent_set (widget, old_parent);
+ }
+
+ if (parent != NULL) {
+ g_assert (old_parent == NULL);
+
+ if (view->details->slot ==
+ caja_window_info_get_active_slot (view->details->window)) {
+ view->details->active = TRUE;
+
+ fm_directory_view_merge_menus (view);
+ schedule_update_menus (view);
+ }
+ } else {
+ fm_directory_view_unmerge_menus (view);
+ remove_update_menus_timeout_callback (view);
+ }
+}
+
+static void
+fm_directory_view_class_init (FMDirectoryViewClass *klass)
+{
+ GtkWidgetClass *widget_class;
+ GtkScrolledWindowClass *scrolled_window_class;
+ GtkBindingSet *binding_set;
+
+ widget_class = GTK_WIDGET_CLASS (klass);
+ scrolled_window_class = GTK_SCROLLED_WINDOW_CLASS (klass);
+
+ G_OBJECT_CLASS (klass)->finalize = fm_directory_view_finalize;
+ G_OBJECT_CLASS (klass)->set_property = fm_directory_view_set_property;
+
+ GTK_OBJECT_CLASS (klass)->destroy = fm_directory_view_destroy;
+
+ widget_class->scroll_event = fm_directory_view_scroll_event;
+ widget_class->parent_set = fm_directory_view_parent_set;
+
+ /* Get rid of the strange 3-pixel gap that GtkScrolledWindow
+ * uses by default. It does us no good.
+ */
+ scrolled_window_class->scrollbar_spacing = 0;
+
+ signals[ADD_FILE] =
+ g_signal_new ("add_file",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, add_file),
+ NULL, NULL,
+ caja_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
+ signals[BEGIN_FILE_CHANGES] =
+ g_signal_new ("begin_file_changes",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, begin_file_changes),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[BEGIN_LOADING] =
+ g_signal_new ("begin_loading",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, begin_loading),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[CLEAR] =
+ g_signal_new ("clear",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, clear),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[END_FILE_CHANGES] =
+ g_signal_new ("end_file_changes",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, end_file_changes),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[FLUSH_ADDED_FILES] =
+ g_signal_new ("flush_added_files",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, flush_added_files),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[END_LOADING] =
+ g_signal_new ("end_loading",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, end_loading),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ signals[FILE_CHANGED] =
+ g_signal_new ("file_changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, file_changed),
+ NULL, NULL,
+ caja_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
+ signals[LOAD_ERROR] =
+ g_signal_new ("load_error",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, load_error),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+ signals[REMOVE_FILE] =
+ g_signal_new ("remove_file",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, remove_file),
+ NULL, NULL,
+ caja_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
+
+ klass->accepts_dragged_files = real_accepts_dragged_files;
+ klass->file_limit_reached = real_file_limit_reached;
+ klass->file_still_belongs = real_file_still_belongs;
+ klass->get_emblem_names_to_exclude = real_get_emblem_names_to_exclude;
+ klass->get_selected_icon_locations = real_get_selected_icon_locations;
+ klass->is_read_only = real_is_read_only;
+ klass->load_error = real_load_error;
+ klass->can_rename_file = can_rename_file;
+ klass->start_renaming_file = start_renaming_file;
+ klass->supports_creating_files = real_supports_creating_files;
+ klass->supports_properties = real_supports_properties;
+ klass->supports_zooming = real_supports_zooming;
+ klass->using_manual_layout = real_using_manual_layout;
+ klass->merge_menus = real_merge_menus;
+ klass->unmerge_menus = real_unmerge_menus;
+ klass->update_menus = real_update_menus;
+ klass->set_is_active = real_set_is_active;
+
+ /* Function pointers that subclasses must override */
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, add_file);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, bump_zoom_level);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, can_zoom_in);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, can_zoom_out);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, clear);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, file_changed);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_background_widget);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_selection);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_selection_for_file_transfer);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_item_count);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, is_empty);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, reset_to_defaults);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, restore_default_zoom_level);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, select_all);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, set_selection);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, invert_selection);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, zoom_to_level);
+ EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_zoom_level);
+
+ copied_files_atom = gdk_atom_intern ("x-special/mate-copied-files", FALSE);
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_WINDOW_SLOT,
+ g_param_spec_object ("window-slot",
+ "Window Slot",
+ "The parent window slot reference",
+ CAJA_TYPE_WINDOW_SLOT_INFO,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ signals[TRASH] =
+ g_signal_new ("trash",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, trash),
+ g_signal_accumulator_true_handled, NULL,
+ eel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+ signals[DELETE] =
+ g_signal_new ("delete",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (FMDirectoryViewClass, delete),
+ g_signal_accumulator_true_handled, NULL,
+ eel_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+
+ binding_set = gtk_binding_set_by_class (klass);
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
+ "trash", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, 0,
+ "trash", 0);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Delete, GDK_SHIFT_MASK,
+ "delete", 0);
+
+ klass->trash = real_trash;
+ klass->delete = real_delete;
+}