diff options
Diffstat (limited to 'src/caja-places-sidebar.c')
-rw-r--r-- | src/caja-places-sidebar.c | 3092 |
1 files changed, 3092 insertions, 0 deletions
diff --git a/src/caja-places-sidebar.c b/src/caja-places-sidebar.c new file mode 100644 index 00000000..52c1048e --- /dev/null +++ b/src/caja-places-sidebar.c @@ -0,0 +1,3092 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Caja + * + * This library 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 library 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 library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk) + * + */ + +#include <config.h> + +#include <eel/eel-debug.h> +#include <eel/eel-gtk-extensions.h> +#include <eel/eel-glib-extensions.h> +#include <eel/eel-preferences.h> +#include <eel/eel-string.h> +#include <eel/eel-stock-dialogs.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <libcaja-private/caja-debug-log.h> +#include <libcaja-private/caja-dnd.h> +#include <libcaja-private/caja-bookmark.h> +#include <libcaja-private/caja-global-preferences.h> +#include <libcaja-private/caja-sidebar-provider.h> +#include <libcaja-private/caja-module.h> +#include <libcaja-private/caja-file.h> +#include <libcaja-private/caja-file-utilities.h> +#include <libcaja-private/caja-file-operations.h> +#include <libcaja-private/caja-trash-monitor.h> +#include <libcaja-private/caja-icon-names.h> +#include <libcaja-private/caja-autorun.h> +#include <libcaja-private/caja-window-info.h> +#include <libcaja-private/caja-window-slot-info.h> +#include <gio/gio.h> + +#include "caja-bookmark-list.h" +#include "caja-places-sidebar.h" +#include "caja-window.h" + +#define EJECT_BUTTON_XPAD 5 + +typedef struct +{ + GtkScrolledWindow parent; + GtkTreeView *tree_view; + GtkCellRenderer *icon_cell_renderer; + GtkCellRenderer *eject_text_cell_renderer; + char *uri; + GtkListStore *store; + GtkTreeModel *filter_model; + CajaWindowInfo *window; + CajaBookmarkList *bookmarks; + GVolumeMonitor *volume_monitor; + + /* DnD */ + GList *drag_list; + gboolean drag_data_received; + int drag_data_info; + gboolean drop_occured; + + GtkWidget *popup_menu; + GtkWidget *popup_menu_open_in_new_tab_item; + GtkWidget *popup_menu_remove_item; + GtkWidget *popup_menu_rename_item; + GtkWidget *popup_menu_separator_item; + GtkWidget *popup_menu_mount_item; + GtkWidget *popup_menu_unmount_item; + GtkWidget *popup_menu_eject_item; + GtkWidget *popup_menu_rescan_item; + GtkWidget *popup_menu_format_item; + GtkWidget *popup_menu_empty_trash_item; + GtkWidget *popup_menu_start_item; + GtkWidget *popup_menu_stop_item; + + /* volume mounting - delayed open process */ + gboolean mounting; + CajaWindowSlotInfo *go_to_after_mount_slot; + CajaWindowOpenFlags go_to_after_mount_flags; +} CajaPlacesSidebar; + +typedef struct +{ + GtkScrolledWindowClass parent; +} CajaPlacesSidebarClass; + +typedef struct +{ + GObject parent; +} CajaPlacesSidebarProvider; + +typedef struct +{ + GObjectClass parent; +} CajaPlacesSidebarProviderClass; + +enum +{ + PLACES_SIDEBAR_COLUMN_ROW_TYPE, + PLACES_SIDEBAR_COLUMN_URI, + PLACES_SIDEBAR_COLUMN_DRIVE, + PLACES_SIDEBAR_COLUMN_VOLUME, + PLACES_SIDEBAR_COLUMN_MOUNT, + PLACES_SIDEBAR_COLUMN_NAME, + PLACES_SIDEBAR_COLUMN_ICON, + PLACES_SIDEBAR_COLUMN_INDEX, + PLACES_SIDEBAR_COLUMN_EJECT, + PLACES_SIDEBAR_COLUMN_NO_EJECT, + PLACES_SIDEBAR_COLUMN_BOOKMARK, + PLACES_SIDEBAR_COLUMN_NO_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, + + PLACES_SIDEBAR_COLUMN_COUNT +}; + +typedef enum +{ + PLACES_BUILT_IN, + PLACES_MOUNTED_VOLUME, + PLACES_BOOKMARK, + PLACES_SEPARATOR +} PlaceType; + +static void caja_places_sidebar_iface_init (CajaSidebarIface *iface); +static void sidebar_provider_iface_init (CajaSidebarProviderIface *iface); +static GType caja_places_sidebar_provider_get_type (void); +static void open_selected_bookmark (CajaPlacesSidebar *sidebar, + GtkTreeModel *model, + GtkTreePath *path, + CajaWindowOpenFlags flags); +static void caja_places_sidebar_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static gboolean eject_or_unmount_bookmark (CajaPlacesSidebar *sidebar, + GtkTreePath *path); +static gboolean eject_or_unmount_selection (CajaPlacesSidebar *sidebar); +static void check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject); + +static void bookmarks_check_popup_sensitivity (CajaPlacesSidebar *sidebar); + +/* Identifiers for target types */ +enum +{ + GTK_TREE_MODEL_ROW, + TEXT_URI_LIST +}; + +/* Target types for dragging from the shortcuts list */ +static const GtkTargetEntry caja_shortcuts_source_targets[] = +{ + { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW } +}; + +/* Target types for dropping into the shortcuts list */ +static const GtkTargetEntry caja_shortcuts_drop_targets [] = +{ + { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }, + { "text/uri-list", 0, TEXT_URI_LIST } +}; + +/* Drag and drop interface declarations */ +typedef struct +{ + GtkTreeModelFilter parent; + + CajaPlacesSidebar *sidebar; +} CajaShortcutsModelFilter; + +typedef struct +{ + GtkTreeModelFilterClass parent_class; +} CajaShortcutsModelFilterClass; + +#define CAJA_SHORTCUTS_MODEL_FILTER_TYPE (_caja_shortcuts_model_filter_get_type ()) +#define CAJA_SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CAJA_SHORTCUTS_MODEL_FILTER_TYPE, CajaShortcutsModelFilter)) + +GType _caja_shortcuts_model_filter_get_type (void); +static void caja_shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface); + +G_DEFINE_TYPE_WITH_CODE (CajaShortcutsModelFilter, + _caja_shortcuts_model_filter, + GTK_TYPE_TREE_MODEL_FILTER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + caja_shortcuts_model_filter_drag_source_iface_init)); + +static GtkTreeModel *caja_shortcuts_model_filter_new (CajaPlacesSidebar *sidebar, + GtkTreeModel *child_model, + GtkTreePath *root); + +G_DEFINE_TYPE_WITH_CODE (CajaPlacesSidebar, caja_places_sidebar, GTK_TYPE_SCROLLED_WINDOW, + G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR, + caja_places_sidebar_iface_init)); + +G_DEFINE_TYPE_WITH_CODE (CajaPlacesSidebarProvider, caja_places_sidebar_provider, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (CAJA_TYPE_SIDEBAR_PROVIDER, + sidebar_provider_iface_init)); + +static GtkTreeIter +add_place (CajaPlacesSidebar *sidebar, + PlaceType place_type, + const char *name, + GIcon *icon, + const char *uri, + GDrive *drive, + GVolume *volume, + GMount *mount, + const int index, + const char *tooltip) +{ + GdkPixbuf *pixbuf; + GtkTreeIter iter, child_iter; + CajaIconInfo *icon_info; + int icon_size; + gboolean show_eject, show_unmount; + gboolean show_eject_button; + + icon_size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); + icon_info = caja_icon_info_lookup (icon, icon_size); + + pixbuf = caja_icon_info_get_pixbuf_at_size (icon_info, icon_size); + g_object_unref (icon_info); + check_unmount_and_eject (mount, volume, drive, + &show_unmount, &show_eject); + + if (show_unmount || show_eject) + { + g_assert (place_type != PLACES_BOOKMARK); + } + + if (mount == NULL) + { + show_eject_button = FALSE; + } + else + { + show_eject_button = (show_unmount || show_eject); + } + + gtk_list_store_append (sidebar->store, &iter); + gtk_list_store_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_ICON, pixbuf, + PLACES_SIDEBAR_COLUMN_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, uri, + PLACES_SIDEBAR_COLUMN_DRIVE, drive, + PLACES_SIDEBAR_COLUMN_VOLUME, volume, + PLACES_SIDEBAR_COLUMN_MOUNT, mount, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type, + PLACES_SIDEBAR_COLUMN_INDEX, index, + PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button, + PLACES_SIDEBAR_COLUMN_NO_EJECT, !show_eject_button, + PLACES_SIDEBAR_COLUMN_BOOKMARK, place_type != PLACES_BOOKMARK, + PLACES_SIDEBAR_COLUMN_NO_BOOKMARK, place_type == PLACES_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, tooltip, + -1); + + if (pixbuf != NULL) + { + g_object_unref (pixbuf); + } + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->filter_model)); + gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (sidebar->filter_model), + &child_iter, + &iter); + return child_iter; +} + +static void +compare_for_selection (CajaPlacesSidebar *sidebar, + const gchar *location, + const gchar *added_uri, + const gchar *last_uri, + GtkTreeIter *iter, + GtkTreePath **path) +{ + int res; + + res = eel_strcmp (added_uri, last_uri); + + if (res == 0) + { + /* last_uri always comes first */ + if (*path != NULL) + { + gtk_tree_path_free (*path); + } + *path = gtk_tree_model_get_path (sidebar->filter_model, + iter); + } + else if (eel_strcmp (location, added_uri) == 0) + { + if (*path == NULL) + { + *path = gtk_tree_model_get_path (sidebar->filter_model, + iter); + } + } +} + +static void +update_places (CajaPlacesSidebar *sidebar) +{ + CajaBookmark *bookmark; + GtkTreeSelection *selection; + GtkTreeIter iter, last_iter; + GtkTreePath *select_path; + GtkTreeModel *model; + GVolumeMonitor *volume_monitor; + GList *mounts, *l, *ll; + GMount *mount; + GList *drives; + GDrive *drive; + GList *volumes; + GVolume *volume; + int bookmark_count, index; + char *location, *mount_uri, *name, *desktop_path, *last_uri; + GIcon *icon; + GFile *root; + CajaWindowSlotInfo *slot; + char *tooltip; + + model = NULL; + last_uri = NULL; + select_path = NULL; + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + if (gtk_tree_selection_get_selected (selection, &model, &last_iter)) + { + gtk_tree_model_get (model, + &last_iter, + PLACES_SIDEBAR_COLUMN_URI, &last_uri, -1); + } + gtk_list_store_clear (sidebar->store); + + slot = caja_window_info_get_active_slot (sidebar->window); + location = caja_window_slot_info_get_current_location (slot); + + /* add built in bookmarks */ + desktop_path = caja_get_desktop_directory (); + + if (strcmp (g_get_home_dir(), desktop_path) != 0) + { + char *display_name; + + mount_uri = caja_get_home_directory_uri (); + display_name = g_filename_display_basename (g_get_home_dir ()); + icon = g_themed_icon_new (CAJA_ICON_HOME); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + display_name, icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open your personal folder")); + g_object_unref (icon); + g_free (display_name); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_free (mount_uri); + } + + mount_uri = g_filename_to_uri (desktop_path, NULL, NULL); + icon = g_themed_icon_new (CAJA_ICON_DESKTOP); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + _("Desktop"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open the contents of your desktop in a folder")); + g_object_unref (icon); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_free (mount_uri); + g_free (desktop_path); + + mount_uri = "file:///"; /* No need to strdup */ + icon = g_themed_icon_new (CAJA_ICON_FILESYSTEM); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + _("File System"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open the contents of the File System")); + g_object_unref (icon); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + + mount_uri = "network:///"; /* No need to strdup */ + icon = g_themed_icon_new (CAJA_ICON_NETWORK); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + _("Network"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Browse the contents of the network")); + g_object_unref (icon); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + + volume_monitor = sidebar->volume_monitor; + + /* first go through all connected drives */ + drives = g_volume_monitor_get_connected_drives (volume_monitor); + for (l = drives; l != NULL; l = l->next) + { + drive = l->data; + + volumes = g_drive_get_volumes (drive); + if (volumes != NULL) + { + for (ll = volumes; ll != NULL; ll = ll->next) + { + volume = ll->data; + mount = g_volume_get_mount (volume); + if (mount != NULL) + { + /* Show mounted volume in the sidebar */ + icon = g_mount_get_icon (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + name, icon, mount_uri, + drive, volume, mount, 0, tooltip); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_object_unref (root); + g_object_unref (mount); + g_object_unref (icon); + g_free (tooltip); + g_free (name); + g_free (mount_uri); + } + else + { + /* Do show the unmounted volumes in the sidebar; + * this is so the user can mount it (in case automounting + * is off). + * + * Also, even if automounting is enabled, this gives a visual + * cue that the user should remember to yank out the media if + * he just unmounted it. + */ + icon = g_volume_get_icon (volume); + name = g_volume_get_name (volume); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + name, icon, NULL, + drive, volume, NULL, 0, tooltip); + g_object_unref (icon); + g_free (name); + g_free (tooltip); + } + g_object_unref (volume); + } + g_list_free (volumes); + } + else + { + if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive)) + { + /* If the drive has no mountable volumes and we cannot detect media change.. we + * display the drive in the sidebar so the user can manually poll the drive by + * right clicking and selecting "Rescan..." + * + * This is mainly for drives like floppies where media detection doesn't + * work.. but it's also for human beings who like to turn off media detection + * in the OS to save battery juice. + */ + icon = g_drive_get_icon (drive); + name = g_drive_get_name (drive); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + name, icon, NULL, + drive, NULL, NULL, 0, tooltip); + g_object_unref (icon); + g_free (tooltip); + g_free (name); + } + } + g_object_unref (drive); + } + g_list_free (drives); + + /* add all volumes that is not associated with a drive */ + volumes = g_volume_monitor_get_volumes (volume_monitor); + for (l = volumes; l != NULL; l = l->next) + { + volume = l->data; + drive = g_volume_get_drive (volume); + if (drive != NULL) + { + g_object_unref (volume); + g_object_unref (drive); + continue; + } + mount = g_volume_get_mount (volume); + if (mount != NULL) + { + icon = g_mount_get_icon (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + tooltip = g_file_get_parse_name (root); + g_object_unref (root); + name = g_mount_get_name (mount); + last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + name, icon, mount_uri, + NULL, volume, mount, 0, tooltip); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_object_unref (mount); + g_object_unref (icon); + g_free (name); + g_free (tooltip); + g_free (mount_uri); + } + else + { + /* see comment above in why we add an icon for an unmounted mountable volume */ + icon = g_volume_get_icon (volume); + name = g_volume_get_name (volume); + last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + name, icon, NULL, + NULL, volume, NULL, 0, name); + g_object_unref (icon); + g_free (name); + } + g_object_unref (volume); + } + g_list_free (volumes); + + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (volume_monitor); + for (l = mounts; l != NULL; l = l->next) + { + mount = l->data; + if (g_mount_is_shadowed (mount)) + { + g_object_unref (mount); + continue; + } + volume = g_mount_get_volume (mount); + if (volume != NULL) + { + g_object_unref (volume); + g_object_unref (mount); + continue; + } + icon = g_mount_get_icon (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + last_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + name, icon, mount_uri, + NULL, NULL, mount, 0, tooltip); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_object_unref (root); + g_object_unref (mount); + g_object_unref (icon); + g_free (name); + g_free (mount_uri); + g_free (tooltip); + } + g_list_free (mounts); + + mount_uri = "trash:///"; /* No need to strdup */ + icon = caja_trash_monitor_get_icon (); + last_iter = add_place (sidebar, PLACES_BUILT_IN, + _("Trash"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Open the trash")); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_object_unref (icon); + + /* add separator */ + + gtk_list_store_append (sidebar->store, &iter); + gtk_list_store_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_SEPARATOR, + -1); + + /* add bookmarks */ + + bookmark_count = caja_bookmark_list_length (sidebar->bookmarks); + for (index = 0; index < bookmark_count; ++index) + { + bookmark = caja_bookmark_list_item_at (sidebar->bookmarks, index); + + if (caja_bookmark_uri_known_not_to_exist (bookmark)) + { + continue; + } + + name = caja_bookmark_get_name (bookmark); + icon = caja_bookmark_get_icon (bookmark); + mount_uri = caja_bookmark_get_uri (bookmark); + root = caja_bookmark_get_location (bookmark); + tooltip = g_file_get_parse_name (root); + last_iter = add_place (sidebar, PLACES_BOOKMARK, + name, icon, mount_uri, + NULL, NULL, NULL, index, + tooltip); + compare_for_selection (sidebar, + location, mount_uri, last_uri, + &last_iter, &select_path); + g_free (name); + g_object_unref (root); + g_object_unref (icon); + g_free (mount_uri); + g_free (tooltip); + } + g_free (location); + + if (select_path != NULL) + { + gtk_tree_selection_select_path (selection, select_path); + } + + if (select_path != NULL) + { + gtk_tree_path_free (select_path); + } + + g_free (last_uri); +} + +static gboolean +caja_shortcuts_row_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + PlaceType type; + + gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, -1); + + if (type == PLACES_SEPARATOR) + { + return TRUE; + } + + return FALSE; +} + +static void +mount_added_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +mount_removed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +mount_changed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_added_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_removed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_changed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_disconnected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_connected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_changed_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + CajaPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static gboolean +clicked_eject_button (CajaPlacesSidebar *sidebar, + GtkTreePath **path) +{ + GdkEvent *event = gtk_get_current_event (); + GdkEventButton *button_event = (GdkEventButton *) event; + GtkTreeViewColumn *column; + GtkTextDirection direction; + int width, total_width; + int eject_button_size; + gboolean show_eject; + GtkTreeIter iter; + GtkTreeModel *model; + + *path = NULL; + model = gtk_tree_view_get_model (sidebar->tree_view); + + if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && + gtk_tree_view_get_path_at_pos (sidebar->tree_view, + button_event->x, button_event->y, + path, &column, NULL, NULL)) + { + + gtk_tree_model_get_iter (model, &iter, *path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_EJECT, &show_eject, + -1); + + if (!show_eject) + { + goto out; + } + + total_width = 0; + + gtk_widget_style_get (GTK_WIDGET (sidebar->tree_view), + "horizontal-separator", &width, + NULL); + total_width += width / 2; + + direction = gtk_widget_get_direction (GTK_WIDGET (sidebar->tree_view)); + if (direction != GTK_TEXT_DIR_RTL) + { + gtk_tree_view_column_cell_get_position (column, + sidebar->icon_cell_renderer, + NULL, &width); + total_width += width; + + gtk_tree_view_column_cell_get_position (column, + sidebar->eject_text_cell_renderer, + NULL, &width); + total_width += width; + } + + total_width += EJECT_BUTTON_XPAD; + + eject_button_size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); + + if (button_event->x - total_width >= 0 && + button_event->x - total_width <= eject_button_size) + { + return TRUE; + } + } + +out: + if (*path != NULL) + { + gtk_tree_path_free (*path); + } + + return FALSE; +} + +static void +desktop_location_changed_callback (gpointer user_data) +{ + CajaPlacesSidebar *sidebar; + + sidebar = CAJA_PLACES_SIDEBAR (user_data); + + update_places (sidebar); +} + +static void +loading_uri_callback (CajaWindowInfo *window, + char *location, + CajaPlacesSidebar *sidebar) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + gboolean valid; + char *uri; + + if (strcmp (sidebar->uri, location) != 0) + { + g_free (sidebar->uri); + sidebar->uri = g_strdup (location); + + /* set selection if any place matches location */ + selection = gtk_tree_view_get_selection (sidebar->tree_view); + gtk_tree_selection_unselect_all (selection); + valid = gtk_tree_model_get_iter_first (sidebar->filter_model, &iter); + + while (valid) + { + gtk_tree_model_get (sidebar->filter_model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + if (uri != NULL) + { + if (strcmp (uri, location) == 0) + { + g_free (uri); + gtk_tree_selection_select_iter (selection, &iter); + break; + } + g_free (uri); + } + valid = gtk_tree_model_iter_next (sidebar->filter_model, &iter); + } + } +} + + +static unsigned int +get_bookmark_index (GtkTreeView *tree_view) +{ + GtkTreeModel *model; + GtkTreePath *p; + GtkTreeIter iter; + PlaceType place_type; + int bookmark_index; + + model = gtk_tree_view_get_model (tree_view); + + bookmark_index = -1; + + /* find separator */ + p = gtk_tree_path_new_first (); + while (p != NULL) + { + gtk_tree_model_get_iter (model, &iter, p); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + -1); + + if (place_type == PLACES_SEPARATOR) + { + bookmark_index = *gtk_tree_path_get_indices (p) + 1; + break; + } + + gtk_tree_path_next (p); + } + gtk_tree_path_free (p); + + g_assert (bookmark_index >= 0); + + return bookmark_index; +} + +/* Computes the appropriate row and position for dropping */ +static void +compute_drop_position (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos, + CajaPlacesSidebar *sidebar) +{ + int bookmarks_index; + int num_rows; + int row; + + bookmarks_index = get_bookmark_index (tree_view); + + num_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (sidebar->filter_model), NULL); + + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, + y, + path, + pos)) + { + row = num_rows - 1; + *path = gtk_tree_path_new_from_indices (row, -1); + *pos = GTK_TREE_VIEW_DROP_AFTER; + return; + } + + row = *gtk_tree_path_get_indices (*path); + gtk_tree_path_free (*path); + + if (row == bookmarks_index - 1) + { + /* Only allow to drop after separator */ + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + else if (row < bookmarks_index) + { + /* Hardcoded shortcuts can only be dragged into */ + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } + else if (row >= num_rows) + { + row = num_rows - 1; + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + else if (*pos != GTK_TREE_VIEW_DROP_BEFORE && + sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW) + { + /* bookmark rows are never dragged into other bookmark rows */ + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + + *path = gtk_tree_path_new_from_indices (row, -1); +} + + +static gboolean +get_drag_data (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view), + context, + NULL); + + if (target == GDK_NONE) + { + return FALSE; + } + + gtk_drag_get_data (GTK_WIDGET (tree_view), + context, target, time); + + return TRUE; +} + +static void +free_drag_data (CajaPlacesSidebar *sidebar) +{ + sidebar->drag_data_received = FALSE; + + if (sidebar->drag_list) + { + caja_drag_destroy_selection_list (sidebar->drag_list); + sidebar->drag_list = NULL; + } +} + +static gboolean +can_accept_file_as_bookmark (CajaFile *file) +{ + return caja_file_is_directory (file); +} + +static gboolean +can_accept_items_as_bookmarks (const GList *items) +{ + int max; + char *uri; + CajaFile *file; + + /* Iterate through selection checking if item will get accepted as a bookmark. + * If more than 100 items selected, return an over-optimistic result. + */ + for (max = 100; items != NULL && max >= 0; items = items->next, max--) + { + uri = ((CajaDragSelectionItem *)items->data)->uri; + file = caja_file_get_by_uri (uri); + if (!can_accept_file_as_bookmark (file)) + { + caja_file_unref (file); + return FALSE; + } + caja_file_unref (file); + } + + return TRUE; +} + +static gboolean +drag_motion_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + CajaPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeViewDropPosition pos; + int action; + GtkTreeIter iter, child_iter; + char *uri; + + if (!sidebar->drag_data_received) + { + if (!get_drag_data (tree_view, context, time)) + { + return FALSE; + } + } + + compute_drop_position (tree_view, x, y, &path, &pos, sidebar); + + if (pos == GTK_TREE_VIEW_DROP_BEFORE || + pos == GTK_TREE_VIEW_DROP_AFTER ) + { + if (sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW) + { + action = GDK_ACTION_MOVE; + } + else if (can_accept_items_as_bookmarks (sidebar->drag_list)) + { + action = GDK_ACTION_COPY; + } + else + { + action = 0; + } + } + else + { + if (sidebar->drag_list == NULL) + { + action = 0; + } + else + { + gtk_tree_model_get_iter (sidebar->filter_model, + &iter, path); + gtk_tree_model_filter_convert_iter_to_child_iter ( + GTK_TREE_MODEL_FILTER (sidebar->filter_model), + &child_iter, &iter); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), + &child_iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + caja_drag_default_drop_action_for_icons (context, uri, + sidebar->drag_list, + &action); + g_free (uri); + } + } + + gtk_tree_view_set_drag_dest_row (tree_view, path, pos); + gtk_tree_path_free (path); + g_signal_stop_emission_by_name (tree_view, "drag-motion"); + + if (action != 0) + { + gdk_drag_status (context, action, time); + } + else + { + gdk_drag_status (context, 0, time); + } + + return TRUE; +} + +static void +drag_leave_callback (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time, + CajaPlacesSidebar *sidebar) +{ + free_drag_data (sidebar); + gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE); + g_signal_stop_emission_by_name (tree_view, "drag-leave"); +} + +/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */ +static void +bookmarks_drop_uris (CajaPlacesSidebar *sidebar, + GtkSelectionData *selection_data, + int position) +{ + CajaBookmark *bookmark; + CajaFile *file; + char *uri, *name; + char **uris; + int i; + GFile *location; + GIcon *icon; + + uris = gtk_selection_data_get_uris (selection_data); + if (!uris) + return; + + for (i = 0; uris[i]; i++) + { + uri = uris[i]; + file = caja_file_get_by_uri (uri); + + if (!can_accept_file_as_bookmark (file)) + { + caja_file_unref (file); + continue; + } + + uri = caja_file_get_drop_target_uri (file); + location = g_file_new_for_uri (uri); + caja_file_unref (file); + + name = caja_compute_title_for_location (location); + icon = g_themed_icon_new (CAJA_ICON_FOLDER); + bookmark = caja_bookmark_new (location, name, TRUE, icon); + + if (!caja_bookmark_list_contains (sidebar->bookmarks, bookmark)) + { + caja_bookmark_list_insert_item (sidebar->bookmarks, bookmark, position++); + } + + g_object_unref (location); + g_object_unref (bookmark); + g_object_unref (icon); + g_free (name); + g_free (uri); + } + + g_strfreev (uris); +} + +static GList * +uri_list_from_selection (GList *selection) +{ + CajaDragSelectionItem *item; + GList *ret; + GList *l; + + ret = NULL; + for (l = selection; l != NULL; l = l->next) + { + item = l->data; + ret = g_list_prepend (ret, item->uri); + } + + return g_list_reverse (ret); +} + +static GList* +build_selection_list (const char *data) +{ + CajaDragSelectionItem *item; + GList *result; + char **uris; + char *uri; + int i; + + uris = g_uri_list_extract_uris (data); + + result = NULL; + for (i = 0; uris[i]; i++) + { + uri = uris[i]; + item = caja_drag_selection_item_new (); + item->uri = g_strdup (uri); + item->got_icon_position = FALSE; + result = g_list_prepend (result, item); + } + + g_strfreev (uris); + + return g_list_reverse (result); +} + +static gboolean +get_selected_iter (CajaPlacesSidebar *sidebar, + GtkTreeIter *iter) +{ + GtkTreeSelection *selection; + GtkTreeIter parent_iter; + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter)) + { + return FALSE; + } + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (sidebar->filter_model), + iter, + &parent_iter); + return TRUE; +} + +/* Reorders the selected bookmark to the specified position */ +static void +reorder_bookmarks (CajaPlacesSidebar *sidebar, + int new_position) +{ + GtkTreeIter iter; + PlaceType type; + int old_position; + + /* Get the selected path */ + + if (!get_selected_iter (sidebar, &iter)) + g_assert_not_reached (); + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_INDEX, &old_position, + -1); + + if (type != PLACES_BOOKMARK || + old_position < 0 || + old_position >= caja_bookmark_list_length (sidebar->bookmarks)) + { + return; + } + + caja_bookmark_list_move_item (sidebar->bookmarks, old_position, + new_position); +} + +static void +drag_data_received_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection_data, + unsigned int info, + unsigned int time, + CajaPlacesSidebar *sidebar) +{ + GtkTreeView *tree_view; + GtkTreePath *tree_path; + GtkTreeViewDropPosition tree_pos; + GtkTreeIter iter; + int position; + GtkTreeModel *model; + char *drop_uri; + GList *selection_list, *uris; + PlaceType type; + gboolean success; + + tree_view = GTK_TREE_VIEW (widget); + + if (!sidebar->drag_data_received) + { + if (gtk_selection_data_get_target (selection_data) != GDK_NONE && + info == TEXT_URI_LIST) + { + sidebar->drag_list = build_selection_list (gtk_selection_data_get_data (selection_data)); + } + else + { + sidebar->drag_list = NULL; + } + sidebar->drag_data_received = TRUE; + sidebar->drag_data_info = info; + } + + g_signal_stop_emission_by_name (widget, "drag-data-received"); + + if (!sidebar->drop_occured) + { + return; + } + + /* Compute position */ + compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar); + + success = FALSE; + + if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE || + tree_pos == GTK_TREE_VIEW_DROP_AFTER) + { + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, tree_path)) + { + goto out; + } + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_INDEX, &position, + -1); + + if (type != PLACES_SEPARATOR && type != PLACES_BOOKMARK) + { + goto out; + } + + if (type == PLACES_BOOKMARK && + tree_pos == GTK_TREE_VIEW_DROP_AFTER) + { + position++; + } + + switch (info) + { + case TEXT_URI_LIST: + bookmarks_drop_uris (sidebar, selection_data, position); + success = TRUE; + break; + case GTK_TREE_MODEL_ROW: + reorder_bookmarks (sidebar, position); + success = TRUE; + break; + default: + g_assert_not_reached (); + break; + } + } + else + { + GdkDragAction real_action; + + /* file transfer requested */ + real_action = gdk_drag_context_get_selected_action (context); + + if (real_action == GDK_ACTION_ASK) + { + real_action = + caja_drag_drop_action_ask (GTK_WIDGET (tree_view), + gdk_drag_context_get_actions (context)); + } + + if (real_action > 0) + { + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get_iter (model, &iter, tree_path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &drop_uri, + -1); + + switch (info) + { + case TEXT_URI_LIST: + selection_list = build_selection_list (gtk_selection_data_get_data (selection_data)); + uris = uri_list_from_selection (selection_list); + caja_file_operations_copy_move (uris, NULL, drop_uri, + real_action, GTK_WIDGET (tree_view), + NULL, NULL); + caja_drag_destroy_selection_list (selection_list); + g_list_free (uris); + success = TRUE; + break; + case GTK_TREE_MODEL_ROW: + success = FALSE; + break; + default: + g_assert_not_reached (); + break; + } + + g_free (drop_uri); + } + } + +out: + sidebar->drop_occured = FALSE; + free_drag_data (sidebar); + gtk_drag_finish (context, success, FALSE, time); + + gtk_tree_path_free (tree_path); +} + +static gboolean +drag_drop_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + CajaPlacesSidebar *sidebar) +{ + gboolean retval = FALSE; + sidebar->drop_occured = TRUE; + retval = get_drag_data (tree_view, context, time); + g_signal_stop_emission_by_name (tree_view, "drag-drop"); + return retval; +} + +/* Callback used when the file list's popup menu is detached */ +static void +bookmarks_popup_menu_detach_cb (GtkWidget *attach_widget, + GtkMenu *menu) +{ + CajaPlacesSidebar *sidebar; + + sidebar = CAJA_PLACES_SIDEBAR (attach_widget); + g_assert (CAJA_IS_PLACES_SIDEBAR (sidebar)); + + sidebar->popup_menu = NULL; + sidebar->popup_menu_remove_item = NULL; + sidebar->popup_menu_rename_item = NULL; + sidebar->popup_menu_separator_item = NULL; + sidebar->popup_menu_mount_item = NULL; + sidebar->popup_menu_unmount_item = NULL; + sidebar->popup_menu_eject_item = NULL; + sidebar->popup_menu_rescan_item = NULL; + sidebar->popup_menu_format_item = NULL; + sidebar->popup_menu_start_item = NULL; + sidebar->popup_menu_stop_item = NULL; + sidebar->popup_menu_empty_trash_item = NULL; +} + +static void +check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject) +{ + *show_unmount = FALSE; + *show_eject = FALSE; + + if (drive != NULL) + { + *show_eject = g_drive_can_eject (drive); + } + + if (volume != NULL) + { + *show_eject |= g_volume_can_eject (volume); + } + if (mount != NULL) + { + *show_eject |= g_mount_can_eject (mount); + *show_unmount = g_mount_can_unmount (mount) && !*show_eject; + } +} + +static void +check_visibility (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_mount, + gboolean *show_unmount, + gboolean *show_eject, + gboolean *show_rescan, + gboolean *show_format, + gboolean *show_start, + gboolean *show_stop) +{ + *show_mount = FALSE; + *show_format = FALSE; + *show_rescan = FALSE; + *show_start = FALSE; + *show_stop = FALSE; + + check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); + + if (drive != NULL) + { + if (g_drive_is_media_removable (drive) && + !g_drive_is_media_check_automatic (drive) && + g_drive_can_poll_for_media (drive)) + *show_rescan = TRUE; + + *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); + *show_stop = g_drive_can_stop (drive); + + if (*show_stop) + *show_unmount = FALSE; + } + + if (volume != NULL) + { + if (mount == NULL) + *show_mount = g_volume_can_mount (volume); + } +} + +static void +bookmarks_check_popup_sensitivity (CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + PlaceType type; + GDrive *drive = NULL; + GVolume *volume = NULL; + GMount *mount = NULL; + gboolean show_mount; + gboolean show_unmount; + gboolean show_eject; + gboolean show_rescan; + gboolean show_format; + gboolean show_start; + gboolean show_stop; + gboolean show_empty_trash; + char *uri = NULL; + + type = PLACES_BUILT_IN; + + if (sidebar->popup_menu == NULL) + { + return; + } + + if (get_selected_iter (sidebar, &iter)) + { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + } + + gtk_widget_show (sidebar->popup_menu_open_in_new_tab_item); + + gtk_widget_set_sensitive (sidebar->popup_menu_remove_item, (type == PLACES_BOOKMARK)); + gtk_widget_set_sensitive (sidebar->popup_menu_rename_item, (type == PLACES_BOOKMARK)); + gtk_widget_set_sensitive (sidebar->popup_menu_empty_trash_item, !caja_trash_monitor_is_empty ()); + + check_visibility (mount, volume, drive, + &show_mount, &show_unmount, &show_eject, &show_rescan, &show_format, &show_start, &show_stop); + + /* We actually want both eject and unmount since eject will unmount all volumes. + * TODO: hide unmount if the drive only has a single mountable volume + */ + + show_empty_trash = (uri != NULL) && + (!strcmp (uri, "trash:///")); + + eel_gtk_widget_set_shown (sidebar->popup_menu_separator_item, + show_mount || show_unmount || show_eject || show_format || show_empty_trash); + eel_gtk_widget_set_shown (sidebar->popup_menu_mount_item, show_mount); + eel_gtk_widget_set_shown (sidebar->popup_menu_unmount_item, show_unmount); + eel_gtk_widget_set_shown (sidebar->popup_menu_eject_item, show_eject); + eel_gtk_widget_set_shown (sidebar->popup_menu_rescan_item, show_rescan); + eel_gtk_widget_set_shown (sidebar->popup_menu_format_item, show_format); + eel_gtk_widget_set_shown (sidebar->popup_menu_start_item, show_start); + eel_gtk_widget_set_shown (sidebar->popup_menu_stop_item, show_stop); + eel_gtk_widget_set_shown (sidebar->popup_menu_empty_trash_item, show_empty_trash); + + /* Adjust start/stop items to reflect the type of the drive */ + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Start")); + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Stop")); + if ((show_start || show_stop) && drive != NULL) + { + switch (g_drive_get_start_stop_type (drive)) + { + case G_DRIVE_START_STOP_TYPE_SHUTDOWN: + /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */ + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Power On")); + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Safely Remove Drive")); + break; + case G_DRIVE_START_STOP_TYPE_NETWORK: + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Connect Drive")); + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Disconnect Drive")); + break; + case G_DRIVE_START_STOP_TYPE_MULTIDISK: + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Start Multi-disk Device")); + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Stop Multi-disk Device")); + break; + case G_DRIVE_START_STOP_TYPE_PASSWORD: + /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */ + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_start_item), _("_Unlock Drive")); + gtk_menu_item_set_label (GTK_MENU_ITEM (sidebar->popup_menu_stop_item), _("_Lock Drive")); + break; + + default: + case G_DRIVE_START_STOP_TYPE_UNKNOWN: + /* uses defaults set above */ + break; + } + } + + + g_free (uri); +} + +/* Callback used when the selection in the shortcuts tree changes */ +static void +bookmarks_selection_changed_cb (GtkTreeSelection *selection, + CajaPlacesSidebar *sidebar) +{ + bookmarks_check_popup_sensitivity (sidebar); +} + +static void +volume_mounted_cb (GVolume *volume, + GObject *user_data) +{ + GMount *mount; + CajaPlacesSidebar *sidebar; + GFile *location; + + sidebar = CAJA_PLACES_SIDEBAR (user_data); + + sidebar->mounting = FALSE; + + mount = g_volume_get_mount (volume); + if (mount != NULL) + { + location = g_mount_get_default_location (mount); + + if (sidebar->go_to_after_mount_slot != NULL) + { + if ((sidebar->go_to_after_mount_flags & CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) + { + caja_window_slot_info_open_location (sidebar->go_to_after_mount_slot, location, + CAJA_WINDOW_OPEN_ACCORDING_TO_MODE, + sidebar->go_to_after_mount_flags, NULL); + } + else + { + CajaWindow *cur, *new; + + cur = CAJA_WINDOW (sidebar->window); + new = caja_application_create_navigation_window (cur->application, + NULL, + gtk_window_get_screen (GTK_WINDOW (cur))); + caja_window_go_to (new, location); + } + } + + g_object_unref (G_OBJECT (location)); + g_object_unref (G_OBJECT (mount)); + } + + + eel_remove_weak_pointer (&(sidebar->go_to_after_mount_slot)); +} + +static void +drive_start_from_bookmark_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + char *name; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +open_selected_bookmark (CajaPlacesSidebar *sidebar, + GtkTreeModel *model, + GtkTreePath *path, + CajaWindowOpenFlags flags) +{ + CajaWindowSlotInfo *slot; + GtkTreeIter iter; + GFile *location; + char *uri; + + if (!path) + { + return; + } + + if (!gtk_tree_model_get_iter (model, &iter, path)) + { + return; + } + + gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + + if (uri != NULL) + { + caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER, + "activate from places sidebar window=%p: %s", + sidebar->window, uri); + location = g_file_new_for_uri (uri); + /* Navigate to the clicked location */ + if ((flags & CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) + { + slot = caja_window_info_get_active_slot (sidebar->window); + caja_window_slot_info_open_location (slot, location, + CAJA_WINDOW_OPEN_ACCORDING_TO_MODE, + flags, NULL); + } + else + { + CajaWindow *cur, *new; + + cur = CAJA_WINDOW (sidebar->window); + new = caja_application_create_navigation_window (cur->application, + NULL, + gtk_window_get_screen (GTK_WINDOW (cur))); + caja_window_go_to (new, location); + } + g_object_unref (location); + g_free (uri); + + } + else + { + GDrive *drive; + GVolume *volume; + CajaWindowSlot *slot; + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL && !sidebar->mounting) + { + sidebar->mounting = TRUE; + + g_assert (sidebar->go_to_after_mount_slot == NULL); + + slot = caja_window_info_get_active_slot (sidebar->window); + sidebar->go_to_after_mount_slot = slot; + eel_add_weak_pointer (&(sidebar->go_to_after_mount_slot)); + + sidebar->go_to_after_mount_flags = flags; + + caja_file_operations_mount_volume_full (NULL, volume, FALSE, + volume_mounted_cb, + G_OBJECT (sidebar)); + } + else if (volume == NULL && drive != NULL && + (g_drive_can_start (drive) || g_drive_can_start_degraded (drive))) + { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL); + g_object_unref (mount_op); + } + + if (drive != NULL) + g_object_unref (drive); + if (volume != NULL) + g_object_unref (volume); + } +} + +static void +open_shortcut_from_menu (CajaPlacesSidebar *sidebar, + CajaWindowOpenFlags flags) +{ + GtkTreeModel *model; + GtkTreePath *path; + + model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + + open_selected_bookmark (sidebar, model, path, flags); + + gtk_tree_path_free (path); +} + +static void +open_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, 0); +} + +static void +open_shortcut_in_new_window_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW); +} + +static void +open_shortcut_in_new_tab_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, CAJA_WINDOW_OPEN_FLAG_NEW_TAB); +} + +/* Rename the selected bookmark */ +static void +rename_selected_bookmark (CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GList *renderers; + + if (get_selected_iter (sidebar, &iter)) + { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 0); + renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + cell = g_list_nth_data (renderers, 3); + g_list_free (renderers); + g_object_set (cell, "editable", TRUE, NULL); + gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sidebar->tree_view), + path, column, cell, TRUE); + gtk_tree_path_free (path); + } +} + +static void +rename_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + rename_selected_bookmark (sidebar); +} + +/* Removes the selected bookmarks */ +static void +remove_selected_bookmarks (CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + PlaceType type; + int index; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type != PLACES_BOOKMARK) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_INDEX, &index, + -1); + + caja_bookmark_list_delete_item_at (sidebar->bookmarks, index); +} + +static void +remove_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + remove_selected_bookmarks (sidebar); +} + +static void +mount_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GVolume *volume; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL) + { + caja_file_operations_mount_volume (NULL, volume, FALSE); + g_object_unref (volume); + } +} + +static void +unmount_done (gpointer data) +{ + CajaWindow *window; + + window = data; + caja_window_info_set_initiated_unmount (window, FALSE); + g_object_unref (window); +} + +static void +do_unmount (GMount *mount, + CajaPlacesSidebar *sidebar) +{ + if (mount != NULL) + { + caja_window_info_set_initiated_unmount (sidebar->window, TRUE); + caja_file_operations_unmount_mount_full (NULL, mount, FALSE, TRUE, + unmount_done, + g_object_ref (sidebar->window)); + } +} + +static void +do_unmount_selection (CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + -1); + + if (mount != NULL) + { + do_unmount (mount, sidebar); + g_object_unref (mount); + } +} + +static void +unmount_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + do_unmount_selection (sidebar); +} + +static void +drive_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CajaWindow *window; + GError *error; + char *primary; + char *name; + + window = user_data; + caja_window_info_set_initiated_unmount (window, FALSE); + g_object_unref (window); + + error = NULL; + if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +volume_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CajaWindow *window; + GError *error; + char *primary; + char *name; + + window = user_data; + caja_window_info_set_initiated_unmount (window, FALSE); + g_object_unref (window); + + error = NULL; + if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +mount_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CajaWindow *window; + GError *error; + char *primary; + char *name; + + window = user_data; + caja_window_info_set_initiated_unmount (window, FALSE); + g_object_unref (window); + + error = NULL; + if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_mount_get_name (G_MOUNT (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +do_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + CajaPlacesSidebar *sidebar) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + if (mount != NULL) + { + caja_window_info_set_initiated_unmount (sidebar->window, TRUE); + g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb, + g_object_ref (sidebar->window)); + } + else if (volume != NULL) + { + caja_window_info_set_initiated_unmount (sidebar->window, TRUE); + g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb, + g_object_ref (sidebar->window)); + } + else if (drive != NULL) + { + caja_window_info_set_initiated_unmount (sidebar->window, TRUE); + g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb, + g_object_ref (sidebar->window)); + } + g_object_unref (mount_op); +} + +static void +eject_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + GVolume *volume; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + do_eject (mount, volume, drive, sidebar); +} + +static gboolean +eject_or_unmount_bookmark (CajaPlacesSidebar *sidebar, + GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean can_unmount, can_eject; + GMount *mount; + GVolume *volume; + GDrive *drive; + gboolean ret; + + model = GTK_TREE_MODEL (sidebar->filter_model); + + if (!path) + { + return FALSE; + } + if (!gtk_tree_model_get_iter (model, &iter, path)) + { + return FALSE; + } + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + ret = FALSE; + + check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject); + /* if we can eject, it has priority over unmount */ + if (can_eject) + { + do_eject (mount, volume, drive, sidebar); + ret = TRUE; + } + else if (can_unmount) + { + do_unmount (mount, sidebar); + ret = TRUE; + } + + if (mount != NULL) + g_object_unref (mount); + if (volume != NULL) + g_object_unref (volume); + if (drive != NULL) + g_object_unref (drive); + + return ret; +} + +static gboolean +eject_or_unmount_selection (CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + gboolean ret; + + if (!get_selected_iter (sidebar, &iter)) + { + return FALSE; + } + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + if (path == NULL) + { + return FALSE; + } + + ret = eject_or_unmount_bookmark (sidebar, path); + + gtk_tree_path_free (path); + + return ret; +} + +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + char *name; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to poll %s for media changes"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +rescan_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) + { + g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, NULL); + } + g_object_unref (drive); +} + +static void +format_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + g_spawn_command_line_async ("gfloppy", NULL); +} + +static void +drive_start_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + char *name; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +start_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) + { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, NULL); + + g_object_unref (mount_op); + } + g_object_unref (drive); +} + +static void +drive_stop_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + CajaWindow *window; + GError *error; + char *primary; + char *name; + + window = user_data; + caja_window_info_set_initiated_unmount (window, FALSE); + g_object_unref (window); + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) + { + if (error->code != G_IO_ERROR_FAILED_HANDLED) + { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to stop %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +stop_shortcut_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) + { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) + { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + caja_window_info_set_initiated_unmount (sidebar->window, TRUE); + g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb, + g_object_ref (sidebar->window)); + g_object_unref (mount_op); + } + g_object_unref (drive); +} + +static void +empty_trash_cb (GtkMenuItem *item, + CajaPlacesSidebar *sidebar) +{ + caja_file_operations_empty_trash (GTK_WIDGET (sidebar->window)); +} + +/* Handler for GtkWidget::key-press-event on the shortcuts list */ +static gboolean +bookmarks_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + CajaPlacesSidebar *sidebar) +{ + guint modifiers; + + modifiers = gtk_accelerator_get_default_mod_mask (); + + if (event->keyval == GDK_Down && + (event->state & modifiers) == GDK_MOD1_MASK) + { + return eject_or_unmount_selection (sidebar); + } + + if ((event->keyval == GDK_Delete + || event->keyval == GDK_KP_Delete) + && (event->state & modifiers) == 0) + { + remove_selected_bookmarks (sidebar); + return TRUE; + } + + if ((event->keyval == GDK_F2) + && (event->state & modifiers) == 0) + { + rename_selected_bookmark (sidebar); + return TRUE; + } + + return FALSE; +} + +/* Constructs the popup menu for the file list if needed */ +static void +bookmarks_build_popup_menu (CajaPlacesSidebar *sidebar) +{ + GtkWidget *item; + + if (sidebar->popup_menu) + { + return; + } + + sidebar->popup_menu = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (sidebar->popup_menu), + GTK_WIDGET (sidebar), + bookmarks_popup_menu_detach_cb); + + item = gtk_image_menu_item_new_with_mnemonic (_("_Open")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU)); + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab")); + sidebar->popup_menu_open_in_new_tab_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_in_new_tab_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window")); + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_in_new_window_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + eel_gtk_menu_append_separator (GTK_MENU (sidebar->popup_menu)); + + item = gtk_image_menu_item_new_with_label (_("Remove")); + sidebar->popup_menu_remove_item = item; + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); + g_signal_connect (item, "activate", + G_CALLBACK (remove_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_label (_("Rename...")); + sidebar->popup_menu_rename_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (rename_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + /* Mount/Unmount/Eject menu items */ + + sidebar->popup_menu_separator_item = + GTK_WIDGET (eel_gtk_menu_append_separator (GTK_MENU (sidebar->popup_menu))); + + item = gtk_menu_item_new_with_mnemonic (_("_Mount")); + sidebar->popup_menu_mount_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (mount_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Unmount")); + sidebar->popup_menu_unmount_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (unmount_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Eject")); + sidebar->popup_menu_eject_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (eject_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Detect Media")); + sidebar->popup_menu_rescan_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (rescan_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Format")); + sidebar->popup_menu_format_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (format_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Start")); + sidebar->popup_menu_start_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (start_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Stop")); + sidebar->popup_menu_stop_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (stop_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + /* Empty Trash menu item */ + + item = gtk_menu_item_new_with_mnemonic (_("Empty _Trash")); + sidebar->popup_menu_empty_trash_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (empty_trash_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + bookmarks_check_popup_sensitivity (sidebar); +} + +static void +bookmarks_update_popup_menu (CajaPlacesSidebar *sidebar) +{ + bookmarks_build_popup_menu (sidebar); +} + +static void +bookmarks_popup_menu (CajaPlacesSidebar *sidebar, + GdkEventButton *event) +{ + bookmarks_update_popup_menu (sidebar); + eel_pop_up_context_menu (GTK_MENU(sidebar->popup_menu), + EEL_DEFAULT_POPUP_MENU_DISPLACEMENT, + EEL_DEFAULT_POPUP_MENU_DISPLACEMENT, + event); +} + +/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */ +static gboolean +bookmarks_popup_menu_cb (GtkWidget *widget, + CajaPlacesSidebar *sidebar) +{ + bookmarks_popup_menu (sidebar, NULL); + return TRUE; +} + +static gboolean +bookmarks_button_release_event_cb (GtkWidget *widget, + GdkEventButton *event, + CajaPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeView *tree_view; + gboolean ret; + + if (event->type != GDK_BUTTON_RELEASE) + { + return TRUE; + } + + if (clicked_eject_button (sidebar, &path)) + { + ret = eject_or_unmount_bookmark (sidebar, path); + gtk_tree_path_free (path); + return ret; + } + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + + if (event->button == 1) + { + + if (event->window != gtk_tree_view_get_bin_window (tree_view)) + { + return FALSE; + } + + gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, + &path, NULL, NULL, NULL); + + open_selected_bookmark (sidebar, model, path, 0); + + gtk_tree_path_free (path); + } + + return FALSE; +} + +/* Callback used when a button is pressed on the shortcuts list. + * We trap button 3 to bring up a popup menu, and button 2 to + * open in a new tab. + */ +static gboolean +bookmarks_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + CajaPlacesSidebar *sidebar) +{ + if (event->type != GDK_BUTTON_PRESS) + { + /* ignore multiple clicks */ + return TRUE; + } + + if (event->button == 3) + { + bookmarks_popup_menu (sidebar, event); + } + else if (event->button == 2) + { + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeView *tree_view; + + tree_view = GTK_TREE_VIEW (widget); + g_assert (tree_view == sidebar->tree_view); + + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, + &path, NULL, NULL, NULL); + + open_selected_bookmark (sidebar, model, path, + event->state & GDK_CONTROL_MASK ? + CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW : + CAJA_WINDOW_OPEN_FLAG_NEW_TAB); + + if (path != NULL) + { + gtk_tree_path_free (path); + return TRUE; + } + } + + return FALSE; +} + + +static void +bookmarks_edited (GtkCellRenderer *cell, + gchar *path_string, + gchar *new_text, + CajaPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeIter iter; + CajaBookmark *bookmark; + int index; + + g_object_set (cell, "editable", FALSE, NULL); + + path = gtk_tree_path_new_from_string (path_string); + gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_INDEX, &index, + -1); + gtk_tree_path_free (path); + bookmark = caja_bookmark_list_item_at (sidebar->bookmarks, index); + + if (bookmark != NULL) + { + caja_bookmark_set_name (bookmark, new_text); + } +} + +static void +bookmarks_editing_canceled (GtkCellRenderer *cell, + CajaPlacesSidebar *sidebar) +{ + g_object_set (cell, "editable", FALSE, NULL); +} + +static void +trash_state_changed_cb (CajaTrashMonitor *trash_monitor, + gboolean state, + gpointer data) +{ + CajaPlacesSidebar *sidebar; + + sidebar = CAJA_PLACES_SIDEBAR (data); + + /* The trash icon changed, update the sidebar */ + update_places (sidebar); + + bookmarks_check_popup_sensitivity (sidebar); +} + +static void +caja_places_sidebar_init (CajaPlacesSidebar *sidebar) +{ + GtkTreeView *tree_view; + GtkTreeViewColumn *col; + GtkCellRenderer *cell; + GtkTreeSelection *selection; + + sidebar->volume_monitor = g_volume_monitor_get (); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN); + + /* tree view */ + tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); + gtk_tree_view_set_headers_visible (tree_view, FALSE); + + col = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); + + cell = gtk_cell_renderer_pixbuf_new (); + sidebar->icon_cell_renderer = cell; + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_attributes (col, cell, + "pixbuf", PLACES_SIDEBAR_COLUMN_ICON, + NULL); + + + cell = gtk_cell_renderer_text_new (); + sidebar->eject_text_cell_renderer = cell; + gtk_tree_view_column_pack_start (col, cell, TRUE); + gtk_tree_view_column_set_attributes (col, cell, + "text", PLACES_SIDEBAR_COLUMN_NAME, + "visible", PLACES_SIDEBAR_COLUMN_EJECT, + NULL); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + + + cell = gtk_cell_renderer_pixbuf_new (); + g_object_set (cell, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "icon-name", "media-eject", + "stock-size", GTK_ICON_SIZE_MENU, + "xpad", EJECT_BUTTON_XPAD, + NULL); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_attributes (col, cell, + "visible", PLACES_SIDEBAR_COLUMN_EJECT, + NULL); + + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, TRUE); + g_object_set (G_OBJECT (cell), "editable", FALSE, NULL); + gtk_tree_view_column_set_attributes (col, cell, + "text", PLACES_SIDEBAR_COLUMN_NAME, + "visible", PLACES_SIDEBAR_COLUMN_NO_EJECT, + "editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK, + NULL); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + + g_signal_connect (cell, "edited", + G_CALLBACK (bookmarks_edited), sidebar); + g_signal_connect (cell, "editing-canceled", + G_CALLBACK (bookmarks_editing_canceled), sidebar); + + gtk_tree_view_set_row_separator_func (tree_view, + caja_shortcuts_row_separator_func, + NULL, + NULL); + + /* this is required to align the eject buttons to the right */ + gtk_tree_view_column_set_max_width (GTK_TREE_VIEW_COLUMN (col), CAJA_ICON_SIZE_SMALLER); + gtk_tree_view_append_column (tree_view, col); + + sidebar->store = gtk_list_store_new (PLACES_SIDEBAR_COLUMN_COUNT, + G_TYPE_INT, + G_TYPE_STRING, + G_TYPE_DRIVE, + G_TYPE_VOLUME, + G_TYPE_MOUNT, + G_TYPE_STRING, + GDK_TYPE_PIXBUF, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_STRING + ); + + gtk_tree_view_set_tooltip_column (tree_view, PLACES_SIDEBAR_COLUMN_TOOLTIP); + + sidebar->filter_model = caja_shortcuts_model_filter_new (sidebar, + GTK_TREE_MODEL (sidebar->store), + NULL); + + gtk_tree_view_set_model (tree_view, sidebar->filter_model); + gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view)); + gtk_widget_show (GTK_WIDGET (tree_view)); + + gtk_widget_show (GTK_WIDGET (sidebar)); + sidebar->tree_view = tree_view; + + gtk_tree_view_set_search_column (tree_view, PLACES_SIDEBAR_COLUMN_NAME); + selection = gtk_tree_view_get_selection (tree_view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view), + GDK_BUTTON1_MASK, + caja_shortcuts_source_targets, + G_N_ELEMENTS (caja_shortcuts_source_targets), + GDK_ACTION_MOVE); + gtk_drag_dest_set (GTK_WIDGET (tree_view), + 0, + caja_shortcuts_drop_targets, G_N_ELEMENTS (caja_shortcuts_drop_targets), + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + + g_signal_connect (tree_view, "key-press-event", + G_CALLBACK (bookmarks_key_press_event_cb), sidebar); + + g_signal_connect (tree_view, "drag-motion", + G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (tree_view, "drag-leave", + G_CALLBACK (drag_leave_callback), sidebar); + g_signal_connect (tree_view, "drag-data-received", + G_CALLBACK (drag_data_received_callback), sidebar); + g_signal_connect (tree_view, "drag-drop", + G_CALLBACK (drag_drop_callback), sidebar); + g_signal_connect (selection, "changed", + G_CALLBACK (bookmarks_selection_changed_cb), sidebar); + g_signal_connect (tree_view, "popup-menu", + G_CALLBACK (bookmarks_popup_menu_cb), sidebar); + g_signal_connect (tree_view, "button-press-event", + G_CALLBACK (bookmarks_button_press_event_cb), sidebar); + g_signal_connect (tree_view, "button-release-event", + G_CALLBACK (bookmarks_button_release_event_cb), sidebar); + + eel_gtk_tree_view_set_activate_on_single_click (sidebar->tree_view, + TRUE); + + eel_preferences_add_callback_while_alive (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR, + desktop_location_changed_callback, + sidebar, + G_OBJECT (sidebar)); + + g_signal_connect_object (caja_trash_monitor_get (), + "trash_state_changed", + G_CALLBACK (trash_state_changed_cb), + sidebar, 0); +} + +static void +caja_places_sidebar_dispose (GObject *object) +{ + CajaPlacesSidebar *sidebar; + + sidebar = CAJA_PLACES_SIDEBAR (object); + + sidebar->window = NULL; + sidebar->tree_view = NULL; + + g_free (sidebar->uri); + sidebar->uri = NULL; + + free_drag_data (sidebar); + + if (sidebar->store != NULL) + { + g_object_unref (sidebar->store); + sidebar->store = NULL; + } + + if (sidebar->volume_monitor != NULL) + { + g_object_unref (sidebar->volume_monitor); + sidebar->volume_monitor = NULL; + } + + if (sidebar->bookmarks != NULL) + { + g_object_unref (sidebar->bookmarks); + sidebar->bookmarks = NULL; + } + + eel_remove_weak_pointer (&(sidebar->go_to_after_mount_slot)); + + G_OBJECT_CLASS (caja_places_sidebar_parent_class)->dispose (object); +} + +static void +caja_places_sidebar_class_init (CajaPlacesSidebarClass *class) +{ + G_OBJECT_CLASS (class)->dispose = caja_places_sidebar_dispose; + + GTK_WIDGET_CLASS (class)->style_set = caja_places_sidebar_style_set; +} + +static const char * +caja_places_sidebar_get_sidebar_id (CajaSidebar *sidebar) +{ + return CAJA_PLACES_SIDEBAR_ID; +} + +static char * +caja_places_sidebar_get_tab_label (CajaSidebar *sidebar) +{ + return g_strdup (_("Places")); +} + +static char * +caja_places_sidebar_get_tab_tooltip (CajaSidebar *sidebar) +{ + return g_strdup (_("Show Places")); +} + +static GdkPixbuf * +caja_places_sidebar_get_tab_icon (CajaSidebar *sidebar) +{ + return NULL; +} + +static void +caja_places_sidebar_is_visible_changed (CajaSidebar *sidebar, + gboolean is_visible) +{ + /* Do nothing */ +} + +static void +caja_places_sidebar_iface_init (CajaSidebarIface *iface) +{ + iface->get_sidebar_id = caja_places_sidebar_get_sidebar_id; + iface->get_tab_label = caja_places_sidebar_get_tab_label; + iface->get_tab_tooltip = caja_places_sidebar_get_tab_tooltip; + iface->get_tab_icon = caja_places_sidebar_get_tab_icon; + iface->is_visible_changed = caja_places_sidebar_is_visible_changed; +} + +static void +caja_places_sidebar_set_parent_window (CajaPlacesSidebar *sidebar, + CajaWindowInfo *window) +{ + CajaWindowSlotInfo *slot; + + sidebar->window = window; + + slot = caja_window_info_get_active_slot (window); + + sidebar->bookmarks = caja_bookmark_list_new (); + sidebar->uri = caja_window_slot_info_get_current_location (slot); + + g_signal_connect_object (sidebar->bookmarks, "contents_changed", + G_CALLBACK (update_places), + sidebar, G_CONNECT_SWAPPED); + + g_signal_connect_object (window, "loading_uri", + G_CALLBACK (loading_uri_callback), + sidebar, 0); + + g_signal_connect_object (sidebar->volume_monitor, "volume_added", + G_CALLBACK (volume_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_removed", + G_CALLBACK (volume_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_changed", + G_CALLBACK (volume_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_added", + G_CALLBACK (mount_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_removed", + G_CALLBACK (mount_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_changed", + G_CALLBACK (mount_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected", + G_CALLBACK (drive_disconnected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_connected", + G_CALLBACK (drive_connected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_changed", + G_CALLBACK (drive_changed_callback), sidebar, 0); + + update_places (sidebar); +} + +static void +caja_places_sidebar_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + CajaPlacesSidebar *sidebar; + + sidebar = CAJA_PLACES_SIDEBAR (widget); + + update_places (sidebar); +} + +static CajaSidebar * +caja_places_sidebar_create (CajaSidebarProvider *provider, + CajaWindowInfo *window) +{ + CajaPlacesSidebar *sidebar; + + sidebar = g_object_new (caja_places_sidebar_get_type (), NULL); + caja_places_sidebar_set_parent_window (sidebar, window); + g_object_ref_sink (sidebar); + + return CAJA_SIDEBAR (sidebar); +} + +static void +sidebar_provider_iface_init (CajaSidebarProviderIface *iface) +{ + iface->create = caja_places_sidebar_create; +} + +static void +caja_places_sidebar_provider_init (CajaPlacesSidebarProvider *sidebar) +{ +} + +static void +caja_places_sidebar_provider_class_init (CajaPlacesSidebarProviderClass *class) +{ +} + +void +caja_places_sidebar_register (void) +{ + caja_module_add_type (caja_places_sidebar_provider_get_type ()); +} + +/* Drag and drop interfaces */ + +static void +_caja_shortcuts_model_filter_class_init (CajaShortcutsModelFilterClass *class) +{ +} + +static void +_caja_shortcuts_model_filter_init (CajaShortcutsModelFilter *model) +{ + model->sidebar = NULL; +} + +/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */ +static gboolean +caja_shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + CajaShortcutsModelFilter *model; + int pos; + int bookmarks_pos; + int num_bookmarks; + + model = CAJA_SHORTCUTS_MODEL_FILTER (drag_source); + + pos = *gtk_tree_path_get_indices (path); + bookmarks_pos = get_bookmark_index (model->sidebar->tree_view); + num_bookmarks = caja_bookmark_list_length (model->sidebar->bookmarks); + + return (pos >= bookmarks_pos && pos < bookmarks_pos + num_bookmarks); +} + +/* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */ +static gboolean +caja_shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + CajaShortcutsModelFilter *model; + + model = CAJA_SHORTCUTS_MODEL_FILTER (drag_source); + + /* FIXME */ + + return FALSE; +} + +/* Fill the GtkTreeDragSourceIface vtable */ +static void +caja_shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = caja_shortcuts_model_filter_row_draggable; + iface->drag_data_get = caja_shortcuts_model_filter_drag_data_get; +} + +static GtkTreeModel * +caja_shortcuts_model_filter_new (CajaPlacesSidebar *sidebar, + GtkTreeModel *child_model, + GtkTreePath *root) +{ + CajaShortcutsModelFilter *model; + + model = g_object_new (CAJA_SHORTCUTS_MODEL_FILTER_TYPE, + "child-model", child_model, + "virtual-root", root, + NULL); + + model->sidebar = sidebar; + + return GTK_TREE_MODEL (model); +} |