1 /* nautilus-list-view-dnd.c
2 *
3 * Copyright (C) 2015 Carlos Soriano <csoriano@gnome.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include "nautilus-list-view-dnd.h"
22 #include "nautilus-list-view-private.h"
23
24 static GtkTargetList *source_target_list = NULL;
25
26 static void
27 drag_info_data_free (NautilusListView *list_view);
28
29 static void
drag_data_get_callback(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,gpointer user_data)30 drag_data_get_callback (GtkWidget *widget,
31 GdkDragContext *context,
32 GtkSelectionData *selection_data,
33 guint info,
34 guint time,
35 gpointer user_data)
36 {
37 GtkTreeView *tree_view;
38 GtkTreeModel *model;
39 NautilusListView *list_view;
40
41 tree_view = GTK_TREE_VIEW (widget);
42 list_view = NAUTILUS_LIST_VIEW (user_data);
43
44 model = gtk_tree_view_get_model (tree_view);
45
46 if (model == NULL)
47 {
48 return;
49 }
50
51 if (list_view->details->drag_source_info == NULL ||
52 list_view->details->drag_source_info->selection_cache == NULL)
53 {
54 return;
55 }
56
57 nautilus_drag_drag_data_get_from_cache (list_view->details->drag_source_info->selection_cache,
58 context, selection_data, info, time);
59 }
60
61 static cairo_surface_t *
get_drag_surface(NautilusListView * view)62 get_drag_surface (NautilusListView *view)
63 {
64 GtkTreeModel *model;
65 GtkTreePath *path;
66 GtkTreeIter iter;
67 cairo_surface_t *ret;
68 GdkRectangle cell_area;
69
70 ret = NULL;
71
72 if (gtk_tree_view_get_path_at_pos (view->details->tree_view,
73 view->details->drag_x,
74 view->details->drag_y,
75 &path, NULL, NULL, NULL))
76 {
77 model = gtk_tree_view_get_model (view->details->tree_view);
78 gtk_tree_model_get_iter (model, &iter, path);
79 gtk_tree_model_get (model, &iter,
80 nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
81 &ret,
82 -1);
83 }
84
85 gtk_tree_view_get_cell_area (view->details->tree_view,
86 path,
87 view->details->file_name_column,
88 &cell_area);
89
90 gtk_tree_path_free (path);
91
92 return ret;
93 }
94
95 /* iteration glue struct */
96 typedef struct
97 {
98 NautilusListView *view;
99 NautilusDragEachSelectedItemDataGet iteratee;
100 gpointer iteratee_data;
101 } ListGetDataBinderContext;
102
103 static void
item_get_data_binder(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)104 item_get_data_binder (GtkTreeModel *model,
105 GtkTreePath *path,
106 GtkTreeIter *iter,
107 gpointer data)
108 {
109 ListGetDataBinderContext *context = data;
110 NautilusFile *file;
111 GtkTreeView *treeview;
112 GtkTreeViewColumn *column;
113 GdkRectangle cell_area;
114 int drag_begin_y = 0;
115 char *uri;
116
117 treeview = nautilus_list_model_get_drag_view (context->view->details->model,
118 NULL,
119 &drag_begin_y);
120 column = gtk_tree_view_get_column (treeview, 0);
121
122 file = nautilus_list_model_file_for_path (NAUTILUS_LIST_MODEL (model), path);
123 if (file == NULL)
124 {
125 return;
126 }
127
128 gtk_tree_view_get_cell_area (treeview,
129 path,
130 column,
131 &cell_area);
132
133 uri = nautilus_file_get_activation_uri (file);
134
135 nautilus_file_unref (file);
136
137 /* pass the uri, mouse-relative x/y and icon width/height */
138 context->iteratee (uri,
139 0,
140 cell_area.y - drag_begin_y,
141 cell_area.width,
142 cell_area.height,
143 context->iteratee_data);
144
145 g_free (uri);
146 }
147
148 static void
each_item_get_data_binder(NautilusDragEachSelectedItemDataGet iteratee,gpointer iterator_context,gpointer data)149 each_item_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee,
150 gpointer iterator_context,
151 gpointer data)
152 {
153 NautilusListView *view = NAUTILUS_LIST_VIEW (iterator_context);
154 ListGetDataBinderContext context;
155 GtkTreeSelection *selection;
156
157 context.view = view;
158 context.iteratee = iteratee;
159 context.iteratee_data = data;
160
161 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_view));
162 gtk_tree_selection_selected_foreach (selection, item_get_data_binder, &context);
163 }
164
165 static void
drag_begin_callback(GtkWidget * widget,GdkDragContext * context,NautilusListView * view)166 drag_begin_callback (GtkWidget *widget,
167 GdkDragContext *context,
168 NautilusListView *view)
169 {
170 cairo_surface_t *surface;
171 NautilusWindow *window;
172 GList *dragged_files;
173
174 window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (view));
175 surface = get_drag_surface (view);
176 if (surface)
177 {
178 gtk_drag_set_icon_surface (context, surface);
179 cairo_surface_destroy (surface);
180 }
181 else
182 {
183 gtk_drag_set_icon_default (context);
184 }
185
186 view->details->drag_button = 0;
187 view->details->drag_started = TRUE;
188
189 view->details->drag_source_info->selection_cache = nautilus_drag_create_selection_cache (view,
190 each_item_get_data_binder);
191
192 dragged_files = nautilus_drag_file_list_from_selection_list (view->details->drag_source_info->selection_cache);
193 if (nautilus_file_list_are_all_folders (dragged_files))
194 {
195 nautilus_window_start_dnd (window, context);
196 }
197 g_list_free_full (dragged_files, g_object_unref);
198 }
199
200 static void
drag_end_callback(GtkWidget * widget,GdkDragContext * context,NautilusListView * list_view)201 drag_end_callback (GtkWidget *widget,
202 GdkDragContext *context,
203 NautilusListView *list_view)
204 {
205 NautilusWindow *window;
206
207 window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (list_view));
208
209 nautilus_window_end_dnd (window, context);
210
211 drag_info_data_free (list_view);
212 }
213
214 static void
drag_info_data_free(NautilusListView * list_view)215 drag_info_data_free (NautilusListView *list_view)
216 {
217 nautilus_drag_destroy_selection_list (list_view->details->drag_source_info->selection_cache);
218 list_view->details->drag_source_info->selection_cache = NULL;
219
220 g_free (list_view->details->drag_source_info);
221 list_view->details->drag_source_info = NULL;
222
223 g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_begin_callback, list_view);
224 g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_data_get_callback, list_view);
225 g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_end_callback, list_view);
226 }
227
228 NautilusDragInfo *
nautilus_list_view_dnd_get_drag_source_data(NautilusListView * list_view,GdkDragContext * context)229 nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view,
230 GdkDragContext *context)
231 {
232 GtkTreeView *tree_view;
233 GtkTreeModel *model;
234
235 tree_view = GTK_TREE_VIEW (list_view->details->tree_view);
236
237 model = gtk_tree_view_get_model (tree_view);
238
239 if (model == NULL)
240 {
241 return NULL;
242 }
243
244 if (list_view->details->drag_source_info == NULL ||
245 list_view->details->drag_source_info->selection_cache == NULL)
246 {
247 return NULL;
248 }
249
250 return list_view->details->drag_source_info;
251 }
252
253 void
nautilus_list_view_dnd_init(NautilusListView * list_view)254 nautilus_list_view_dnd_init (NautilusListView *list_view)
255 {
256 if (list_view->details->drag_source_info != NULL)
257 {
258 return;
259 }
260
261 list_view->details->drag_source_info = g_new0 (NautilusDragInfo, 1);
262
263 g_signal_connect_object (list_view->details->tree_view, "drag-begin",
264 G_CALLBACK (drag_begin_callback), list_view, 0);
265 g_signal_connect_object (list_view->details->tree_view, "drag-end",
266 G_CALLBACK (drag_end_callback), list_view, 0);
267 g_signal_connect_object (list_view->details->tree_view, "drag-data-get",
268 G_CALLBACK (drag_data_get_callback), list_view, 0);
269 }
270
271 void
nautilus_list_view_dnd_drag_begin(NautilusListView * list_view,gdouble offset_x,gdouble offset_y,const GdkEvent * event)272 nautilus_list_view_dnd_drag_begin (NautilusListView *list_view,
273 gdouble offset_x,
274 gdouble offset_y,
275 const GdkEvent *event)
276 {
277 if (list_view->details->drag_button == 0)
278 {
279 return;
280 }
281
282 if (!source_target_list)
283 {
284 source_target_list = nautilus_list_model_get_drag_target_list ();
285 }
286
287 if (gtk_drag_check_threshold (GTK_WIDGET (list_view->details->tree_view),
288 list_view->details->drag_x,
289 list_view->details->drag_y,
290 list_view->details->drag_x + offset_x,
291 list_view->details->drag_y + offset_y))
292 {
293 guint32 actions;
294
295 actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK;
296 list_view->details->drag_source_info->source_actions = actions;
297 gtk_drag_begin_with_coordinates (GTK_WIDGET (list_view->details->tree_view),
298 source_target_list,
299 actions,
300 list_view->details->drag_button,
301 (GdkEvent *) event,
302 -1,
303 -1);
304 }
305 }
306