r69: Tidied up the options system a bit. Added drag-and-drop section (with
[rox-filer.git] / ROX-Filer / src / dnd.c
blobbc0ad831d203ac644a96c9e950079a4b8828278b
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.
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},
368 FileItem *item;
370 if (number_selected == 1)
371 item = selected_item(collection);
372 else
373 item = NULL;
375 widget = GTK_WIDGET(collection);
377 target_list = gtk_target_list_new(target_table,
378 item && item->base_type == TYPE_FILE ? 2 : 1);
380 context = gtk_drag_begin(widget,
381 target_list,
382 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK,
383 (event->state & GDK_BUTTON1_MASK) ? 1 : 2,
384 (GdkEvent *) event);
385 g_dataset_set_data(context, "filer_window", filer_window);
387 image = item ? item->image : &default_pixmap[TYPE_MULTIPLE];
389 gtk_drag_set_icon_pixmap(context,
390 gtk_widget_get_colormap(widget),
391 image->pixmap,
392 image->mask,
393 0, 0);
396 /* Called when a remote app wants us to send it some data.
397 * TODO: Maybe we should handle errors better (ie, let the remote app know
398 * the drag has failed)?
400 void drag_data_get(GtkWidget *widget,
401 GdkDragContext *context,
402 GtkSelectionData *selection_data,
403 guint info,
404 guint32 time)
406 char *to_send = "E"; /* Default to sending an error */
407 long to_send_length = 1;
408 gboolean delete_once_sent = FALSE;
409 GdkAtom type = XA_STRING;
410 GString *string;
411 FilerWindow *filer_window;
412 FileItem *item;
414 filer_window = g_dataset_get_data(context, "filer_window");
415 g_return_if_fail(filer_window != NULL);
417 switch (info)
419 case TARGET_RAW:
420 item = selected_item(filer_window->collection);
421 if (item && load_file(make_path(filer_window->path,
422 item->leafname)->str,
423 &to_send, &to_send_length))
425 delete_once_sent = TRUE;
426 type = application_octet_stream; /* XXX */
427 break;
429 return;
430 case TARGET_URI_LIST:
431 string = g_string_new(NULL);
432 create_uri_list(string,
433 COLLECTION(widget),
434 filer_window);
435 to_send = string->str;
436 to_send_length = string->len;
437 delete_once_sent = TRUE;
438 g_string_free(string, FALSE);
439 break;
440 default:
441 delayed_error("drag_data_get",
442 "Internal error - bad info type\n");
443 break;
446 gtk_selection_data_set(selection_data,
447 type,
449 to_send,
450 to_send_length);
452 if (delete_once_sent)
453 g_free(to_send);
456 /* DRAGGING TO US */
458 /* Set up this filer window as a drop target. Called once, when the
459 * filer window is first created.
461 void drag_set_dest(GtkWidget *widget, FilerWindow *filer_window)
463 GtkTargetEntry target_table[] =
465 {"text/uri-list", 0, TARGET_URI_LIST},
466 {"XdndDirectSave0", 0, TARGET_XDS},
467 {"application/octet-stream", 0, TARGET_RAW},
470 gtk_drag_dest_set(widget,
471 0, /* GTK_DEST_DEFAULT_MOTION, */
472 target_table,
473 sizeof(target_table) / sizeof(*target_table),
474 GDK_ACTION_COPY | GDK_ACTION_MOVE
475 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
477 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
478 GTK_SIGNAL_FUNC(drag_motion), filer_window);
479 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
480 GTK_SIGNAL_FUNC(drag_leave), filer_window);
481 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
482 GTK_SIGNAL_FUNC(drag_drop), filer_window);
483 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
484 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
487 /* Decide if panel drag is OK, setting drop_dest_type and drop_dest_path
488 * on the context as needed.
490 static gboolean panel_drag_ok(FilerWindow *filer_window,
491 GdkDragContext *context,
492 int item)
494 FileItem *fileitem = NULL;
495 char *old_path;
496 char *new_path;
497 char *type;
499 panel_set_timeout(NULL, 0);
501 if (item >= 0)
502 fileitem = (FileItem *)
503 filer_window->collection->items[item].data;
505 if (item == -1)
507 new_path = NULL;
509 else if (fileitem->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
511 if (provides(context, text_uri_list))
513 type = drop_dest_prog;
514 new_path = make_path(filer_window->path,
515 fileitem->leafname)->str;
517 else
518 new_path = NULL;
520 else if (fileitem->base_type == TYPE_DIRECTORY)
522 type = drop_dest_dir;
523 new_path = make_path(filer_window->path,
524 fileitem->leafname)->str;
526 else
527 new_path = NULL;
529 if (new_path && access(new_path, W_OK))
530 new_path = NULL;
532 old_path = g_dataset_get_data(context, "drop_dest_path");
533 if (old_path == new_path ||
534 (old_path && new_path && strcmp(old_path, new_path) == 0))
536 return new_path != NULL; /* Same as before */
539 if (new_path)
541 g_dataset_set_data(context, "drop_dest_type", type);
542 g_dataset_set_data_full(context, "drop_dest_path",
543 g_strdup(new_path), g_free);
545 else
547 item = -1;
548 g_dataset_set_data(context, "drop_dest_path", NULL);
551 collection_set_cursor_item(filer_window->collection, item);
553 return new_path != NULL;
556 /* Called during the drag when the mouse is in a widget registered
557 * as a drop target. Returns TRUE if we can accept the drop.
559 static gboolean drag_motion(GtkWidget *widget,
560 GdkDragContext *context,
561 gint x,
562 gint y,
563 guint time)
565 FilerWindow *filer_window;
566 int item;
568 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
569 g_return_val_if_fail(filer_window != NULL, TRUE);
571 if (gtk_drag_get_source_widget(context) == widget)
572 return FALSE; /* Not within a single widget! */
574 if (filer_window->panel == FALSE)
576 if (access(filer_window->path, W_OK))
577 return FALSE; /* We can't write here */
578 gdk_drag_status(context, context->suggested_action, time);
579 return TRUE;
582 /* OK, this is a drag to a panel.
583 * Allow drags to directories, applications and X bit files only.
585 item = collection_get_item(filer_window->collection, x, y);
587 if (panel_drag_ok(filer_window, context, item))
589 gdk_drag_status(context, context->suggested_action, time);
590 return TRUE;
593 return FALSE;
596 /* Remove panel highlights */
597 static void drag_leave(GtkWidget *widget,
598 GdkDragContext *context)
600 FilerWindow *filer_window;
602 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
603 g_return_if_fail(filer_window != NULL);
605 panel_set_timeout(NULL, 0);
606 collection_set_cursor_item(filer_window->collection, -1);
609 /* User has tried to drop some data on us. Decide what format we would
610 * like the data in.
612 static gboolean drag_drop(GtkWidget *widget,
613 GdkDragContext *context,
614 gint x,
615 gint y,
616 guint time)
618 char *error = NULL;
619 char *leafname = NULL;
620 FilerWindow *filer_window;
621 GdkAtom target;
622 char *dest_path;
623 char *dest_type;
625 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
626 g_return_val_if_fail(filer_window != NULL, TRUE);
628 dest_path = g_dataset_get_data(context, "drop_dest_path");
629 if (dest_path == NULL)
631 if (filer_window->panel)
632 error = "Bad drop on panel";
633 else
635 dest_path = filer_window->path;
636 dest_type = drop_dest_dir;
639 else
641 dest_type = g_dataset_get_data(context, "drop_dest_type");
644 if (error)
646 /* Do nothing */
648 else if (dest_type == drop_dest_dir
649 && provides(context, XdndDirectSave0))
651 leafname = get_xds_prop(context);
652 if (leafname)
654 if (strchr(leafname, '/'))
656 error = "XDS protocol error: "
657 "leafname may not contain '/'\n";
658 g_free(leafname);
660 leafname = NULL;
662 else
664 GString *uri;
666 uri = g_string_new(NULL);
667 g_string_sprintf(uri, "file://%s%s",
668 our_host_name(),
669 make_path(dest_path,
670 leafname)->str);
671 set_xds_prop(context, uri->str);
672 g_string_free(uri, TRUE);
674 target = XdndDirectSave0;
675 g_dataset_set_data_full(context, "leafname",
676 leafname, g_free);
679 else
680 error = "XdndDirectSave0 target provided, but the atom "
681 "XdndDirectSave0 (type text/plain) did not "
682 "contain a leafname\n";
684 else if (provides(context, text_uri_list))
685 target = text_uri_list;
686 else
688 if (dest_type == drop_dest_dir)
689 error = "Sorry - I require a target type of "
690 "text/uri-list or XdndDirectSave0.";
691 else
692 error = "Sorry - I require a target type of "
693 "text/uri-list.";
696 if (error)
698 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
700 delayed_error("ROX-Filer", error);
702 else
703 gtk_drag_get_data(widget, context, target, time);
705 return TRUE;
708 /* Called when some data arrives from the remote app (which we asked for
709 * in drag_drop).
711 static void drag_data_received(GtkWidget *widget,
712 GdkDragContext *context,
713 gint x,
714 gint y,
715 GtkSelectionData *selection_data,
716 guint info,
717 guint32 time)
719 if (!selection_data->data)
721 /* Timeout? */
722 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
723 return;
726 switch (info)
728 case TARGET_XDS:
729 got_data_xds_reply(widget, context,
730 selection_data, time);
731 break;
732 case TARGET_RAW:
733 got_data_raw(widget, context, selection_data, time);
734 break;
735 case TARGET_URI_LIST:
736 got_uri_list(widget, context, selection_data, time);
737 break;
738 default:
739 gtk_drag_finish(context, FALSE, FALSE, time);
740 delayed_error("drag_data_received", "Unknown target");
741 break;
745 static void got_data_xds_reply(GtkWidget *widget,
746 GdkDragContext *context,
747 GtkSelectionData *selection_data,
748 guint32 time)
750 gboolean mark_unsafe = TRUE;
751 char response = *selection_data->data;
752 char *error = NULL;
754 if (selection_data->length != 1)
755 response = '?';
757 if (response == 'F')
759 /* Sender couldn't save there - ask for another
760 * type if possible.
762 if (provides(context, application_octet_stream))
764 mark_unsafe = FALSE; /* Wait and see */
766 gtk_drag_get_data(widget, context,
767 application_octet_stream, time);
769 else
770 error = "Remote app can't or won't send me "
771 "the data - sorry";
773 else if (response == 'S')
775 FilerWindow *filer_window;
777 /* Success - data is saved */
778 mark_unsafe = FALSE; /* It really is safe */
779 gtk_drag_finish(context, TRUE, FALSE, time);
781 filer_window = gtk_object_get_data(GTK_OBJECT(widget),
782 "filer_window");
783 g_return_if_fail(filer_window != NULL);
785 scan_dir(filer_window);
787 else if (response != 'E')
789 error = "XDS protocol error: "
790 "return code should be 'S', 'F' or 'E'\n";
792 /* else: error has been reported by the sender */
794 if (mark_unsafe)
796 set_xds_prop(context, "");
797 /* Unsave also implies that the drag failed */
798 gtk_drag_finish(context, FALSE, FALSE, time);
801 if (error)
802 delayed_error("ROX-Filer", error);
805 static void got_data_raw(GtkWidget *widget,
806 GdkDragContext *context,
807 GtkSelectionData *selection_data,
808 guint32 time)
810 FilerWindow *filer_window;
811 char *leafname;
812 int fd;
813 char *error = NULL;
814 char *dest_path;
816 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
817 g_return_if_fail(filer_window != NULL);
819 leafname = g_dataset_get_data(context, "leafname");
820 if (!leafname)
821 leafname = "UntitledData";
823 dest_path = get_dest_path(filer_window, context);
825 fd = open(make_path(dest_path, leafname)->str,
826 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
827 S_IRUSR | S_IRGRP | S_IROTH |
828 S_IWUSR | S_IWGRP | S_IWOTH);
830 if (fd == -1)
831 error = g_strerror(errno);
832 else
834 if (write(fd,
835 selection_data->data,
836 selection_data->length) == -1)
837 error = g_strerror(errno);
839 if (close(fd) == -1 && !error)
840 error = g_strerror(errno);
842 scan_dir(filer_window);
845 if (error)
847 if (provides(context, XdndDirectSave0))
848 set_xds_prop(context, "");
849 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
850 delayed_error("Error saving file", error);
852 else
853 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
856 /* Execute this program, passing all the URIs in the list as arguments.
857 * URIs that are files on the local machine will be passed as simple
858 * pathnames. The uri_list should be freed after this function returns.
860 static void run_with_files(char *path, GSList *uri_list)
862 char **argv;
863 int argc = 0;
864 struct stat info;
866 if (stat(path, &info))
868 delayed_error("ROX-Filer", "Program not found - deleted?");
869 return;
872 argv = g_malloc(sizeof(char *) * (g_slist_length(uri_list) + 2));
874 if (S_ISDIR(info.st_mode))
875 argv[argc++] = make_path(path, "AppRun")->str;
876 else
877 argv[argc++] = path;
879 while (uri_list)
881 char *uri = (char *) uri_list->data;
882 char *local;
884 local = get_local_path(uri);
885 if (local)
886 argv[argc++] = local;
887 else
888 argv[argc++] = uri;
889 uri_list = uri_list->next;
892 argv[argc++] = NULL;
894 if (!spawn_full(argv, getenv("HOME"), 0))
895 delayed_error("ROX-Filer", "Failed to fork() child process");
898 /* We've got a list of URIs from somewhere (probably another filer window).
899 * If the files are on the local machine then try to copy them ourselves,
900 * otherwise, if there was only one file and application/octet-stream was
901 * provided, get the data via the X server.
903 static void got_uri_list(GtkWidget *widget,
904 GdkDragContext *context,
905 GtkSelectionData *selection_data,
906 guint32 time)
908 FilerWindow *filer_window;
909 GSList *uri_list;
910 char *error = NULL;
911 char **argv = NULL; /* Command to exec, or NULL */
912 GSList *next_uri;
913 gboolean send_reply = TRUE;
914 char *dest_path;
915 char *type;
917 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
918 g_return_if_fail(filer_window != NULL);
920 dest_path = get_dest_path(filer_window, context);
921 type = g_dataset_get_data(context, "drop_dest_type");
923 uri_list = uri_list_to_gslist(selection_data->data);
925 if (!uri_list)
926 error = "No URIs in the text/uri-list (nothing to do!)";
927 else if (type == drop_dest_prog)
928 run_with_files(dest_path, uri_list);
929 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
931 /* There is one URI in the list, and it's not on the local
932 * machine. Get it via the X server if possible.
935 if (provides(context, application_octet_stream))
937 char *leaf;
938 leaf = strrchr(uri_list->data, '/');
939 if (leaf)
940 leaf++;
941 else
942 leaf = uri_list->data;
943 g_dataset_set_data_full(context, "leafname",
944 g_strdup(leaf), g_free);
945 gtk_drag_get_data(widget, context,
946 application_octet_stream, time);
947 send_reply = FALSE;
949 else
950 error = "Can't get data from remote machine "
951 "(application/octet-stream not provided)";
953 else
955 GSList *local_paths = NULL;
956 GSList *next;
958 /* Either one local URI, or a list. If everything in the list
959 * isn't local then we are stuck.
962 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
964 char *path;
966 path = get_local_path((char *) next_uri->data);
968 if (path)
969 local_paths = g_slist_append(local_paths,
970 g_strdup(path));
971 else
972 error = "Some of these files are on a "
973 "different machine - they will be "
974 "ignored - sorry";
977 if (!local_paths)
979 error = "None of these files are on the local machine "
980 "- I can't operate on multiple remote files - "
981 "sorry.";
983 else if (context->action == GDK_ACTION_MOVE)
984 action_move(local_paths, filer_window->path);
985 else if (context->action == GDK_ACTION_COPY)
986 action_copy(local_paths, filer_window->path);
987 else if (context->action == GDK_ACTION_LINK)
988 action_link(local_paths, filer_window->path);
989 else
990 error = "Unknown action requested";
992 for (next = local_paths; next; next = next->next)
993 g_free(next->data);
994 g_slist_free(local_paths);
997 if (error)
999 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
1000 delayed_error("Error getting file list", error);
1002 else if (send_reply)
1004 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
1005 if (argv)
1007 int child; /* Child process ID */
1009 child = spawn(argv);
1010 if (child)
1011 g_hash_table_insert(child_to_filer,
1012 (gpointer) child, filer_window);
1013 else
1014 delayed_error("ROX-Filer",
1015 "Failed to fork() child process");
1016 g_free(argv);
1020 next_uri = uri_list;
1021 while (next_uri)
1023 g_free(next_uri->data);
1024 next_uri = next_uri->next;
1026 g_slist_free(uri_list);