/* nautilus-list-view-dnd.c
*
* Copyright (C) 2015 Carlos Soriano <csoriano@gnome.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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "nautilus-list-view-dnd.h"
#include "nautilus-list-view-private.h"
static GtkTargetList *source_target_list = NULL;
static void
drag_info_data_free (NautilusListView *list_view);
static void
drag_data_get_callback (GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time,
gpointer user_data)
{
GtkTreeView *tree_view;
GtkTreeModel *model;
NautilusListView *list_view;
tree_view = GTK_TREE_VIEW (widget);
list_view = NAUTILUS_LIST_VIEW (user_data);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
{
return;
}
if (list_view->details->drag_source_info == NULL ||
list_view->details->drag_source_info->selection_cache == NULL)
{
return;
}
nautilus_drag_drag_data_get_from_cache (list_view->details->drag_source_info->selection_cache,
context, selection_data, info, time);
}
static cairo_surface_t *
get_drag_surface (NautilusListView *view)
{
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
cairo_surface_t *ret;
GdkRectangle cell_area;
ret = NULL;
if (gtk_tree_view_get_path_at_pos (view->details->tree_view,
view->details->drag_x,
view->details->drag_y,
&path, NULL, NULL, NULL))
{
model = gtk_tree_view_get_model (view->details->tree_view);
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter,
nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
&ret,
-1);
}
gtk_tree_view_get_cell_area (view->details->tree_view,
path,
view->details->file_name_column,
&cell_area);
gtk_tree_path_free (path);
return ret;
}
/* iteration glue struct */
typedef struct
{
NautilusListView *view;
NautilusDragEachSelectedItemDataGet iteratee;
gpointer iteratee_data;
} ListGetDataBinderContext;
static void
item_get_data_binder (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
ListGetDataBinderContext *context = data;
NautilusFile *file;
GtkTreeView *treeview;
GtkTreeViewColumn *column;
GdkRectangle cell_area;
int drag_begin_y = 0;
char *uri;
treeview = nautilus_list_model_get_drag_view (context->view->details->model,
NULL,
&drag_begin_y);
column = gtk_tree_view_get_column (treeview, 0);
file = nautilus_list_model_file_for_path (NAUTILUS_LIST_MODEL (model), path);
if (file == NULL)
{
return;
}
gtk_tree_view_get_cell_area (treeview,
path,
column,
&cell_area);
if (nautilus_file_is_nautilus_link (file))
{
uri = nautilus_file_get_uri (file);
}
else
{
uri = nautilus_file_get_activation_uri (file);
}
nautilus_file_unref (file);
/* pass the uri, mouse-relative x/y and icon width/height */
context->iteratee (uri,
0,
cell_area.y - drag_begin_y,
cell_area.width,
cell_area.height,
context->iteratee_data);
g_free (uri);
}
static void
each_item_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee,
gpointer iterator_context,
gpointer data)
{
NautilusListView *view = NAUTILUS_LIST_VIEW (iterator_context);
ListGetDataBinderContext context;
GtkTreeSelection *selection;
context.view = view;
context.iteratee = iteratee;
context.iteratee_data = data;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_view));
gtk_tree_selection_selected_foreach (selection, item_get_data_binder, &context);
}
static void
drag_begin_callback (GtkWidget *widget,
GdkDragContext *context,
NautilusListView *view)
{
cairo_surface_t *surface;
NautilusWindow *window;
GList *dragged_files;
window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (view));
surface = get_drag_surface (view);
if (surface)
{
gtk_drag_set_icon_surface (context, surface);
cairo_surface_destroy (surface);
}
else
{
gtk_drag_set_icon_default (context);
}
view->details->drag_button = 0;
view->details->drag_started = TRUE;
view->details->drag_source_info->selection_cache = nautilus_drag_create_selection_cache (view,
each_item_get_data_binder);
dragged_files = nautilus_drag_file_list_from_selection_list (view->details->drag_source_info->selection_cache);
if (nautilus_file_list_are_all_folders (dragged_files))
{
nautilus_window_start_dnd (window, context);
}
g_list_free_full (dragged_files, g_object_unref);
}
static void
drag_end_callback (GtkWidget *widget,
GdkDragContext *context,
NautilusListView *list_view)
{
NautilusWindow *window;
window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (list_view));
nautilus_window_end_dnd (window, context);
drag_info_data_free (list_view);
}
static void
drag_info_data_free (NautilusListView *list_view)
{
nautilus_drag_destroy_selection_list (list_view->details->drag_source_info->selection_cache);
list_view->details->drag_source_info->selection_cache = NULL;
g_free (list_view->details->drag_source_info);
list_view->details->drag_source_info = NULL;
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_begin_callback, list_view);
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_data_get_callback, list_view);
g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_end_callback, list_view);
}
NautilusDragInfo *
nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view,
GdkDragContext *context)
{
GtkTreeView *tree_view;
GtkTreeModel *model;
tree_view = GTK_TREE_VIEW (list_view->details->tree_view);
model = gtk_tree_view_get_model (tree_view);
if (model == NULL)
{
return NULL;
}
if (list_view->details->drag_source_info == NULL ||
list_view->details->drag_source_info->selection_cache == NULL)
{
return NULL;
}
return list_view->details->drag_source_info;
}
void
nautilus_list_view_dnd_init (NautilusListView *list_view)
{
if (list_view->details->drag_source_info != NULL)
{
return;
}
list_view->details->drag_source_info = g_new0 (NautilusDragInfo, 1);
g_signal_connect_object (list_view->details->tree_view, "drag-begin",
G_CALLBACK (drag_begin_callback), list_view, 0);
g_signal_connect_object (list_view->details->tree_view, "drag-end",
G_CALLBACK (drag_end_callback), list_view, 0);
g_signal_connect_object (list_view->details->tree_view, "drag-data-get",
G_CALLBACK (drag_data_get_callback), list_view, 0);
}
gboolean
nautilus_list_view_dnd_drag_begin (NautilusListView *list_view,
GdkEventMotion *event)
{
if (list_view->details->drag_button != 0)
{
if (!source_target_list)
{
source_target_list = nautilus_list_model_get_drag_target_list ();
}
if (gtk_drag_check_threshold (GTK_WIDGET (list_view->details->tree_view),
list_view->details->drag_x,
list_view->details->drag_y,
event->x,
event->y))
{
guint32 actions;
actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK;
list_view->details->drag_source_info->source_actions = actions;
gtk_drag_begin_with_coordinates (GTK_WIDGET (list_view->details->tree_view),
source_target_list,
actions,
list_view->details->drag_button,
(GdkEvent *) event,
-1,
-1);
}
return TRUE;
}
return FALSE;
}