No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 * nautilus-view-dnd.c: DnD helpers for NautilusView
5 *
6 * Copyright (C) 1999, 2000 Free Software Foundaton
7 * Copyright (C) 2000, 2001 Eazel, Inc.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 *
24 * Authors: Ettore Perazzoli
25 * Darin Adler <darin@bentspoon.com>
26 * John Sullivan <sullivan@eazel.com>
27 * Pavel Cisler <pavel@eazel.com>
28 */
29
30 #include <config.h>
31
32 #include "nautilus-view-dnd.h"
33
34 #include "nautilus-view.h"
35
36 #include <eel/eel-stock-dialogs.h>
37 #include <eel/eel-string.h>
38
39 #include <glib/gi18n.h>
40
41 #include <libnautilus-private/nautilus-clipboard.h>
42 #include <libnautilus-private/nautilus-dnd.h>
43
44 #define GET_ANCESTOR(obj) \
45 GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (obj), GTK_TYPE_WINDOW))
46
47 static inline void
48 view_widget_to_file_operation_position (NautilusView *view,
49 GdkPoint *position)
50 {
51 NautilusViewClass *class = NAUTILUS_VIEW_GET_CLASS (view);
52
53 if (class->widget_to_file_operation_position != NULL) {
54 class->widget_to_file_operation_position (view, position);
55 }
56 }
57
58 static void
59 view_widget_to_file_operation_position_xy (NautilusView *view,
60 int *x, int *y)
61 {
62 GdkPoint position;
63
64 position.x = *x;
65 position.y = *y;
66 view_widget_to_file_operation_position (view, &position);
67 *x = position.x;
68 *y = position.y;
69 }
70
71 typedef struct {
72 NautilusView *view;
73 char *link_name;
74 char *target_uri;
75 char *url;
76 GdkPoint point;
77 } NetscapeUrlDropLink;
78
79 static void
80 revert_slashes (char *string)
81 {
82 while (*string != 0) {
83 if (*string == '/') {
84 *string = '\\';
85 }
86 string++;
87 }
88 }
89
90 static void
91 handle_netscape_url_drop_link_cb (GObject *source_object,
92 GAsyncResult *res,
93 gpointer user_data)
94 {
95 NetscapeUrlDropLink *data = user_data;
96 char *link_name = data->link_name;
97 char *link_display_name;
98 gint screen_num;
99 GFileInfo *info;
100 char *icon_name = NULL;
101 GdkScreen *screen;
102
103 info = g_file_query_info_finish (G_FILE (source_object),
104 res, NULL);
105
106 if (info != NULL) {
107 GIcon *icon;
108 const char * const *names;
109
110 icon = g_file_info_get_icon (info);
111
112 if (G_IS_THEMED_ICON (icon)) {
113 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
114 icon_name = g_strdup (names[0]);
115 }
116
117 g_object_unref (info);
118 }
119
120 if (icon_name == NULL) {
121 icon_name = g_strdup ("text-html");
122 }
123
124 link_display_name = g_strdup_printf (_("Link to %s"), link_name);
125
126 /* The filename can't contain slashes, strip em.
127 (the basename of http://foo/ is http://foo/) */
128 revert_slashes (link_name);
129
130 screen = gtk_widget_get_screen (GTK_WIDGET (data->view));
131 screen_num = gdk_screen_get_number (screen);
132
133 nautilus_link_local_create (data->target_uri,
134 link_name,
135 link_display_name,
136 icon_name,
137 data->url,
138 &data->point,
139 screen_num,
140 TRUE);
141
142 g_free (link_display_name);
143 g_free (icon_name);
144
145 g_free (data->url);
146 g_free (data->link_name);
147 g_free (data->target_uri);
148
149 g_object_unref (data->view);
150 g_slice_free (NetscapeUrlDropLink, data);
151 }
152
153 void
154 nautilus_view_handle_netscape_url_drop (NautilusView *view,
155 const char *encoded_url,
156 const char *target_uri,
157 GdkDragAction action,
158 int x,
159 int y)
160 {
161 char *url, *title;
162 char *link_name;
163 GArray *points;
164 char **bits;
165 GList *uri_list = NULL;
166 GFile *f;
167
168 f = g_file_new_for_uri (target_uri);
169
170 if (!g_file_is_native (f)) {
171 eel_show_warning_dialog (_("Drag and drop is not supported."),
172 _("Drag and drop is only supported on local file systems."),
173 GET_ANCESTOR (view));
174 g_object_unref (f);
175 return;
176 }
177
178 g_object_unref (f);
179
180 /* _NETSCAPE_URL_ works like this: $URL\n$TITLE */
181 bits = g_strsplit (encoded_url, "\n", 0);
182 switch (g_strv_length (bits)) {
183 case 0:
184 g_strfreev (bits);
185 return;
186 case 1:
187 url = bits[0];
188 title = NULL;
189 break;
190 default:
191 url = bits[0];
192 title = bits[1];
193 }
194
195 f = g_file_new_for_uri (url);
196
197 view_widget_to_file_operation_position_xy (view, &x, &y);
198
199 /* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
200 * and we don't support combinations either. */
201 if ((action != GDK_ACTION_DEFAULT) &&
202 (action != GDK_ACTION_COPY) &&
203 (action != GDK_ACTION_MOVE) &&
204 (action != GDK_ACTION_LINK)) {
205 eel_show_warning_dialog (_("Drag and drop is not supported."),
206 _("An invalid drag type was used."),
207 GET_ANCESTOR (view));
208 return;
209 }
210
211 if (action == GDK_ACTION_LINK) {
212 if (g_strcmp0 (title, NULL) == 0) {
213 link_name = g_file_get_basename (f);
214 } else {
215 link_name = g_strdup (title);
216 }
217
218 if (g_strcmp0 (link_name, NULL) != 0) {
219 NetscapeUrlDropLink *data;
220
221 data = g_slice_new0 (NetscapeUrlDropLink);
222 data->link_name = link_name;
223 data->point.x = x;
224 data->point.y = y;
225 data->view = g_object_ref (view);
226 data->target_uri = g_strdup (target_uri);
227 data->url = g_strdup (url);
228
229 g_file_query_info_async (f,
230 G_FILE_ATTRIBUTE_STANDARD_ICON,
231 0, 0, NULL,
232 handle_netscape_url_drop_link_cb,
233 data);
234 }
235 } else {
236 GdkPoint tmp_point = { 0, 0 };
237
238 /* pass in a 1-item array of icon positions, relative to x, y */
239 points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
240 g_array_append_val (points, tmp_point);
241
242 uri_list = g_list_append (uri_list, url);
243
244 nautilus_view_move_copy_items (view, uri_list, points,
245 target_uri,
246 action, x, y);
247
248 g_list_free (uri_list);
249 g_array_free (points, TRUE);
250 }
251
252 g_object_unref (f);
253 g_strfreev (bits);
254 }
255
256 void
257 nautilus_view_handle_uri_list_drop (NautilusView *view,
258 const char *item_uris,
259 const char *target_uri,
260 GdkDragAction action,
261 int x,
262 int y)
263 {
264 gchar **uri_list;
265 GList *real_uri_list = NULL;
266 char *container_uri;
267 int n_uris, i;
268 GArray *points;
269
270 if (item_uris == NULL) {
271 return;
272 }
273
274 container_uri = NULL;
275 if (target_uri == NULL) {
276 container_uri = nautilus_view_get_backing_uri (view);
277 g_assert (container_uri != NULL);
278 }
279
280 if (action == GDK_ACTION_ASK) {
281 action = nautilus_drag_drop_action_ask
282 (GTK_WIDGET (view),
283 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
284 if (action == 0) {
285 g_free (container_uri);
286 return;
287 }
288 }
289
290 /* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
291 * and we don't support combinations either. */
292 if ((action != GDK_ACTION_DEFAULT) &&
293 (action != GDK_ACTION_COPY) &&
294 (action != GDK_ACTION_MOVE) &&
295 (action != GDK_ACTION_LINK)) {
296 eel_show_warning_dialog (_("Drag and drop is not supported."),
297 _("An invalid drag type was used."),
298 GET_ANCESTOR (view));
299 g_free (container_uri);
300 return;
301 }
302
303 n_uris = 0;
304 uri_list = g_uri_list_extract_uris (item_uris);
305 for (i = 0; uri_list[i] != NULL; i++) {
306 real_uri_list = g_list_append (real_uri_list, uri_list[i]);
307 n_uris++;
308 }
309 g_free (uri_list);
310
311 /* do nothing if no real uris are left */
312 if (n_uris == 0) {
313 g_free (container_uri);
314 return;
315 }
316
317 if (n_uris == 1) {
318 GdkPoint tmp_point = { 0, 0 };
319
320 /* pass in a 1-item array of icon positions, relative to x, y */
321 points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
322 g_array_append_val (points, tmp_point);
323 } else {
324 points = NULL;
325 }
326
327 view_widget_to_file_operation_position_xy (view, &x, &y);
328
329 nautilus_view_move_copy_items (view, real_uri_list, points,
330 target_uri != NULL ? target_uri : container_uri,
331 action, x, y);
332
333 g_list_free_full (real_uri_list, g_free);
334
335 if (points != NULL)
336 g_array_free (points, TRUE);
337
338 g_free (container_uri);
339 }
340
341 #define MAX_LEN_FILENAME 128
342 #define MIN_LEN_FILENAME 10
343
344 static char *
345 get_drop_filename (const char *text)
346 {
347 char *filename;
348 char trimmed[MAX_LEN_FILENAME];
349 int i;
350 int last_word = -1;
351 int last_sentence = -1;
352 int last_nonspace = -1;
353 int num_attrs;
354 PangoLogAttr *attrs;
355
356 num_attrs = MIN (g_utf8_strlen (text, -1) + 1, MAX_LEN_FILENAME);
357 attrs = g_new (PangoLogAttr, num_attrs);
358 g_utf8_strncpy (trimmed, text, num_attrs);
359 pango_get_log_attrs (trimmed, -1, -1, pango_language_get_default (), attrs, num_attrs);
360
361 /* since the end of the text will always match a word boundary don't include it */
362 for (i = 0; (i < num_attrs - 1); i++) {
363 if (!attrs[i].is_white)
364 last_nonspace = i;
365 if (attrs[i].is_sentence_end)
366 last_sentence = last_nonspace;
367 if (attrs[i].is_word_boundary)
368 last_word = last_nonspace;
369 }
370 g_free (attrs);
371
372 if (last_sentence > 0)
373 i = last_sentence;
374 else
375 i = last_word;
376
377 if (i > MIN_LEN_FILENAME) {
378 char basename[MAX_LEN_FILENAME];
379 g_utf8_strncpy (basename, trimmed, i);
380 filename = g_strdup_printf ("%s.txt", basename);
381 } else {
382 /* Translator: This is the filename used for when you dnd text to a directory */
383 filename = g_strdup (_("Dropped Text.txt"));
384 }
385
386 return filename;
387 }
388
389 void
390 nautilus_view_handle_text_drop (NautilusView *view,
391 const char *text,
392 const char *target_uri,
393 GdkDragAction action,
394 int x,
395 int y)
396 {
397 int length;
398 char *container_uri;
399 GdkPoint pos;
400 char *filename;
401
402 if (text == NULL) {
403 return;
404 }
405
406 g_return_if_fail (action == GDK_ACTION_COPY);
407
408 container_uri = NULL;
409 if (target_uri == NULL) {
410 container_uri = nautilus_view_get_backing_uri (view);
411 g_assert (container_uri != NULL);
412 }
413
414 length = strlen (text);
415
416 pos.x = x;
417 pos.y = y;
418 view_widget_to_file_operation_position (view, &pos);
419
420 /* try to get text to use as a filename */
421 filename = get_drop_filename (text);
422
423 nautilus_view_new_file_with_initial_contents (view,
424 target_uri != NULL ? target_uri : container_uri,
425 filename,
426 text,
427 length,
428 &pos);
429 g_free (filename);
430 g_free (container_uri);
431 }
432
433 void
434 nautilus_view_handle_raw_drop (NautilusView *view,
435 const char *raw_data,
436 int length,
437 const char *target_uri,
438 const char *direct_save_uri,
439 GdkDragAction action,
440 int x,
441 int y)
442 {
443 char *container_uri, *filename;
444 GFile *direct_save_full;
445 GdkPoint pos;
446
447 if (raw_data == NULL) {
448 return;
449 }
450
451 g_return_if_fail (action == GDK_ACTION_COPY);
452
453 container_uri = NULL;
454 if (target_uri == NULL) {
455 container_uri = nautilus_view_get_backing_uri (view);
456 g_assert (container_uri != NULL);
457 }
458
459 pos.x = x;
460 pos.y = y;
461 view_widget_to_file_operation_position (view, &pos);
462
463 filename = NULL;
464 if (direct_save_uri != NULL) {
465 direct_save_full = g_file_new_for_uri (direct_save_uri);
466 filename = g_file_get_basename (direct_save_full);
467 }
468 if (filename == NULL) {
469 /* Translator: This is the filename used for when you dnd raw
470 * data to a directory, if the source didn't supply a name.
471 */
472 filename = g_strdup (_("dropped data"));
473 }
474
475 nautilus_view_new_file_with_initial_contents (
476 view, target_uri != NULL ? target_uri : container_uri,
477 filename, raw_data, length, &pos);
478
479 g_free (container_uri);
480 g_free (filename);
481 }
482
483 void
484 nautilus_view_drop_proxy_received_uris (NautilusView *view,
485 const GList *source_uri_list,
486 const char *target_uri,
487 GdkDragAction action)
488 {
489 char *container_uri;
490
491 container_uri = NULL;
492 if (target_uri == NULL) {
493 container_uri = nautilus_view_get_backing_uri (view);
494 g_assert (container_uri != NULL);
495 }
496
497 if (action == GDK_ACTION_ASK) {
498 action = nautilus_drag_drop_action_ask
499 (GTK_WIDGET (view),
500 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
501 if (action == 0) {
502 return;
503 }
504 }
505
506 nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
507 source_uri_list,
508 nautilus_view_get_copied_files_atom (view));
509
510 nautilus_view_move_copy_items (view, source_uri_list, NULL,
511 target_uri != NULL ? target_uri : container_uri,
512 action, 0, 0);
513
514 g_free (container_uri);
515 }