r70: Removed some old code. Dragging now quotes the correct MIME type for files.
[rox-filer.git] / ROX-Filer / src / dnd.c
blob214fc166830dc941c5dcd5df0edb07f896c227a5
1 /* vi: set cindent:
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
6 */
8 /* dnd.c - code for handling drag and drop */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <errno.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xatom.h>
20 #include <gtk/gtk.h>
21 #include <collection.h>
23 #include "filer.h"
24 #include "action.h"
25 #include "pixmaps.h"
26 #include "gui_support.h"
27 #include "support.h"
28 #include "options.h"
30 #define MAXURILEN 4096 /* Longest URI to allow */
32 /* Static prototypes */
33 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context);
34 static void create_uri_list(GString *string,
35 Collection *collection,
36 FilerWindow *filer_window);
37 static gboolean drag_drop(GtkWidget *widget,
38 GdkDragContext *context,
39 gint x,
40 gint y,
41 guint time);
42 static gboolean provides(GdkDragContext *context, GdkAtom target);
43 static void set_xds_prop(GdkDragContext *context, char *text);
44 static gboolean drag_motion(GtkWidget *widget,
45 GdkDragContext *context,
46 gint x,
47 gint y,
48 guint time);
49 static void drag_leave(GtkWidget *widget,
50 GdkDragContext *context);
51 static void drag_data_received(GtkWidget *widget,
52 GdkDragContext *context,
53 gint x,
54 gint y,
55 GtkSelectionData *selection_data,
56 guint info,
57 guint32 time);
58 static void got_data_xds_reply(GtkWidget *widget,
59 GdkDragContext *context,
60 GtkSelectionData *selection_data,
61 guint32 time);
62 static void got_data_raw(GtkWidget *widget,
63 GdkDragContext *context,
64 GtkSelectionData *selection_data,
65 guint32 time);
66 static GSList *uri_list_to_gslist(char *uri_list);
67 static void got_uri_list(GtkWidget *widget,
68 GdkDragContext *context,
69 GtkSelectionData *selection_data,
70 guint32 time);
71 static char *get_local_path(char *uri);
72 static void run_with_files(char *path, GSList *uri_list);
73 static GtkWidget *create_options();
74 static void update_options();
75 static void set_options();
76 static void save_options();
77 static char *load_no_hostnames(char *data);
79 /* Possible values for drop_dest_type (can also be NULL).
80 * In either case, drop_dest_path is the app/file/dir to use.
82 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
83 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
85 static OptionsSection options =
87 "Drag and Drop options",
88 create_options,
89 update_options,
90 set_options,
91 save_options
94 enum
96 TARGET_RAW,
97 TARGET_URI_LIST,
98 TARGET_XDS,
101 GdkAtom XdndDirectSave0;
102 GdkAtom text_plain;
103 GdkAtom text_uri_list;
104 GdkAtom application_octet_stream;
106 void dnd_init()
108 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
109 text_plain = gdk_atom_intern("text/plain", FALSE);
110 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
111 application_octet_stream = gdk_atom_intern("application/octet-stream",
112 FALSE);
114 options_sections = g_slist_prepend(options_sections, &options);
115 option_register("dnd_no_hostnames", load_no_hostnames);
118 /* OPTIONS */
120 static gboolean o_no_hostnames = FALSE;
121 static GtkWidget *toggle_no_hostnames;
123 /* Build up some option widgets to go in the options dialog, but don't
124 * fill them in yet.
126 static GtkWidget *create_options()
128 GtkWidget *vbox, *label;
130 vbox = gtk_vbox_new(FALSE, 0);
131 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
133 label = gtk_label_new("Some older applications don't support XDND "
134 "fully and may need to have this option turned on. "
135 "Use this if dragging files to an application shows "
136 "a + sign on the pointer but the drop doesn't work.");
137 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
138 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
140 toggle_no_hostnames =
141 gtk_check_button_new_with_label("Don't use hostnames");
142 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
144 return vbox;
147 static void update_options()
149 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
150 o_no_hostnames);
153 static void set_options()
155 o_no_hostnames = gtk_toggle_button_get_active(
156 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
159 static void save_options()
161 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
164 static char *load_no_hostnames(char *data)
166 o_no_hostnames = atoi(data) != 0;
167 return NULL;
170 /* SUPPORT FUNCTIONS */
172 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context)
174 char *path;
176 path = g_dataset_get_data(context, "drop_dest_path");
178 return path ? path : filer_window->path;
181 /* Set the XdndDirectSave0 property on the source window for this context */
182 static void set_xds_prop(GdkDragContext *context, char *text)
184 gdk_property_change(context->source_window,
185 XdndDirectSave0,
186 text_plain, 8,
187 GDK_PROP_MODE_REPLACE,
188 text,
189 strlen(text));
192 static char *get_xds_prop(GdkDragContext *context)
194 guchar *prop_text;
195 gint length;
197 if (gdk_property_get(context->source_window,
198 XdndDirectSave0,
199 text_plain,
200 0, MAXURILEN,
201 FALSE,
202 NULL, NULL,
203 &length, &prop_text) && prop_text)
205 /* Terminate the string */
206 prop_text = g_realloc(prop_text, length + 1);
207 prop_text[length] = '\0';
208 return prop_text;
211 return NULL;
214 /* Is the sender willing to supply this target type? */
215 static gboolean provides(GdkDragContext *context, GdkAtom target)
217 GList *targets = context->targets;
219 while (targets && ((GdkAtom) targets->data != target))
220 targets = targets->next;
222 return targets != NULL;
225 /* Convert a URI to a local pathname (or NULL if it isn't local).
226 * The returned pointer points inside the input string.
227 * Possible formats:
228 * /path
229 * ///path
230 * //host/path
231 * file://host/path
233 static char *get_local_path(char *uri)
235 char *host;
237 host = our_host_name();
239 if (*uri == '/')
241 char *path;
243 if (uri[1] != '/')
244 return uri; /* Just a local path - no host part */
246 path = strchr(uri + 2, '/');
247 if (!path)
248 return NULL; /* //something */
250 if (path - uri == 2)
251 return path; /* ///path */
252 if (strlen(host) == path - uri - 2 &&
253 strncmp(uri + 2, host, path - uri - 2) == 0)
254 return path; /* //myhost/path */
256 return NULL; /* From a different host */
258 else
260 if (strncasecmp(uri, "file:", 5))
261 return NULL; /* Don't know this format */
263 uri += 5;
265 if (*uri == '/')
266 return get_local_path(uri);
268 return NULL;
272 /* Convert a list of URIs into a list of strings.
273 * Lines beginning with # are skipped.
274 * The text block passed in is zero terminated (after the final CRLF)
276 static GSList *uri_list_to_gslist(char *uri_list)
278 GSList *list = NULL;
280 while (*uri_list)
282 char *linebreak;
283 char *uri;
284 int length;
286 linebreak = strchr(uri_list, 13);
288 if (!linebreak || linebreak[1] != 10)
290 delayed_error("uri_list_to_gslist",
291 "Incorrect or missing line break "
292 "in text/uri-list data");
293 return list;
296 length = linebreak - uri_list;
298 if (length && uri_list[0] != '#')
300 uri = g_malloc(sizeof(char) * (length + 1));
301 strncpy(uri, uri_list, length);
302 uri[length] = 0;
303 list = g_slist_append(list, uri);
306 uri_list = linebreak + 2;
309 return list;
312 /* Append all the URIs in the selection to the string */
313 static void create_uri_list(GString *string,
314 Collection *collection,
315 FilerWindow *filer_window)
317 GString *leader;
318 int i, num_selected;
320 leader = g_string_new("file://");
321 if (!o_no_hostnames)
322 g_string_append(leader, our_host_name());
323 g_string_append(leader, filer_window->path);
324 if (leader->str[leader->len - 1] != '/')
325 g_string_append_c(leader, '/');
327 num_selected = collection->number_selected;
329 for (i = 0; num_selected > 0; i++)
331 if (collection->items[i].selected)
333 FileItem *item = (FileItem *) collection->items[i].data;
335 g_string_append(string, leader->str);
336 g_string_append(string, item->leafname);
337 g_string_append(string, "\r\n");
338 num_selected--;
342 g_string_free(leader, TRUE);
345 /* DRAGGING FROM US */
347 /* The user has held the mouse button down over an item and moved -
348 * start a drag.
350 * We always provide text/uri-list. If we are dragging a single, regular file
351 * then we also offer application/octet-stream and the type of the file.
353 void drag_selection(Collection *collection,
354 GdkEventMotion *event,
355 gint number_selected,
356 gpointer user_data)
358 FilerWindow *filer_window = (FilerWindow *) user_data;
359 GtkWidget *widget;
360 MaskedPixmap *image;
361 GdkDragContext *context;
362 GtkTargetList *target_list;
363 GtkTargetEntry target_table[] =
365 {"text/uri-list", 0, TARGET_URI_LIST},
366 {"application/octet-stream", 0, TARGET_RAW},
367 {"", 0, TARGET_RAW},
369 FileItem *item;
371 if (number_selected == 1)
372 item = selected_item(collection);
373 else
374 item = NULL;
376 widget = GTK_WIDGET(collection);
378 if (item && item->mime_type)
380 MIME_type *t = item->mime_type;
382 target_table[2].target = g_strconcat(t->media_type, "/",
383 t->subtype, NULL);
384 target_list = gtk_target_list_new(target_table, 3);
385 g_free(target_table[2].target);
387 else
388 target_list = gtk_target_list_new(target_table, 1);
390 context = gtk_drag_begin(widget,
391 target_list,
392 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK,
393 (event->state & GDK_BUTTON1_MASK) ? 1 : 2,
394 (GdkEvent *) event);
395 g_dataset_set_data(context, "filer_window", filer_window);
397 image = item ? item->image : &default_pixmap[TYPE_MULTIPLE];
399 gtk_drag_set_icon_pixmap(context,
400 gtk_widget_get_colormap(widget),
401 image->pixmap,
402 image->mask,
403 0, 0);
406 /* Called when a remote app wants us to send it some data.
407 * TODO: Maybe we should handle errors better (ie, let the remote app know
408 * the drag has failed)?
410 void drag_data_get(GtkWidget *widget,
411 GdkDragContext *context,
412 GtkSelectionData *selection_data,
413 guint info,
414 guint32 time)
416 char *to_send = "E"; /* Default to sending an error */
417 long to_send_length = 1;
418 gboolean delete_once_sent = FALSE;
419 GdkAtom type = XA_STRING;
420 GString *string;
421 FilerWindow *filer_window;
422 FileItem *item;
424 filer_window = g_dataset_get_data(context, "filer_window");
425 g_return_if_fail(filer_window != NULL);
427 switch (info)
429 case TARGET_RAW:
430 item = selected_item(filer_window->collection);
431 if (item && load_file(make_path(filer_window->path,
432 item->leafname)->str,
433 &to_send, &to_send_length))
435 delete_once_sent = TRUE;
436 if (item->mime_type)
437 type = type_to_atom(item->mime_type);
438 else
439 type = application_octet_stream;
440 break;
442 return;
443 case TARGET_URI_LIST:
444 string = g_string_new(NULL);
445 create_uri_list(string,
446 COLLECTION(widget),
447 filer_window);
448 to_send = string->str;
449 to_send_length = string->len;
450 delete_once_sent = TRUE;
451 g_string_free(string, FALSE);
452 break;
453 default:
454 delayed_error("drag_data_get",
455 "Internal error - bad info type\n");
456 break;
459 gtk_selection_data_set(selection_data,
460 type,
462 to_send,
463 to_send_length);
465 if (delete_once_sent)
466 g_free(to_send);
469 /* DRAGGING TO US */
471 /* Set up this filer window as a drop target. Called once, when the
472 * filer window is first created.
474 void drag_set_dest(GtkWidget *widget, FilerWindow *filer_window)
476 GtkTargetEntry target_table[] =
478 {"text/uri-list", 0, TARGET_URI_LIST},
479 {"XdndDirectSave0", 0, TARGET_XDS},
480 {"application/octet-stream", 0, TARGET_RAW},
483 gtk_drag_dest_set(widget,
484 0, /* GTK_DEST_DEFAULT_MOTION, */
485 target_table,
486 sizeof(target_table) / sizeof(*target_table),
487 GDK_ACTION_COPY | GDK_ACTION_MOVE
488 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
490 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
491 GTK_SIGNAL_FUNC(drag_motion), filer_window);
492 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
493 GTK_SIGNAL_FUNC(drag_leave), filer_window);
494 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
495 GTK_SIGNAL_FUNC(drag_drop), filer_window);
496 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
497 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
500 /* Decide if panel drag is OK, setting drop_dest_type and drop_dest_path
501 * on the context as needed.
503 static gboolean panel_drag_ok(FilerWindow *filer_window,
504 GdkDragContext *context,
505 int item)
507 FileItem *fileitem = NULL;
508 char *old_path;
509 char *new_path;
510 char *type;
512 panel_set_timeout(NULL, 0);
514 if (item >= 0)
515 fileitem = (FileItem *)
516 filer_window->collection->items[item].data;
518 if (item == -1)
520 new_path = NULL;
522 else if (fileitem->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
524 if (provides(context, text_uri_list))
526 type = drop_dest_prog;
527 new_path = make_path(filer_window->path,
528 fileitem->leafname)->str;
530 else
531 new_path = NULL;
533 else if (fileitem->base_type == TYPE_DIRECTORY)
535 type = drop_dest_dir;
536 new_path = make_path(filer_window->path,
537 fileitem->leafname)->str;
539 else
540 new_path = NULL;
542 if (new_path && access(new_path, W_OK))
543 new_path = NULL;
545 old_path = g_dataset_get_data(context, "drop_dest_path");
546 if (old_path == new_path ||
547 (old_path && new_path && strcmp(old_path, new_path) == 0))
549 return new_path != NULL; /* Same as before */
552 if (new_path)
554 g_dataset_set_data(context, "drop_dest_type", type);
555 g_dataset_set_data_full(context, "drop_dest_path",
556 g_strdup(new_path), g_free);
558 else
560 item = -1;
561 g_dataset_set_data(context, "drop_dest_path", NULL);
564 collection_set_cursor_item(filer_window->collection, item);
566 return new_path != NULL;
569 /* Called during the drag when the mouse is in a widget registered
570 * as a drop target. Returns TRUE if we can accept the drop.
572 static gboolean drag_motion(GtkWidget *widget,
573 GdkDragContext *context,
574 gint x,
575 gint y,
576 guint time)
578 FilerWindow *filer_window;
579 int item;
581 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
582 g_return_val_if_fail(filer_window != NULL, TRUE);
584 if (gtk_drag_get_source_widget(context) == widget)
585 return FALSE; /* Not within a single widget! */
587 if (filer_window->panel == FALSE)
589 if (access(filer_window->path, W_OK))
590 return FALSE; /* We can't write here */
591 gdk_drag_status(context, context->suggested_action, time);
592 return TRUE;
595 /* OK, this is a drag to a panel.
596 * Allow drags to directories, applications and X bit files only.
598 item = collection_get_item(filer_window->collection, x, y);
600 if (panel_drag_ok(filer_window, context, item))
602 gdk_drag_status(context, context->suggested_action, time);
603 return TRUE;
606 return FALSE;
609 /* Remove panel highlights */
610 static void drag_leave(GtkWidget *widget,
611 GdkDragContext *context)
613 FilerWindow *filer_window;
615 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
616 g_return_if_fail(filer_window != NULL);
618 panel_set_timeout(NULL, 0);
619 collection_set_cursor_item(filer_window->collection, -1);
622 /* User has tried to drop some data on us. Decide what format we would
623 * like the data in.
625 static gboolean drag_drop(GtkWidget *widget,
626 GdkDragContext *context,
627 gint x,
628 gint y,
629 guint time)
631 char *error = NULL;
632 char *leafname = NULL;
633 FilerWindow *filer_window;
634 GdkAtom target;
635 char *dest_path;
636 char *dest_type;
638 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
639 g_return_val_if_fail(filer_window != NULL, TRUE);
641 dest_path = g_dataset_get_data(context, "drop_dest_path");
642 if (dest_path == NULL)
644 if (filer_window->panel)
645 error = "Bad drop on panel";
646 else
648 dest_path = filer_window->path;
649 dest_type = drop_dest_dir;
652 else
654 dest_type = g_dataset_get_data(context, "drop_dest_type");
657 if (error)
659 /* Do nothing */
661 else if (dest_type == drop_dest_dir
662 && provides(context, XdndDirectSave0))
664 leafname = get_xds_prop(context);
665 if (leafname)
667 if (strchr(leafname, '/'))
669 error = "XDS protocol error: "
670 "leafname may not contain '/'\n";
671 g_free(leafname);
673 leafname = NULL;
675 else
677 GString *uri;
679 uri = g_string_new(NULL);
680 g_string_sprintf(uri, "file://%s%s",
681 our_host_name(),
682 make_path(dest_path,
683 leafname)->str);
684 set_xds_prop(context, uri->str);
685 g_string_free(uri, TRUE);
687 target = XdndDirectSave0;
688 g_dataset_set_data_full(context, "leafname",
689 leafname, g_free);
692 else
693 error = "XdndDirectSave0 target provided, but the atom "
694 "XdndDirectSave0 (type text/plain) did not "
695 "contain a leafname\n";
697 else if (provides(context, text_uri_list))
698 target = text_uri_list;
699 else
701 if (dest_type == drop_dest_dir)
702 error = "Sorry - I require a target type of "
703 "text/uri-list or XdndDirectSave0.";
704 else
705 error = "Sorry - I require a target type of "
706 "text/uri-list.";
709 if (error)
711 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
713 delayed_error("ROX-Filer", error);
715 else
716 gtk_drag_get_data(widget, context, target, time);
718 return TRUE;
721 /* Called when some data arrives from the remote app (which we asked for
722 * in drag_drop).
724 static void drag_data_received(GtkWidget *widget,
725 GdkDragContext *context,
726 gint x,
727 gint y,
728 GtkSelectionData *selection_data,
729 guint info,
730 guint32 time)
732 if (!selection_data->data)
734 /* Timeout? */
735 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
736 return;
739 switch (info)
741 case TARGET_XDS:
742 got_data_xds_reply(widget, context,
743 selection_data, time);
744 break;
745 case TARGET_RAW:
746 got_data_raw(widget, context, selection_data, time);
747 break;
748 case TARGET_URI_LIST:
749 got_uri_list(widget, context, selection_data, time);
750 break;
751 default:
752 gtk_drag_finish(context, FALSE, FALSE, time);
753 delayed_error("drag_data_received", "Unknown target");
754 break;
758 static void got_data_xds_reply(GtkWidget *widget,
759 GdkDragContext *context,
760 GtkSelectionData *selection_data,
761 guint32 time)
763 gboolean mark_unsafe = TRUE;
764 char response = *selection_data->data;
765 char *error = NULL;
767 if (selection_data->length != 1)
768 response = '?';
770 if (response == 'F')
772 /* Sender couldn't save there - ask for another
773 * type if possible.
775 if (provides(context, application_octet_stream))
777 mark_unsafe = FALSE; /* Wait and see */
779 gtk_drag_get_data(widget, context,
780 application_octet_stream, time);
782 else
783 error = "Remote app can't or won't send me "
784 "the data - sorry";
786 else if (response == 'S')
788 FilerWindow *filer_window;
790 /* Success - data is saved */
791 mark_unsafe = FALSE; /* It really is safe */
792 gtk_drag_finish(context, TRUE, FALSE, time);
794 filer_window = gtk_object_get_data(GTK_OBJECT(widget),
795 "filer_window");
796 g_return_if_fail(filer_window != NULL);
798 scan_dir(filer_window);
800 else if (response != 'E')
802 error = "XDS protocol error: "
803 "return code should be 'S', 'F' or 'E'\n";
805 /* else: error has been reported by the sender */
807 if (mark_unsafe)
809 set_xds_prop(context, "");
810 /* Unsave also implies that the drag failed */
811 gtk_drag_finish(context, FALSE, FALSE, time);
814 if (error)
815 delayed_error("ROX-Filer", error);
818 static void got_data_raw(GtkWidget *widget,
819 GdkDragContext *context,
820 GtkSelectionData *selection_data,
821 guint32 time)
823 FilerWindow *filer_window;
824 char *leafname;
825 int fd;
826 char *error = NULL;
827 char *dest_path;
829 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
830 g_return_if_fail(filer_window != NULL);
832 leafname = g_dataset_get_data(context, "leafname");
833 if (!leafname)
834 leafname = "UntitledData";
836 dest_path = get_dest_path(filer_window, context);
838 fd = open(make_path(dest_path, leafname)->str,
839 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
840 S_IRUSR | S_IRGRP | S_IROTH |
841 S_IWUSR | S_IWGRP | S_IWOTH);
843 if (fd == -1)
844 error = g_strerror(errno);
845 else
847 if (write(fd,
848 selection_data->data,
849 selection_data->length) == -1)
850 error = g_strerror(errno);
852 if (close(fd) == -1 && !error)
853 error = g_strerror(errno);
855 scan_dir(filer_window);
858 if (error)
860 if (provides(context, XdndDirectSave0))
861 set_xds_prop(context, "");
862 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
863 delayed_error("Error saving file", error);
865 else
866 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
869 /* Execute this program, passing all the URIs in the list as arguments.
870 * URIs that are files on the local machine will be passed as simple
871 * pathnames. The uri_list should be freed after this function returns.
873 static void run_with_files(char *path, GSList *uri_list)
875 char **argv;
876 int argc = 0;
877 struct stat info;
879 if (stat(path, &info))
881 delayed_error("ROX-Filer", "Program not found - deleted?");
882 return;
885 argv = g_malloc(sizeof(char *) * (g_slist_length(uri_list) + 2));
887 if (S_ISDIR(info.st_mode))
888 argv[argc++] = make_path(path, "AppRun")->str;
889 else
890 argv[argc++] = path;
892 while (uri_list)
894 char *uri = (char *) uri_list->data;
895 char *local;
897 local = get_local_path(uri);
898 if (local)
899 argv[argc++] = local;
900 else
901 argv[argc++] = uri;
902 uri_list = uri_list->next;
905 argv[argc++] = NULL;
907 if (!spawn_full(argv, getenv("HOME"), 0))
908 delayed_error("ROX-Filer", "Failed to fork() child process");
911 /* We've got a list of URIs from somewhere (probably another filer window).
912 * If the files are on the local machine then try to copy them ourselves,
913 * otherwise, if there was only one file and application/octet-stream was
914 * provided, get the data via the X server.
916 static void got_uri_list(GtkWidget *widget,
917 GdkDragContext *context,
918 GtkSelectionData *selection_data,
919 guint32 time)
921 FilerWindow *filer_window;
922 GSList *uri_list;
923 char *error = NULL;
924 GSList *next_uri;
925 gboolean send_reply = TRUE;
926 char *dest_path;
927 char *type;
929 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
930 g_return_if_fail(filer_window != NULL);
932 dest_path = get_dest_path(filer_window, context);
933 type = g_dataset_get_data(context, "drop_dest_type");
935 uri_list = uri_list_to_gslist(selection_data->data);
937 if (!uri_list)
938 error = "No URIs in the text/uri-list (nothing to do!)";
939 else if (type == drop_dest_prog)
940 run_with_files(dest_path, uri_list);
941 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
943 /* There is one URI in the list, and it's not on the local
944 * machine. Get it via the X server if possible.
947 if (provides(context, application_octet_stream))
949 char *leaf;
950 leaf = strrchr(uri_list->data, '/');
951 if (leaf)
952 leaf++;
953 else
954 leaf = uri_list->data;
955 g_dataset_set_data_full(context, "leafname",
956 g_strdup(leaf), g_free);
957 gtk_drag_get_data(widget, context,
958 application_octet_stream, time);
959 send_reply = FALSE;
961 else
962 error = "Can't get data from remote machine "
963 "(application/octet-stream not provided)";
965 else
967 GSList *local_paths = NULL;
968 GSList *next;
970 /* Either one local URI, or a list. If everything in the list
971 * isn't local then we are stuck.
974 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
976 char *path;
978 path = get_local_path((char *) next_uri->data);
980 if (path)
981 local_paths = g_slist_append(local_paths,
982 g_strdup(path));
983 else
984 error = "Some of these files are on a "
985 "different machine - they will be "
986 "ignored - sorry";
989 if (!local_paths)
991 error = "None of these files are on the local machine "
992 "- I can't operate on multiple remote files - "
993 "sorry.";
995 else if (context->action == GDK_ACTION_MOVE)
996 action_move(local_paths, filer_window->path);
997 else if (context->action == GDK_ACTION_COPY)
998 action_copy(local_paths, filer_window->path);
999 else if (context->action == GDK_ACTION_LINK)
1000 action_link(local_paths, filer_window->path);
1001 else
1002 error = "Unknown action requested";
1004 for (next = local_paths; next; next = next->next)
1005 g_free(next->data);
1006 g_slist_free(local_paths);
1009 if (error)
1011 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
1012 delayed_error("Error getting file list", error);
1014 else if (send_reply)
1015 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
1017 next_uri = uri_list;
1018 while (next_uri)
1020 g_free(next_uri->data);
1021 next_uri = next_uri->next;
1023 g_slist_free(uri_list);