Inserts a separator row (IS_SEPARATOR column) between local and remote bookmarks in the shortcuts model; the progress renderer draws it as a line. This is what thunar 4.20.2-1~bpo24.04+usagebars1 was built from.
1977 lines
74 KiB
C
1977 lines
74 KiB
C
/* vi:set et ai sw=2 sts=2 ts=2: */
|
|
/*-
|
|
* Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
|
|
* Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
|
|
* Copyright (c) 2012 Nick Schermer <nick@xfce.org>
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#include "thunar/thunar-application.h"
|
|
#include "thunar/thunar-browser.h"
|
|
#include "thunar/thunar-device-monitor.h"
|
|
#include "thunar/thunar-dialogs.h"
|
|
#include "thunar/thunar-dnd.h"
|
|
#include "thunar/thunar-gio-extensions.h"
|
|
#include "thunar/thunar-gtk-extensions.h"
|
|
#include "thunar/thunar-menu.h"
|
|
#include "thunar/thunar-navigator.h"
|
|
#include "thunar/thunar-preferences.h"
|
|
#include "thunar/thunar-private.h"
|
|
#include "thunar/thunar-shortcuts-icon-renderer.h"
|
|
#include "thunar/thunar-shortcuts-model.h"
|
|
#include "thunar/thunar-shortcuts-progress-renderer.h"
|
|
#include "thunar/thunar-shortcuts-view.h"
|
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <libxfce4util/libxfce4util.h>
|
|
|
|
|
|
|
|
/* Property identifiers */
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CURRENT_DIRECTORY,
|
|
};
|
|
|
|
/* Identifiers for DnD target types */
|
|
enum
|
|
{
|
|
GTK_TREE_MODEL_ROW,
|
|
TEXT_URI_LIST,
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_navigator_init (ThunarNavigatorIface *iface);
|
|
static void
|
|
thunar_shortcuts_view_finalize (GObject *object);
|
|
static void
|
|
thunar_shortcuts_view_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_shortcuts_view_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void
|
|
thunar_shortcuts_view_scale_changed (GObject *object,
|
|
GParamSpec *pspec,
|
|
gpointer user_data);
|
|
static ThunarFile *
|
|
thunar_shortcuts_view_get_current_directory (ThunarNavigator *navigator);
|
|
static void
|
|
thunar_shortcuts_view_set_current_directory (ThunarNavigator *navigator,
|
|
ThunarFile *current_directory);
|
|
static gboolean
|
|
thunar_shortcuts_view_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean
|
|
thunar_shortcuts_view_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *event);
|
|
static gboolean
|
|
thunar_shortcuts_view_key_release_event (GtkWidget *widget,
|
|
GdkEventKey *event);
|
|
static void
|
|
thunar_shortcuts_view_drag_begin (GtkWidget *widget,
|
|
GdkDragContext *context);
|
|
static void
|
|
thunar_shortcuts_view_drag_data_received (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint timestamp);
|
|
static gboolean
|
|
thunar_shortcuts_view_drag_drop (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp);
|
|
static gboolean
|
|
thunar_shortcuts_view_drag_motion (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp);
|
|
static void
|
|
thunar_shortcuts_view_drag_leave (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
guint timestamp);
|
|
static gboolean
|
|
thunar_shortcuts_view_popup_menu (GtkWidget *widget);
|
|
static void
|
|
thunar_shortcuts_view_row_activated (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column);
|
|
static gboolean
|
|
thunar_shortcuts_view_selection_func (GtkTreeSelection *selection,
|
|
GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
gboolean path_currently_selected,
|
|
gpointer user_data);
|
|
static void
|
|
thunar_shortcuts_view_context_menu_visibility (ThunarShortcutsView *view,
|
|
GdkEventButton *event,
|
|
GtkTreeModel *model);
|
|
static void
|
|
thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
|
|
GdkEventButton *event,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter);
|
|
static gboolean
|
|
thunar_shortcuts_view_remove_activated (ThunarShortcutsView *view,
|
|
GtkWidget *item);
|
|
static void
|
|
thunar_shortcuts_view_editing_canceled (GtkCellRenderer *renderer,
|
|
ThunarShortcutsView *view);
|
|
static gboolean
|
|
thunar_shortcuts_view_rename_activated (ThunarShortcutsView *view,
|
|
GtkWidget *item);
|
|
static void
|
|
thunar_shortcuts_view_renamed (GtkCellRenderer *renderer,
|
|
const gchar *path_string,
|
|
const gchar *text,
|
|
ThunarShortcutsView *view);
|
|
static GdkDragAction
|
|
thunar_shortcuts_view_compute_drop_actions (ThunarShortcutsView *view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkTreePath **path_return,
|
|
GdkDragAction *action_return,
|
|
GtkTreeViewDropPosition *position_return);
|
|
static GtkTreePath *
|
|
thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
|
|
gint x,
|
|
gint y);
|
|
static void
|
|
thunar_shortcuts_view_drop_uri_list (ThunarShortcutsView *view,
|
|
GList *path_list,
|
|
GtkTreePath *dst_path);
|
|
static void
|
|
thunar_shortcuts_view_open (ThunarShortcutsView *view,
|
|
ThunarActionManagerFolderOpenAction open_in);
|
|
static void
|
|
thunar_shortcuts_view_eject (ThunarShortcutsView *view);
|
|
static void
|
|
thunar_shortcuts_view_stop_spinner (ThunarShortcutsView *view,
|
|
ThunarDevice *device,
|
|
gpointer user_data);
|
|
static void
|
|
thunar_shortcuts_view_start_spinner (ThunarShortcutsView *view,
|
|
ThunarDevice *device,
|
|
gpointer user_data);
|
|
static void
|
|
thunar_shortcuts_view_new_files_created (ThunarShortcutsView *view,
|
|
GList *files_to_selected);
|
|
|
|
|
|
|
|
struct _ThunarShortcutsViewClass
|
|
{
|
|
GtkTreeViewClass __parent__;
|
|
};
|
|
|
|
struct _ThunarShortcutsView
|
|
{
|
|
GtkTreeView __parent__;
|
|
|
|
|
|
ThunarPreferences *preferences;
|
|
GtkCellRenderer *icon_renderer;
|
|
GtkCellRenderer *padding_renderer;
|
|
GtkTreeViewColumn *column;
|
|
gboolean padding_enabled;
|
|
|
|
ThunarxProviderFactory *provider_factory;
|
|
|
|
/* the currently pressed mouse button, set in the
|
|
* button-press-event handler if the associated
|
|
* button-release-event should activate.
|
|
*/
|
|
gint pressed_button;
|
|
guint pressed_eject_button : 1;
|
|
|
|
/* drop site support */
|
|
guint drop_data_ready : 1; /* whether the drop data was received already */
|
|
guint drop_occurred : 1;
|
|
GList *drop_file_list; /* the list of URIs that are contained in the drop data */
|
|
|
|
/* id of the signal used to queue a resize on the
|
|
* column whenever the shortcuts icon size is changed.
|
|
*/
|
|
gulong queue_resize_signal_id;
|
|
|
|
/* used to create menu items for the context menu */
|
|
ThunarActionManager *action_mgr;
|
|
ThunarFile *current_directory;
|
|
|
|
/* reference to the underlying model */
|
|
ThunarShortcutsModel *model;
|
|
};
|
|
|
|
|
|
|
|
/* Target types for dragging from the shortcuts view */
|
|
static const GtkTargetEntry drag_targets[] = {
|
|
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
|
|
};
|
|
|
|
/* Target types for dropping into the shortcuts view */
|
|
static const GtkTargetEntry drop_targets[] = {
|
|
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW },
|
|
{ "text/uri-list", 0, TEXT_URI_LIST },
|
|
};
|
|
|
|
/* #XfceGtkActionEntrys provided by this widget */
|
|
typedef enum
|
|
{
|
|
THUNAR_SHORTCUTS_VIEW_ACTION_RENAME_BOOKMARK,
|
|
THUNAR_SHORTCUTS_VIEW_ACTION_REMOVE_BOOKMARK,
|
|
} ThunarShortcutsViewAction;
|
|
|
|
/* clang-format off */
|
|
static XfceGtkActionEntry thunar_shortcuts_view_action_entries[] =
|
|
{
|
|
{ THUNAR_SHORTCUTS_VIEW_ACTION_RENAME_BOOKMARK, "<Actions>/ThunarShortcutsView/rename-bookmark", "", XFCE_GTK_MENU_ITEM, N_("Re_name Shortcut"), NULL, "", G_CALLBACK (thunar_shortcuts_view_rename_activated), },
|
|
{ THUNAR_SHORTCUTS_VIEW_ACTION_REMOVE_BOOKMARK, "<Actions>/ThunarShortcutsView/remove-bookmark", "", XFCE_GTK_IMAGE_MENU_ITEM, N_("_Remove Shortcut"), NULL, "list-remove", G_CALLBACK (thunar_shortcuts_view_remove_activated), },
|
|
};
|
|
/* clang-format on */
|
|
|
|
#define get_action_entry(id) xfce_gtk_get_action_entry_by_id (thunar_shortcuts_view_action_entries, G_N_ELEMENTS (thunar_shortcuts_view_action_entries), id)
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsView, thunar_shortcuts_view, GTK_TYPE_TREE_VIEW,
|
|
G_IMPLEMENT_INTERFACE (THUNAR_TYPE_NAVIGATOR, thunar_shortcuts_view_navigator_init))
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_class_init (ThunarShortcutsViewClass *klass)
|
|
{
|
|
GtkTreeViewClass *gtktree_view_class;
|
|
GtkWidgetClass *gtkwidget_class;
|
|
GObjectClass *gobject_class;
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->finalize = thunar_shortcuts_view_finalize;
|
|
gobject_class->get_property = thunar_shortcuts_view_get_property;
|
|
gobject_class->set_property = thunar_shortcuts_view_set_property;
|
|
|
|
gtkwidget_class = GTK_WIDGET_CLASS (klass);
|
|
gtkwidget_class->button_press_event = thunar_shortcuts_view_button_press_event;
|
|
gtkwidget_class->button_release_event = thunar_shortcuts_view_button_release_event;
|
|
gtkwidget_class->key_release_event = thunar_shortcuts_view_key_release_event;
|
|
gtkwidget_class->drag_begin = thunar_shortcuts_view_drag_begin;
|
|
gtkwidget_class->drag_data_received = thunar_shortcuts_view_drag_data_received;
|
|
gtkwidget_class->drag_drop = thunar_shortcuts_view_drag_drop;
|
|
gtkwidget_class->drag_motion = thunar_shortcuts_view_drag_motion;
|
|
gtkwidget_class->drag_leave = thunar_shortcuts_view_drag_leave;
|
|
gtkwidget_class->popup_menu = thunar_shortcuts_view_popup_menu;
|
|
|
|
gtktree_view_class = GTK_TREE_VIEW_CLASS (klass);
|
|
gtktree_view_class->row_activated = thunar_shortcuts_view_row_activated;
|
|
|
|
/* Override ThunarNavigator's properties */
|
|
g_object_class_override_property (gobject_class, PROP_CURRENT_DIRECTORY, "current-directory");
|
|
|
|
xfce_gtk_translate_action_entries (thunar_shortcuts_view_action_entries, G_N_ELEMENTS (thunar_shortcuts_view_action_entries));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_navigator_init (ThunarNavigatorIface *iface)
|
|
{
|
|
iface->get_current_directory = thunar_shortcuts_view_get_current_directory;
|
|
iface->set_current_directory = thunar_shortcuts_view_set_current_directory;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_row_separator (GtkTreeModel *model,
|
|
GtkTreeIter *iter,
|
|
gpointer data)
|
|
{
|
|
gboolean is_separator = FALSE;
|
|
gtk_tree_model_get (model, iter,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR, &is_separator,
|
|
-1);
|
|
return is_separator;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_init (ThunarShortcutsView *view)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreeSelection *selection;
|
|
|
|
/* configure the tree view */
|
|
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE);
|
|
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
|
|
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (view), THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP);
|
|
|
|
/* no row_separator_func — separator is drawn by the progress renderer */
|
|
|
|
/* grab a reference on the provider factory */
|
|
view->provider_factory = thunarx_provider_factory_get_default ();
|
|
|
|
/* grab a reference on the preferences; be sure to redraw the view
|
|
* whenever the "shortcuts-icon-emblems" preference changes.
|
|
*/
|
|
view->preferences = thunar_preferences_get ();
|
|
g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::shortcuts-icon-emblems", G_CALLBACK (gtk_widget_queue_draw), view);
|
|
|
|
/* allocate a single column for our renderers */
|
|
column = view->column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN,
|
|
"reorderable", FALSE,
|
|
"resizable", FALSE,
|
|
"expand", TRUE,
|
|
"spacing", 2,
|
|
NULL);
|
|
gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
|
|
|
|
/* queue a resize on the column whenever the icon size is changed */
|
|
view->queue_resize_signal_id = g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::shortcuts-icon-size",
|
|
G_CALLBACK (gtk_tree_view_column_queue_resize), column);
|
|
|
|
/* header */
|
|
renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
|
|
"weight", PANGO_WEIGHT_BOLD,
|
|
"xpad", 6,
|
|
"ypad", 4,
|
|
"ellipsize", PANGO_ELLIPSIZE_END,
|
|
NULL);
|
|
gtk_tree_view_column_pack_end (column, renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER,
|
|
NULL);
|
|
|
|
/* separator for indent */
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
g_object_set (G_OBJECT (renderer), "xpad", 6, NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
|
|
NULL);
|
|
|
|
/* allocate the special icon renderer */
|
|
view->icon_renderer = thunar_shortcuts_icon_renderer_new ();
|
|
gtk_tree_view_column_pack_start (column, view->icon_renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, view->icon_renderer,
|
|
"gicon", THUNAR_SHORTCUTS_MODEL_COLUMN_GICON,
|
|
"file", THUNAR_SHORTCUTS_MODEL_COLUMN_FILE,
|
|
"device", THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
|
|
NULL);
|
|
g_signal_connect (G_OBJECT (view), "notify::scale-factor", G_CALLBACK (thunar_shortcuts_view_scale_changed), NULL);
|
|
|
|
/* sync the "emblems" property of the icon renderer with the "shortcuts-icon-emblems" preference
|
|
* and the "size" property of the renderer with the "shortcuts-icon-size" preference.
|
|
*/
|
|
g_object_bind_property (G_OBJECT (view->preferences), "shortcuts-icon-size", G_OBJECT (view->icon_renderer), "size", G_BINDING_SYNC_CREATE);
|
|
g_object_bind_property (G_OBJECT (view->preferences), "shortcuts-icon-emblems", G_OBJECT (view->icon_renderer), "emblems", G_BINDING_SYNC_CREATE);
|
|
|
|
/* optional symbolic icons */
|
|
g_object_bind_property (G_OBJECT (view->preferences), "misc-symbolic-icons-in-sidepane", G_OBJECT (view->icon_renderer), "use-symbolic-icons", G_BINDING_SYNC_CREATE);
|
|
g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::misc-symbolic-icons-in-sidepane", G_CALLBACK (gtk_widget_queue_draw), view);
|
|
|
|
/* allocate the text+progress renderer for device names with usage bars */
|
|
renderer = thunar_shortcuts_progress_renderer_new ();
|
|
g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (thunar_shortcuts_view_renamed), view);
|
|
gtk_tree_view_column_pack_start (column, renderer, TRUE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
|
|
"usage-percent", THUNAR_SHORTCUTS_MODEL_COLUMN_USAGE_PERCENT,
|
|
"is-separator", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
|
|
NULL);
|
|
|
|
/* spinner to indicate (un)mount/eject delay */
|
|
renderer = gtk_cell_renderer_spinner_new ();
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
|
|
"active", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
|
|
"pulse", THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE,
|
|
NULL);
|
|
|
|
/* allocate icon renderer for the eject symbol */
|
|
renderer = gtk_cell_renderer_pixbuf_new ();
|
|
g_object_set (renderer, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, "icon-name", "media-eject-symbolic", NULL);
|
|
gtk_tree_view_column_pack_start (column, renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, renderer,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT,
|
|
NULL);
|
|
|
|
/* padding for the eject symbol so it's not covered by scroll bar */
|
|
view->padding_enabled = FALSE;
|
|
view->padding_renderer = gtk_cell_renderer_text_new ();
|
|
g_object_set (G_OBJECT (view->padding_renderer), "xpad", 6, NULL);
|
|
gtk_tree_view_column_pack_start (column, view->padding_renderer, FALSE);
|
|
gtk_tree_view_column_set_attributes (column, view->padding_renderer,
|
|
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT,
|
|
NULL);
|
|
|
|
/* enable drag support for the shortcuts view (actually used to support reordering) */
|
|
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (view), GDK_BUTTON1_MASK, drag_targets,
|
|
G_N_ELEMENTS (drag_targets), GDK_ACTION_MOVE);
|
|
|
|
/* enable drop support for the shortcuts view (both internal reordering
|
|
* and adding new shortcuts from other widgets)
|
|
*/
|
|
gtk_drag_dest_set (GTK_WIDGET (view), 0, drop_targets, G_N_ELEMENTS (drop_targets),
|
|
GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
|
|
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
gtk_tree_selection_set_select_function (selection, thunar_shortcuts_view_selection_func, NULL, NULL);
|
|
|
|
view->action_mgr = g_object_new (THUNAR_TYPE_ACTION_MANAGER, "widget", GTK_WIDGET (view), NULL);
|
|
|
|
g_signal_connect_swapped (G_OBJECT (view->action_mgr), "change-directory", G_CALLBACK (thunar_navigator_change_directory), view);
|
|
g_signal_connect_swapped (G_OBJECT (view->action_mgr), "open-new-tab", G_CALLBACK (thunar_navigator_open_new_tab), view);
|
|
g_signal_connect_swapped (G_OBJECT (view->action_mgr), "device-operation-started", G_CALLBACK (thunar_shortcuts_view_start_spinner), view);
|
|
g_signal_connect_swapped (G_OBJECT (view->action_mgr), "device-operation-finished", G_CALLBACK (thunar_shortcuts_view_stop_spinner), view);
|
|
g_signal_connect_swapped (G_OBJECT (view->action_mgr), "new-files-created", G_CALLBACK (thunar_shortcuts_view_new_files_created), view);
|
|
g_object_bind_property (G_OBJECT (view), "current-directory", G_OBJECT (view->action_mgr), "current-directory", G_BINDING_SYNC_CREATE);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_finalize (GObject *object)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (object);
|
|
|
|
/* Disconnect from the model */
|
|
g_signal_handlers_disconnect_by_data (G_OBJECT (view->model), view);
|
|
|
|
/* release our reference on the model */
|
|
g_object_unref (view->model);
|
|
|
|
/* release drop path list (if drag_leave wasn't called) */
|
|
thunar_g_list_free_full (view->drop_file_list);
|
|
|
|
/* release the provider factory */
|
|
g_object_unref (G_OBJECT (view->provider_factory));
|
|
|
|
/* disconnect the queue resize signal handler */
|
|
g_signal_handler_disconnect (G_OBJECT (view->preferences), view->queue_resize_signal_id);
|
|
|
|
/* reset the current-directory property */
|
|
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (view), NULL);
|
|
|
|
/* release reference on the action manager */
|
|
g_object_unref (view->action_mgr);
|
|
|
|
/* disconnect from the preferences object */
|
|
g_signal_handlers_disconnect_by_data (G_OBJECT (view->preferences), view);
|
|
g_object_unref (G_OBJECT (view->preferences));
|
|
|
|
(*G_OBJECT_CLASS (thunar_shortcuts_view_parent_class)->finalize) (object);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CURRENT_DIRECTORY:
|
|
g_value_set_object (value, thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (object)));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (prop_id)
|
|
{
|
|
case PROP_CURRENT_DIRECTORY:
|
|
thunar_navigator_set_current_directory (THUNAR_NAVIGATOR (object), g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_scale_changed (GObject *object,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
gtk_widget_queue_draw (GTK_WIDGET (object));
|
|
}
|
|
|
|
|
|
|
|
static ThunarFile *
|
|
thunar_shortcuts_view_get_current_directory (ThunarNavigator *navigator)
|
|
{
|
|
return THUNAR_SHORTCUTS_VIEW (navigator)->current_directory;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_set_current_directory (ThunarNavigator *navigator,
|
|
ThunarFile *current_directory)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (navigator);
|
|
|
|
/* check if we already use that directory */
|
|
if (G_UNLIKELY (view->current_directory == current_directory))
|
|
return;
|
|
|
|
/* check if we have an active directory */
|
|
if (G_LIKELY (view->current_directory != NULL))
|
|
{
|
|
/* disconnect from the previous current directory */
|
|
g_object_unref (G_OBJECT (view->current_directory));
|
|
}
|
|
|
|
/* activate the new current directory */
|
|
view->current_directory = current_directory;
|
|
|
|
/* connect to the new current directory */
|
|
if (G_LIKELY (current_directory != NULL))
|
|
{
|
|
/* take a reference on the directory */
|
|
g_object_ref (G_OBJECT (current_directory));
|
|
}
|
|
|
|
/* notify listeners */
|
|
g_object_notify (G_OBJECT (view), "current-directory");
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_button_press_event (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
gboolean result;
|
|
gboolean can_eject;
|
|
gboolean button_clicked;
|
|
gint icon_width, column_width;
|
|
|
|
/* reset the pressed button state */
|
|
view->pressed_button = -1;
|
|
view->pressed_eject_button = 0;
|
|
|
|
/* completely ignore double click events */
|
|
if (event->type == GDK_2BUTTON_PRESS)
|
|
return TRUE;
|
|
|
|
/* let the widget process the event first (handles focussing and scrolling) */
|
|
result = (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->button_press_event) (widget, event);
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
|
|
/* resolve the path at the cursor position */
|
|
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), event->x, event->y, &path, NULL, NULL, NULL))
|
|
{
|
|
/* check if we should popup the context menu */
|
|
if (G_UNLIKELY (event->button == 3))
|
|
{
|
|
/* determine the iterator for the path */
|
|
if (gtk_tree_model_get_iter (model, &iter, path))
|
|
{
|
|
/* popup the context menu */
|
|
thunar_shortcuts_view_context_menu (view, event, model, &iter);
|
|
|
|
/* we effectively handled the event */
|
|
result = TRUE;
|
|
}
|
|
}
|
|
else if (event->button == 1 || event->button == 2)
|
|
{
|
|
/* check if we clicked the eject button area */
|
|
column_width = gtk_tree_view_column_get_width (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0));
|
|
gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &icon_width, NULL);
|
|
|
|
if (gtk_get_locale_direction () == GTK_TEXT_DIR_LTR)
|
|
button_clicked = event->x >= column_width - icon_width - (view->padding_enabled ? 16 : 3);
|
|
else
|
|
button_clicked = event->x <= icon_width + (view->padding_enabled ? 16 : 3);
|
|
|
|
if (event->button == 1 && button_clicked)
|
|
{
|
|
/* check if that shortcut actually has an eject button */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
if (gtk_tree_model_get_iter (model, &iter, path))
|
|
{
|
|
gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT, &can_eject, -1);
|
|
if (can_eject)
|
|
view->pressed_eject_button = 1;
|
|
}
|
|
}
|
|
else
|
|
gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), FALSE);
|
|
|
|
/* remember the button as pressed and handle it in the release handler */
|
|
view->pressed_button = event->button;
|
|
}
|
|
|
|
/* release the path */
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else if (event->button == 3)
|
|
{
|
|
thunar_shortcuts_view_context_menu_visibility (view, event, model);
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_button_release_event (GtkWidget *widget,
|
|
GdkEventButton *event)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
gboolean in_tab;
|
|
|
|
/* check if we have an event matching the pressed button state */
|
|
if (G_LIKELY (view->pressed_button == (gint) event->button))
|
|
{
|
|
/* check if the eject button has been pressed */
|
|
if (view->pressed_eject_button)
|
|
{
|
|
thunar_shortcuts_view_eject (view);
|
|
}
|
|
else if (G_LIKELY (event->button == 1))
|
|
{
|
|
/* button 1 opens in the same window */
|
|
thunar_shortcuts_view_open (view, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY);
|
|
}
|
|
else if (G_UNLIKELY (event->button == 2))
|
|
{
|
|
/* button 2 opens in a new window or tab */
|
|
g_object_get (view->preferences, "misc-middle-click-in-tab", &in_tab, NULL);
|
|
|
|
/* holding ctrl inverts the action */
|
|
if ((event->state & GDK_CONTROL_MASK) != 0)
|
|
in_tab = !in_tab;
|
|
|
|
thunar_shortcuts_view_open (view, in_tab ? THUNAR_ACTION_MANAGER_OPEN_AS_NEW_TAB : THUNAR_ACTION_MANAGER_OPEN_AS_NEW_WINDOW);
|
|
}
|
|
}
|
|
|
|
/* reset the pressed button state */
|
|
view->pressed_button = -1;
|
|
view->pressed_eject_button = 0;
|
|
|
|
/* call the parent's release event handler */
|
|
return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->button_release_event) (widget, event);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_key_release_event (GtkWidget *widget,
|
|
GdkEventKey *event)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
|
|
/* work nicer with keyboard navigation */
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_KEY_Up:
|
|
case GDK_KEY_Down:
|
|
case GDK_KEY_KP_Up:
|
|
case GDK_KEY_KP_Down:
|
|
thunar_shortcuts_view_open (view, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY);
|
|
|
|
/* keep focus on us */
|
|
gtk_widget_grab_focus (widget);
|
|
break;
|
|
}
|
|
|
|
/* call the parent's release event handler */
|
|
return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->key_release_event) (widget, event);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_drag_begin (GtkWidget *widget,
|
|
GdkDragContext *context)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
|
|
/* reset the pressed button state */
|
|
view->pressed_button = -1;
|
|
|
|
/* call the parent's drag_begin() handler */
|
|
if (GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_begin != NULL)
|
|
(*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_begin) (widget, context);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_drag_data_received (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData *selection_data,
|
|
guint info,
|
|
guint timestamp)
|
|
{
|
|
GtkTreeViewDropPosition position = GTK_TREE_VIEW_DROP_BEFORE;
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
GdkDragAction actions;
|
|
GdkDragAction action;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
ThunarFile *file;
|
|
gboolean succeed = FALSE;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
/* check if we don't already know the drop data */
|
|
if (G_LIKELY (!view->drop_data_ready))
|
|
{
|
|
/* extract the URI list from the selection data (if valid) */
|
|
if (info == TEXT_URI_LIST && gtk_selection_data_get_format (selection_data) == 8 && gtk_selection_data_get_length (selection_data) > 0)
|
|
view->drop_file_list = thunar_g_file_list_new_from_string ((const gchar *) gtk_selection_data_get_data (selection_data));
|
|
|
|
/* reset the state */
|
|
view->drop_data_ready = TRUE;
|
|
}
|
|
|
|
/* check if the data was droppped */
|
|
if (G_UNLIKELY (view->drop_occurred))
|
|
{
|
|
/* reset the state */
|
|
view->drop_occurred = FALSE;
|
|
|
|
/* verify that we only handle text/uri-list here */
|
|
if (G_LIKELY (info == TEXT_URI_LIST))
|
|
{
|
|
/* determine the drop actions */
|
|
actions = thunar_shortcuts_view_compute_drop_actions (view, context, x, y, &path, &action, &position);
|
|
if (G_LIKELY (actions != 0))
|
|
{
|
|
/* check if we should add a shortcut */
|
|
if (position == GTK_TREE_VIEW_DROP_BEFORE || position == GTK_TREE_VIEW_DROP_AFTER)
|
|
{
|
|
/* if position is "after", we need to advance the path,
|
|
* as the drop_uri_list() will insert "at" the path.
|
|
*/
|
|
if (position == GTK_TREE_VIEW_DROP_AFTER)
|
|
gtk_tree_path_next (path);
|
|
|
|
/* just add the required shortcuts then */
|
|
thunar_shortcuts_view_drop_uri_list (view, view->drop_file_list, path);
|
|
}
|
|
else if (G_LIKELY ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK)) != 0))
|
|
{
|
|
/* get the shortcuts model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
|
|
/* determine the iterator for the path */
|
|
if (gtk_tree_model_get_iter (model, &iter, path))
|
|
{
|
|
/* determine the file for the iter */
|
|
gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file, -1);
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
/* ask the user what to do with the drop data */
|
|
if (G_UNLIKELY (action == GDK_ACTION_ASK))
|
|
action = thunar_dnd_ask (widget, file, view->drop_file_list, actions);
|
|
|
|
/* perform the requested action */
|
|
if (G_LIKELY (action != 0))
|
|
{
|
|
/* really perform the drop :-) */
|
|
succeed = thunar_dnd_perform (widget, file, view->drop_file_list, action, NULL);
|
|
}
|
|
|
|
/* release the file */
|
|
g_object_unref (G_OBJECT (file));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* release the tree path */
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
/* disable the drop highlighting */
|
|
thunar_shortcuts_view_drag_leave (widget, context, timestamp);
|
|
|
|
/* tell the peer that we handled the drop */
|
|
gtk_drag_finish (context, succeed, FALSE, timestamp);
|
|
}
|
|
else
|
|
{
|
|
gdk_drag_status (context, 0, timestamp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_drag_drop (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *dst_path;
|
|
GtkTreePath *src_path;
|
|
GtkTreeIter iter;
|
|
GdkAtom target;
|
|
GtkTreeModel *child_model;
|
|
GtkTreePath *child_dst_path;
|
|
GtkTreePath *child_src_path;
|
|
|
|
_thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view), FALSE);
|
|
|
|
/* determine the drop target */
|
|
target = gtk_drag_dest_find_target (widget, context, NULL);
|
|
if (G_LIKELY (target == gdk_atom_intern_static_string ("text/uri-list")))
|
|
{
|
|
/* set state so the drag-data-received handler
|
|
* knows that this is really a drop this time.
|
|
*/
|
|
view->drop_occurred = TRUE;
|
|
|
|
/* request the drag data from the source. */
|
|
gtk_drag_get_data (widget, context, target, timestamp);
|
|
}
|
|
else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
|
|
{
|
|
/* compute the drop position */
|
|
dst_path = thunar_shortcuts_view_compute_drop_position (view, x, y);
|
|
|
|
/* determine the source path */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
/* get the source path */
|
|
src_path = gtk_tree_model_get_path (model, &iter);
|
|
|
|
/* if we drop downwards, correct the drop destination */
|
|
if (gtk_tree_path_compare (src_path, dst_path) < 0)
|
|
gtk_tree_path_prev (dst_path);
|
|
|
|
/* convert iters */
|
|
src_path = gtk_tree_model_get_path (model, &iter);
|
|
child_src_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), src_path);
|
|
child_dst_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), dst_path);
|
|
gtk_tree_path_free (src_path);
|
|
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
|
|
/* perform the move */
|
|
thunar_shortcuts_model_move (THUNAR_SHORTCUTS_MODEL (child_model), child_src_path, child_dst_path);
|
|
|
|
gtk_tree_path_free (child_src_path);
|
|
gtk_tree_path_free (child_dst_path);
|
|
|
|
/* make sure the new position is selectde */
|
|
gtk_tree_selection_select_path (selection, dst_path);
|
|
}
|
|
|
|
/* release the dst path */
|
|
gtk_tree_path_free (dst_path);
|
|
|
|
/* finish the dnd operation */
|
|
gtk_drag_finish (context, TRUE, FALSE, timestamp);
|
|
}
|
|
else
|
|
{
|
|
/* we cannot handle the drop */
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_drag_motion (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
guint timestamp)
|
|
{
|
|
GtkTreeViewDropPosition position = GTK_TREE_VIEW_DROP_BEFORE;
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
GdkDragAction action = 0;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path = NULL;
|
|
GdkAtom target;
|
|
|
|
/* reset the "drop-file" of the icon renderer */
|
|
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
|
|
|
|
/* determine the drag target */
|
|
target = gtk_drag_dest_find_target (widget, context, NULL);
|
|
if (target == gdk_atom_intern_static_string ("text/uri-list"))
|
|
{
|
|
/* request the drop data on-demand (if we don't have it already) */
|
|
if (G_UNLIKELY (!view->drop_data_ready))
|
|
{
|
|
/* request the drag data from the source */
|
|
gtk_drag_get_data (widget, context, target, timestamp);
|
|
|
|
/* gdk_drag_status() will be called by drag_data_received */
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* compute the drop position */
|
|
thunar_shortcuts_view_compute_drop_actions (view, context, x, y, &path, &action, &position);
|
|
}
|
|
}
|
|
else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW"))
|
|
{
|
|
/* check the action that should be performed */
|
|
if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_LINK || (gdk_drag_context_get_actions (context) & GDK_ACTION_LINK) != 0)
|
|
action = GDK_ACTION_LINK;
|
|
else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY || (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
|
|
action = GDK_ACTION_COPY;
|
|
else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE || (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
|
|
action = GDK_ACTION_MOVE;
|
|
else
|
|
return FALSE;
|
|
|
|
/* compute the drop position for the coordinates */
|
|
path = thunar_shortcuts_view_compute_drop_position (view, x, y);
|
|
if (path == NULL)
|
|
return FALSE;
|
|
|
|
/* check if path is about to append to the model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
if (gtk_tree_path_get_indices (path)[0] >= gtk_tree_model_iter_n_children (model, NULL))
|
|
{
|
|
/* set the position to "after" and move the path to
|
|
* point to the previous row instead; required to
|
|
* get the highlighting in GtkTreeView correct.
|
|
*/
|
|
position = GTK_TREE_VIEW_DROP_AFTER;
|
|
gtk_tree_path_prev (path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* we cannot handle the drop */
|
|
return FALSE;
|
|
}
|
|
|
|
/* check if we have a drop path */
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
/* highlight the appropriate row */
|
|
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), path, position);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
else
|
|
{
|
|
/* remove highlight */
|
|
gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (view), NULL, position);
|
|
}
|
|
|
|
/* tell Gdk whether we can drop here */
|
|
gdk_drag_status (context, action, timestamp);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_drag_leave (GtkWidget *widget,
|
|
GdkDragContext *context,
|
|
guint timestamp)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
|
|
/* reset the "drop-file" of the icon renderer */
|
|
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", NULL, NULL);
|
|
|
|
/* reset the "drop data ready" status and free the URI list */
|
|
if (G_LIKELY (view->drop_data_ready))
|
|
{
|
|
thunar_g_list_free_full (view->drop_file_list);
|
|
view->drop_data_ready = FALSE;
|
|
view->drop_file_list = NULL;
|
|
}
|
|
|
|
/* schedule a repaint to make sure the special drop icon for the target row
|
|
* is reset to its default (https://bugzilla.xfce.org/show_bug.cgi?id=2498).
|
|
*/
|
|
gtk_widget_queue_draw (widget);
|
|
|
|
/* call the parent's handler */
|
|
(*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->drag_leave) (widget, context, timestamp);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_popup_menu (GtkWidget *widget)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (widget);
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
|
|
/* determine the selection for the tree view */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
|
|
/* determine the selected row */
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
/* popup the context menu */
|
|
thunar_shortcuts_view_context_menu (view, NULL, model, &iter);
|
|
return TRUE;
|
|
}
|
|
else if (GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->popup_menu != NULL)
|
|
{
|
|
/* call the parent's "popup-menu" handler */
|
|
return (*GTK_WIDGET_CLASS (thunar_shortcuts_view_parent_class)->popup_menu) (widget);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_row_activated (GtkTreeView *tree_view,
|
|
GtkTreePath *path,
|
|
GtkTreeViewColumn *column)
|
|
{
|
|
ThunarShortcutsView *view = THUNAR_SHORTCUTS_VIEW (tree_view);
|
|
|
|
/* call the parent's "row-activated" handler */
|
|
if (GTK_TREE_VIEW_CLASS (thunar_shortcuts_view_parent_class)->row_activated != NULL)
|
|
(*GTK_TREE_VIEW_CLASS (thunar_shortcuts_view_parent_class)->row_activated) (tree_view, path, column);
|
|
|
|
/* open the selected shortcut */
|
|
thunar_shortcuts_view_open (view, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_selection_func (GtkTreeSelection *selection,
|
|
GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
gboolean path_currently_selected,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeIter iter;
|
|
gboolean is_header;
|
|
|
|
/* don't allow selecting headers */
|
|
gtk_tree_model_get_iter (model, &iter, path);
|
|
gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER, &is_header, -1);
|
|
return !is_header;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_context_menu_visibility_toggled (GtkCheckMenuItem *item,
|
|
GtkTreeModel *model)
|
|
{
|
|
gboolean hidden;
|
|
GtkTreePath *path;
|
|
GtkTreeRowReference *row;
|
|
|
|
_thunar_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
|
|
|
|
row = g_object_get_data (G_OBJECT (item), I_ ("thunar-shortcuts-row"));
|
|
path = gtk_tree_row_reference_get_path (row);
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
hidden = !gtk_check_menu_item_get_active (item);
|
|
thunar_shortcuts_model_set_hidden (THUNAR_SHORTCUTS_MODEL (model), path, hidden);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_context_menu_visibility (ThunarShortcutsView *view,
|
|
GdkEventButton *event,
|
|
GtkTreeModel *model)
|
|
{
|
|
GtkTreeIter iter;
|
|
guint mask = 0;
|
|
ThunarShortcutGroup group;
|
|
GtkWidget *menu;
|
|
GtkWidget *mi;
|
|
gchar *label;
|
|
GtkTreeSelection *selection;
|
|
gboolean visible;
|
|
GtkTreePath *path;
|
|
GtkTreeModel *child_model;
|
|
GtkWidget *submenu;
|
|
|
|
/* unselect the items */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
gtk_tree_selection_unselect_all (GTK_TREE_SELECTION (selection));
|
|
|
|
/* prepare the popup menu */
|
|
menu = submenu = gtk_menu_new ();
|
|
|
|
/* process all items below the header */
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (child_model), &iter))
|
|
return;
|
|
path = gtk_tree_model_get_path (child_model, &iter);
|
|
do
|
|
{
|
|
/* get all the info */
|
|
gtk_tree_model_get (GTK_TREE_MODEL (child_model), &iter,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP, &group,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_NAME, &label,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE, &visible,
|
|
-1);
|
|
|
|
if ((group & THUNAR_SHORTCUT_GROUP_HEADER) != 0)
|
|
{
|
|
/* get the mask of the group */
|
|
if ((group & THUNAR_SHORTCUT_GROUP_DEVICES) != 0)
|
|
mask = THUNAR_SHORTCUT_GROUP_DEVICES;
|
|
else if ((group & THUNAR_SHORTCUT_GROUP_PLACES) != 0)
|
|
mask = THUNAR_SHORTCUT_GROUP_PLACES;
|
|
else if ((group & THUNAR_SHORTCUT_GROUP_NETWORK) != 0)
|
|
mask = THUNAR_SHORTCUT_GROUP_NETWORK;
|
|
else
|
|
_thunar_assert_not_reached ();
|
|
|
|
/* create menu items */
|
|
mi = gtk_menu_item_new_with_label (label);
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
|
|
gtk_widget_show (mi);
|
|
|
|
submenu = gtk_menu_new ();
|
|
gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu);
|
|
}
|
|
else if ((group & mask) != 0)
|
|
{
|
|
mi = gtk_check_menu_item_new_with_label (label);
|
|
gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mi);
|
|
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), visible);
|
|
g_object_set_data_full (G_OBJECT (mi), I_ ("thunar-shortcuts-row"),
|
|
gtk_tree_row_reference_new (child_model, path),
|
|
(GDestroyNotify) gtk_tree_row_reference_free);
|
|
g_signal_connect (G_OBJECT (mi), "toggled",
|
|
G_CALLBACK (thunar_shortcuts_view_context_menu_visibility_toggled), child_model);
|
|
gtk_widget_show (mi);
|
|
}
|
|
|
|
g_free (label);
|
|
gtk_tree_path_next (path);
|
|
}
|
|
while (gtk_tree_model_iter_next (child_model, &iter));
|
|
|
|
gtk_tree_path_free (path);
|
|
|
|
/* run the menu (taking over the floating reference on menu) */
|
|
thunar_gtk_menu_run (GTK_MENU (menu));
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_context_menu (ThunarShortcutsView *view,
|
|
GdkEventButton *event,
|
|
GtkTreeModel *model,
|
|
GtkTreeIter *iter)
|
|
{
|
|
GtkTreePath *path;
|
|
ThunarFile *file;
|
|
GtkWidget *item;
|
|
GtkWidget *window;
|
|
gboolean mutable;
|
|
ThunarDevice *device;
|
|
ThunarShortcutGroup group;
|
|
gboolean is_header;
|
|
GFile *location;
|
|
GList *files;
|
|
ThunarMenu *context_menu;
|
|
|
|
/* check if this is an item menu or a header menu */
|
|
gtk_tree_model_get (model, iter, THUNAR_SHORTCUTS_MODEL_COLUMN_IS_HEADER, &is_header, -1);
|
|
if (is_header)
|
|
{
|
|
thunar_shortcuts_view_context_menu_visibility (view, event, model);
|
|
return;
|
|
}
|
|
|
|
/* determine the tree path for the given iter */
|
|
path = gtk_tree_model_get_path (model, iter);
|
|
if (G_UNLIKELY (path == NULL))
|
|
return;
|
|
|
|
/* check whether the shortcut at the given path is mutable */
|
|
gtk_tree_model_get (model, iter,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION, &location,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_MUTABLE, &mutable,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_GROUP, &group,
|
|
-1);
|
|
|
|
context_menu = g_object_new (THUNAR_TYPE_MENU, "menu-type", THUNAR_MENU_TYPE_CONTEXT_SHORTCUTS_VIEW,
|
|
"action_mgr", view->action_mgr,
|
|
"force-section-open", TRUE, NULL);
|
|
|
|
/* try to determine the ThunarFile of the location, if not set */
|
|
if (location != NULL && file == NULL)
|
|
{
|
|
file = thunar_file_get (location, NULL);
|
|
if (file != NULL && thunar_file_exists (file) == FALSE)
|
|
{
|
|
g_object_unref (G_OBJECT (file));
|
|
file = NULL;
|
|
}
|
|
}
|
|
|
|
/* try to determine the ThunarFile of the device, if not set */
|
|
if (device != NULL && file == NULL && thunar_device_is_mounted (device))
|
|
{
|
|
GFile *mount_point = thunar_device_get_root (device);
|
|
if (G_LIKELY (mount_point != NULL))
|
|
{
|
|
file = thunar_file_get (mount_point, NULL);
|
|
g_object_unref (mount_point);
|
|
}
|
|
}
|
|
|
|
if (device != NULL && !thunar_device_is_mounted (device))
|
|
{
|
|
g_object_set (G_OBJECT (view->action_mgr), "selected-device", device, NULL);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW, TRUE);
|
|
xfce_gtk_menu_append_separator (GTK_MENU_SHELL (context_menu));
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_MOUNTABLE);
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_PROPERTIES);
|
|
}
|
|
else if (file != NULL)
|
|
{
|
|
files = g_list_append (NULL, file);
|
|
g_object_set (G_OBJECT (view->action_mgr), "current-directory", file, NULL);
|
|
thunar_action_manager_set_selection (view->action_mgr, files, device, NULL);
|
|
g_list_free (files);
|
|
if (thunar_g_file_is_trash (thunar_file_get_file (file)) || thunar_g_file_is_computer (thunar_file_get_file (file)) || thunar_g_file_is_network (thunar_file_get_file (file)))
|
|
{
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW, TRUE);
|
|
xfce_gtk_menu_append_separator (GTK_MENU_SHELL (context_menu));
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_EMPTY_TRASH);
|
|
}
|
|
else
|
|
{
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_OPEN
|
|
| THUNAR_MENU_SECTION_SENDTO);
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_CREATE_NEW_FILES
|
|
| THUNAR_MENU_SECTION_COPY_PASTE
|
|
| THUNAR_MENU_SECTION_CUSTOM_ACTIONS);
|
|
}
|
|
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_MOUNTABLE);
|
|
thunar_menu_add_sections (context_menu, THUNAR_MENU_SECTION_PROPERTIES);
|
|
}
|
|
else if (location != NULL)
|
|
{
|
|
g_object_set (G_OBJECT (view->action_mgr), "selected-location", location, NULL);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB, TRUE);
|
|
thunar_action_manager_append_menu_item (view->action_mgr, GTK_MENU_SHELL (context_menu), THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW, TRUE);
|
|
}
|
|
else
|
|
g_error ("Unknown type");
|
|
|
|
if (group == THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS)
|
|
{
|
|
xfce_gtk_menu_append_separator (GTK_MENU_SHELL (context_menu));
|
|
item = xfce_gtk_menu_item_new_from_action_entry (get_action_entry (THUNAR_SHORTCUTS_VIEW_ACTION_REMOVE_BOOKMARK), G_OBJECT (model), GTK_MENU_SHELL (context_menu));
|
|
g_object_set_data_full (G_OBJECT (item), I_ ("thunar-shortcuts-row"),
|
|
gtk_tree_row_reference_new (model, path),
|
|
(GDestroyNotify) gtk_tree_row_reference_free);
|
|
item = xfce_gtk_menu_item_new_from_action_entry (get_action_entry (THUNAR_SHORTCUTS_VIEW_ACTION_RENAME_BOOKMARK), G_OBJECT (view), GTK_MENU_SHELL (context_menu));
|
|
g_object_set_data_full (G_OBJECT (item), I_ ("thunar-shortcuts-row"),
|
|
gtk_tree_row_reference_new (model, path),
|
|
(GDestroyNotify) gtk_tree_row_reference_free);
|
|
}
|
|
|
|
thunar_gtk_menu_hide_accel_labels (GTK_MENU (context_menu));
|
|
gtk_widget_show_all (GTK_WIDGET (context_menu));
|
|
window = gtk_widget_get_toplevel (GTK_WIDGET (view));
|
|
thunar_window_redirect_menu_tooltips_to_statusbar (THUNAR_WINDOW (window), GTK_MENU (context_menu));
|
|
thunar_gtk_menu_run (GTK_MENU (context_menu));
|
|
|
|
/* return the focus to the current folder */
|
|
thunar_shortcuts_view_select_by_file (view, view->current_directory);
|
|
|
|
/* clean up */
|
|
if (G_LIKELY (file != NULL))
|
|
g_object_unref (G_OBJECT (file));
|
|
if (G_UNLIKELY (device != NULL))
|
|
g_object_unref (G_OBJECT (device));
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_remove_activated (ThunarShortcutsView *view,
|
|
GtkWidget *item)
|
|
{
|
|
GtkTreeRowReference *row;
|
|
GtkTreeModel *child_model;
|
|
GtkTreePath *path;
|
|
GtkTreePath *child_path;
|
|
|
|
row = g_object_get_data (G_OBJECT (item), I_ ("thunar-shortcuts-row"));
|
|
path = gtk_tree_row_reference_get_path (row);
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (view), path);
|
|
gtk_tree_path_free (path);
|
|
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (view));
|
|
thunar_shortcuts_model_remove (THUNAR_SHORTCUTS_MODEL (child_model), child_path);
|
|
gtk_tree_path_free (child_path);
|
|
}
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_editing_canceled (GtkCellRenderer *renderer,
|
|
ThunarShortcutsView *view)
|
|
{
|
|
g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
|
|
|
|
g_signal_handlers_disconnect_by_func (G_OBJECT (renderer),
|
|
G_CALLBACK (thunar_shortcuts_view_editing_canceled),
|
|
view);
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
thunar_shortcuts_view_rename_activated (ThunarShortcutsView *view,
|
|
GtkWidget *item)
|
|
{
|
|
GtkTreeRowReference *row;
|
|
GtkTreeViewColumn *column;
|
|
GtkCellRenderer *renderer;
|
|
GtkTreePath *path;
|
|
GList *renderers;
|
|
|
|
row = g_object_get_data (G_OBJECT (item), I_ ("thunar-shortcuts-row"));
|
|
path = gtk_tree_row_reference_get_path (row);
|
|
if (G_LIKELY (path != NULL))
|
|
{
|
|
/* determine the text renderer */
|
|
column = gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0);
|
|
renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
|
|
renderer = g_list_nth_data (renderers, 3);
|
|
|
|
/* make sure the text renderer is editable */
|
|
g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
|
|
|
|
/* set up signals for the cell renderer */
|
|
g_signal_connect (G_OBJECT (renderer), "editing-canceled",
|
|
G_CALLBACK (thunar_shortcuts_view_editing_canceled), view);
|
|
|
|
/* tell the tree view to start editing the given row */
|
|
gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (view), path, column, renderer, TRUE);
|
|
|
|
/* cleanup */
|
|
gtk_tree_path_free (path);
|
|
g_list_free (renderers);
|
|
}
|
|
|
|
/* required in case of shortcut activation, in order to signal that the accel key got handled */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_renamed (GtkCellRenderer *renderer,
|
|
const gchar *path_string,
|
|
const gchar *text,
|
|
ThunarShortcutsView *view)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *child_model;
|
|
GtkTreeIter child_iter;
|
|
|
|
/* reset the editable flag */
|
|
g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
|
|
|
|
/* perform the rename */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
if (gtk_tree_model_get_iter_from_string (model, &iter, path_string))
|
|
{
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
|
|
thunar_shortcuts_model_rename (THUNAR_SHORTCUTS_MODEL (child_model), &child_iter, text);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static GdkDragAction
|
|
thunar_shortcuts_view_compute_drop_actions (ThunarShortcutsView *view,
|
|
GdkDragContext *context,
|
|
gint x,
|
|
gint y,
|
|
GtkTreePath **path_return,
|
|
GdkDragAction *action_return,
|
|
GtkTreeViewDropPosition *position_return)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GdkDragAction actions = 0;
|
|
GdkRectangle cell_area;
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
GtkTreeIter iter;
|
|
ThunarFile *file;
|
|
gint cell_y;
|
|
GList *lp;
|
|
guint n;
|
|
|
|
/* determine the shortcuts model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
|
|
/* determine the path for x/y */
|
|
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y, &path, &column, NULL, &cell_y))
|
|
{
|
|
/* determine the background area of the column */
|
|
gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &cell_area);
|
|
|
|
/* check if 1/6th inside the cell and the path is within the model */
|
|
if ((cell_y > (cell_area.height / 6) && cell_y < (cell_area.height - cell_area.height / 6))
|
|
&& gtk_tree_model_get_iter (model, &iter, path))
|
|
{
|
|
/* determine the file for the iterator */
|
|
gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file, -1);
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
/* check if the file accepts the drop */
|
|
actions = thunar_file_accepts_drop (file, view->drop_file_list, context, action_return);
|
|
if (G_LIKELY (actions != 0))
|
|
{
|
|
/* we can drop into this location */
|
|
*position_return = GTK_TREE_VIEW_DROP_INTO_OR_AFTER;
|
|
*path_return = gtk_tree_path_copy (path);
|
|
|
|
/* display a drop icon for the file */
|
|
g_object_set (G_OBJECT (view->icon_renderer), "drop-file", file, NULL);
|
|
}
|
|
|
|
/* release the file */
|
|
g_object_unref (G_OBJECT (file));
|
|
}
|
|
}
|
|
|
|
/* release the path */
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
/* check if we cannot drop into the shortcut */
|
|
if (G_UNLIKELY (actions == 0))
|
|
{
|
|
/* check if any of the paths is not directory but only up to 100, if
|
|
* someone drags too many directories it may slowdown or even freeze
|
|
* Thunar while moving the cursor.
|
|
*/
|
|
for (lp = view->drop_file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n)
|
|
{
|
|
GFileInfo *info;
|
|
gboolean is_directory;
|
|
|
|
info = g_file_query_info (lp->data,
|
|
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
|
G_FILE_QUERY_INFO_NONE,
|
|
NULL, NULL);
|
|
|
|
if (G_UNLIKELY (info == NULL))
|
|
return 0;
|
|
|
|
is_directory = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
|
|
g_object_unref (info);
|
|
|
|
if (!is_directory)
|
|
return 0;
|
|
}
|
|
|
|
/* check the action that should be performed */
|
|
if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_LINK || (gdk_drag_context_get_actions (context) & GDK_ACTION_LINK) != 0)
|
|
actions = GDK_ACTION_LINK;
|
|
else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY || (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0)
|
|
actions = GDK_ACTION_COPY;
|
|
else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE || (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0)
|
|
actions = GDK_ACTION_MOVE;
|
|
else
|
|
return 0;
|
|
|
|
/* but maybe we can add as shortcut */
|
|
path = thunar_shortcuts_view_compute_drop_position (view, x, y);
|
|
|
|
if (path == NULL)
|
|
return 0;
|
|
|
|
/* check if path is about to append to the model */
|
|
if (gtk_tree_path_get_indices (path)[0] >= gtk_tree_model_iter_n_children (model, NULL))
|
|
{
|
|
/* set the position to "after" and move the path to
|
|
* point to the previous row instead; required to
|
|
* get the highlighting in GtkTreeView correct.
|
|
*/
|
|
*position_return = GTK_TREE_VIEW_DROP_AFTER;
|
|
gtk_tree_path_prev (path);
|
|
}
|
|
else
|
|
{
|
|
/* drop before the path */
|
|
*position_return = GTK_TREE_VIEW_DROP_BEFORE;
|
|
}
|
|
|
|
/* got it */
|
|
*action_return = actions;
|
|
*path_return = path;
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
|
|
|
|
static GtkTreePath *
|
|
thunar_shortcuts_view_compute_drop_position (ThunarShortcutsView *view,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
GtkTreeViewColumn *column;
|
|
GtkTreeModel *model;
|
|
GdkRectangle area;
|
|
GtkTreePath *path;
|
|
gint n_rows;
|
|
gboolean result;
|
|
GtkTreePath *child_path;
|
|
GtkTreeModel *child_model;
|
|
|
|
_thunar_return_val_if_fail (gtk_tree_view_get_model (GTK_TREE_VIEW (view)) != NULL, NULL);
|
|
_thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view), NULL);
|
|
|
|
/* query the number of rows in the model */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
n_rows = gtk_tree_model_iter_n_children (model, NULL);
|
|
|
|
if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), x, y,
|
|
&path, &column, &x, &y))
|
|
{
|
|
/* determine the exact path of the row the user is trying to drop
|
|
* (taking into account the relative y position)
|
|
*/
|
|
gtk_tree_view_get_background_area (GTK_TREE_VIEW (view), path, column, &area);
|
|
if (y >= area.height / 2)
|
|
gtk_tree_path_next (path);
|
|
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
|
|
/* find a suitable drop path (we cannot drop into the default shortcuts list) */
|
|
for (; gtk_tree_path_get_indices (path)[0] < n_rows; gtk_tree_path_next (path))
|
|
{
|
|
child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path);
|
|
result = thunar_shortcuts_model_drop_possible (THUNAR_SHORTCUTS_MODEL (child_model), child_path);
|
|
gtk_tree_path_free (child_path);
|
|
if (result)
|
|
return path;
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_drop_uri_list (ThunarShortcutsView *view,
|
|
GList *path_list,
|
|
GtkTreePath *dst_path)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreePath *path;
|
|
ThunarFile *file;
|
|
GError *error = NULL;
|
|
GList *lp;
|
|
GtkTreeModel *child_model;
|
|
GtkTreePath *child_path;
|
|
|
|
/* take a copy of the destination path */
|
|
path = gtk_tree_path_copy (dst_path);
|
|
|
|
/* process the URIs one-by-one and stop on error */
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
for (lp = path_list; lp != NULL; lp = lp->next)
|
|
{
|
|
file = thunar_file_get (lp->data, &error);
|
|
if (G_UNLIKELY (file == NULL))
|
|
break;
|
|
|
|
/* make sure, that only directories gets added to the shortcuts list */
|
|
if (G_UNLIKELY (!thunar_file_is_directory (file)))
|
|
{
|
|
g_set_error (&error, G_FILE_ERROR, G_FILE_ERROR_NOTDIR,
|
|
_("The path \"%s\" does not refer to a directory"),
|
|
thunar_file_get_display_name (file));
|
|
g_object_unref (file);
|
|
break;
|
|
}
|
|
|
|
child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path);
|
|
thunar_shortcuts_model_add (THUNAR_SHORTCUTS_MODEL (child_model), child_path, file);
|
|
gtk_tree_path_free (child_path);
|
|
|
|
g_object_unref (file);
|
|
gtk_tree_path_next (path);
|
|
}
|
|
|
|
/* release the tree path copy */
|
|
gtk_tree_path_free (path);
|
|
|
|
if (G_UNLIKELY (error != NULL))
|
|
{
|
|
/* display an error message to the user */
|
|
thunar_dialogs_show_error (GTK_WIDGET (view), error, _("Failed to add new shortcut"));
|
|
|
|
/* release the error */
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_open (ThunarShortcutsView *view,
|
|
ThunarActionManagerFolderOpenAction open_in)
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
ThunarFile *file;
|
|
GList *files;
|
|
ThunarDevice *device;
|
|
GFile *location;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
/* determine the selected item */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
|
|
/* avoid dealing with invalid selections */
|
|
if (!GTK_IS_TREE_SELECTION (selection))
|
|
return;
|
|
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
/* determine the file for the shortcut at the given tree iterator */
|
|
gtk_tree_model_get (model, &iter,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_FILE, &file,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device,
|
|
THUNAR_SHORTCUTS_MODEL_COLUMN_LOCATION, &location,
|
|
-1);
|
|
|
|
if (device != NULL)
|
|
{
|
|
g_object_set (G_OBJECT (view->action_mgr), "selected-device", device, NULL);
|
|
}
|
|
else if (file != NULL)
|
|
{
|
|
files = g_list_append (NULL, file);
|
|
thunar_action_manager_set_selection (view->action_mgr, files, device, NULL);
|
|
g_list_free (files);
|
|
}
|
|
else if (location != 0)
|
|
{
|
|
g_object_set (G_OBJECT (view->action_mgr), "selected-location", location, NULL);
|
|
}
|
|
|
|
thunar_action_manager_activate_selected_files (view->action_mgr, (ThunarActionManagerFolderOpenAction) open_in, NULL);
|
|
|
|
/* return the focus to the current folder, unless the folder changed */
|
|
if (open_in != THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY)
|
|
thunar_shortcuts_view_select_by_file (view, view->current_directory);
|
|
|
|
if (file != NULL)
|
|
g_object_unref (file);
|
|
|
|
if (device != NULL)
|
|
g_object_unref (device);
|
|
|
|
if (location != NULL)
|
|
g_object_unref (location);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_start_spinner (ThunarShortcutsView *view,
|
|
ThunarDevice *device,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeModel *child_model;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, TRUE);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_stop_spinner (ThunarShortcutsView *view,
|
|
ThunarDevice *device,
|
|
gpointer user_data)
|
|
{
|
|
GtkTreeModel *model;
|
|
GtkTreeModel *child_model;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_DEVICE (device));
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
thunar_shortcuts_model_set_busy (THUNAR_SHORTCUTS_MODEL (child_model), device, FALSE);
|
|
|
|
/* return the focus to the current folder */
|
|
thunar_shortcuts_view_select_by_file (view, view->current_directory);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_eject (ThunarShortcutsView *view)
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
ThunarDevice *device;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
/* determine the selected item */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
if (gtk_tree_selection_get_selected (selection, &model, &iter))
|
|
{
|
|
/* determine the device/mount for the shortcut at the given tree iterator */
|
|
gtk_tree_model_get (model, &iter, THUNAR_SHORTCUTS_MODEL_COLUMN_DEVICE, &device, -1);
|
|
|
|
if (G_LIKELY (THUNAR_IS_DEVICE (device)))
|
|
{
|
|
g_object_set (G_OBJECT (view->action_mgr), "selected-device", device, NULL);
|
|
thunar_action_manager_action_eject (view->action_mgr);
|
|
g_object_unref (G_OBJECT (device));
|
|
}
|
|
}
|
|
|
|
/* return the focus to the current folder */
|
|
thunar_shortcuts_view_select_by_file (view, view->current_directory);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_shortcuts_view_new:
|
|
*
|
|
* Allocates a new #ThunarShortcutsView instance and associates
|
|
* it with the default #ThunarShortcutsModel instance.
|
|
*
|
|
* Return value: the newly allocated #ThunarShortcutsView instance.
|
|
**/
|
|
GtkWidget *
|
|
thunar_shortcuts_view_new (void)
|
|
{
|
|
ThunarShortcutsModel *model;
|
|
GtkWidget *view;
|
|
GtkTreeModel *filter_model;
|
|
|
|
model = thunar_shortcuts_model_get_default ();
|
|
filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (model), NULL);
|
|
gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter_model), THUNAR_SHORTCUTS_MODEL_COLUMN_VISIBLE);
|
|
|
|
view = g_object_new (THUNAR_TYPE_SHORTCUTS_VIEW, "model", filter_model, NULL);
|
|
THUNAR_SHORTCUTS_VIEW (view)->model = model;
|
|
|
|
/* redraw the view when the model changes */
|
|
g_signal_connect_swapped (G_OBJECT (model), "row-changed", G_CALLBACK (gtk_widget_queue_draw), view);
|
|
|
|
g_object_unref (G_OBJECT (filter_model));
|
|
|
|
return view;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* thunar_shortcuts_view_select_by_file:
|
|
* @view : a #ThunarShortcutsView instance.
|
|
* @file : a #ThunarFile instance.
|
|
*
|
|
* Looks up the first shortcut that refers to @file in @view and selects it.
|
|
* If @file is not present in the underlying #ThunarShortcutsModel, no
|
|
* shortcut will be selected afterwards.
|
|
**/
|
|
void
|
|
thunar_shortcuts_view_select_by_file (ThunarShortcutsView *view,
|
|
ThunarFile *file)
|
|
{
|
|
GtkTreeSelection *selection;
|
|
GtkTreeModel *model;
|
|
GtkTreeIter iter;
|
|
GtkTreeModel *child_model;
|
|
GtkTreeIter child_iter;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
_thunar_return_if_fail (THUNAR_IS_FILE (file));
|
|
|
|
/* get the selection */
|
|
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
|
|
|
|
model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
|
|
child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
|
|
|
|
/* try to lookup a tree iter for the given file */
|
|
if (thunar_shortcuts_model_iter_for_file (THUNAR_SHORTCUTS_MODEL (child_model), file, &child_iter)
|
|
&& gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (model), &iter, &child_iter))
|
|
gtk_tree_selection_select_iter (selection, &iter);
|
|
else
|
|
gtk_tree_selection_unselect_all (selection);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
thunar_shortcuts_view_toggle_padding (ThunarShortcutsView *view,
|
|
gboolean enable)
|
|
{
|
|
gtk_tree_view_column_set_attributes (view->column, view->padding_renderer,
|
|
"visible", enable ? THUNAR_SHORTCUTS_MODEL_COLUMN_CAN_EJECT : THUNAR_SHORTCUTS_MODEL_COLUMN_HIDDEN,
|
|
NULL);
|
|
|
|
view->padding_enabled = enable;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
thunar_shortcuts_view_new_files_created (ThunarShortcutsView *view,
|
|
GList *files_to_selected)
|
|
{
|
|
ThunarFile *file;
|
|
|
|
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_VIEW (view));
|
|
|
|
if (files_to_selected == NULL)
|
|
return;
|
|
|
|
/* Just pick the first file */
|
|
file = thunar_file_get (files_to_selected->data, NULL);
|
|
|
|
/* And open it, if it is a directory (It always should be a directory for shortcuts-view) */
|
|
if (G_LIKELY (file != NULL))
|
|
{
|
|
if (G_LIKELY (thunar_file_is_directory (file)))
|
|
thunar_navigator_change_directory (THUNAR_NAVIGATOR (view), file);
|
|
g_object_unref (file);
|
|
}
|
|
}
|