r180: Updated filer action code to use exec instead of system (stops problems
[rox-filer/ma.git] / ROX-Filer / src / dnd.c
blob5baeba6cc07e9ca8fc74b765de0b5439668879e7
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* dnd.c - code for handling drag and drop */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <errno.h>
32 #include <X11/Xlib.h>
33 #include <X11/Xatom.h>
34 #include <gtk/gtk.h>
35 #include "collection.h"
37 #include "filer.h"
38 #include "action.h"
39 #include "pixmaps.h"
40 #include "gui_support.h"
41 #include "support.h"
42 #include "options.h"
44 #define MAXURILEN 4096 /* Longest URI to allow */
46 /* Static prototypes */
47 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context);
48 static void create_uri_list(GString *string,
49 Collection *collection,
50 FilerWindow *filer_window);
51 static gboolean drag_drop(GtkWidget *widget,
52 GdkDragContext *context,
53 gint x,
54 gint y,
55 guint time);
56 static gboolean provides(GdkDragContext *context, GdkAtom target);
57 static void set_xds_prop(GdkDragContext *context, char *text);
58 static gboolean drag_motion(GtkWidget *widget,
59 GdkDragContext *context,
60 gint x,
61 gint y,
62 guint time);
63 static void drag_leave(GtkWidget *widget,
64 GdkDragContext *context);
65 static void drag_data_received(GtkWidget *widget,
66 GdkDragContext *context,
67 gint x,
68 gint y,
69 GtkSelectionData *selection_data,
70 guint info,
71 guint32 time);
72 static void got_data_xds_reply(GtkWidget *widget,
73 GdkDragContext *context,
74 GtkSelectionData *selection_data,
75 guint32 time);
76 static void got_data_raw(GtkWidget *widget,
77 GdkDragContext *context,
78 GtkSelectionData *selection_data,
79 guint32 time);
80 static GSList *uri_list_to_gslist(char *uri_list);
81 static void got_uri_list(GtkWidget *widget,
82 GdkDragContext *context,
83 GtkSelectionData *selection_data,
84 guint32 time);
85 static char *get_local_path(char *uri);
86 static void run_with_files(char *path, GSList *uri_list);
87 static GtkWidget *create_options();
88 static void update_options();
89 static void set_options();
90 static void save_options();
91 static char *load_no_hostnames(char *data);
93 /* Possible values for drop_dest_type (can also be NULL).
94 * In either case, drop_dest_path is the app/file/dir to use.
96 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
97 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
99 static OptionsSection options =
101 "Drag and Drop options",
102 create_options,
103 update_options,
104 set_options,
105 save_options
108 enum
110 TARGET_RAW,
111 TARGET_URI_LIST,
112 TARGET_XDS,
115 GdkAtom XdndDirectSave0;
116 GdkAtom xa_text_plain;
117 GdkAtom text_uri_list;
118 GdkAtom application_octet_stream;
120 void dnd_init()
122 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
123 xa_text_plain = gdk_atom_intern("text/plain", FALSE);
124 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
125 application_octet_stream = gdk_atom_intern("application/octet-stream",
126 FALSE);
128 options_sections = g_slist_prepend(options_sections, &options);
129 option_register("dnd_no_hostnames", load_no_hostnames);
132 /* OPTIONS */
134 static gboolean o_no_hostnames = FALSE;
135 static GtkWidget *toggle_no_hostnames;
137 /* Build up some option widgets to go in the options dialog, but don't
138 * fill them in yet.
140 static GtkWidget *create_options()
142 GtkWidget *vbox, *label;
144 vbox = gtk_vbox_new(FALSE, 0);
145 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
147 label = gtk_label_new("Some older applications don't support XDND "
148 "fully and may need to have this option turned on. "
149 "Use this if dragging files to an application shows "
150 "a + sign on the pointer but the drop doesn't work.");
151 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
152 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
154 toggle_no_hostnames =
155 gtk_check_button_new_with_label("Don't use hostnames");
156 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
158 return vbox;
161 static void update_options()
163 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
164 o_no_hostnames);
167 static void set_options()
169 o_no_hostnames = gtk_toggle_button_get_active(
170 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
173 static void save_options()
175 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
178 static char *load_no_hostnames(char *data)
180 o_no_hostnames = atoi(data) != 0;
181 return NULL;
184 /* SUPPORT FUNCTIONS */
186 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context)
188 char *path;
190 path = g_dataset_get_data(context, "drop_dest_path");
192 return path ? path : filer_window->path;
195 /* Set the XdndDirectSave0 property on the source window for this context */
196 static void set_xds_prop(GdkDragContext *context, char *text)
198 gdk_property_change(context->source_window,
199 XdndDirectSave0,
200 xa_text_plain, 8,
201 GDK_PROP_MODE_REPLACE,
202 text,
203 strlen(text));
206 static char *get_xds_prop(GdkDragContext *context)
208 guchar *prop_text;
209 gint length;
211 if (gdk_property_get(context->source_window,
212 XdndDirectSave0,
213 xa_text_plain,
214 0, MAXURILEN,
215 FALSE,
216 NULL, NULL,
217 &length, &prop_text) && prop_text)
219 /* Terminate the string */
220 prop_text = g_realloc(prop_text, length + 1);
221 prop_text[length] = '\0';
222 return prop_text;
225 return NULL;
228 /* Is the sender willing to supply this target type? */
229 static gboolean provides(GdkDragContext *context, GdkAtom target)
231 GList *targets = context->targets;
233 while (targets && ((GdkAtom) targets->data != target))
234 targets = targets->next;
236 return targets != NULL;
239 /* Convert a URI to a local pathname (or NULL if it isn't local).
240 * The returned pointer points inside the input string.
241 * Possible formats:
242 * /path
243 * ///path
244 * //host/path
245 * file://host/path
247 static char *get_local_path(char *uri)
249 char *host;
251 host = our_host_name();
253 if (*uri == '/')
255 char *path;
257 if (uri[1] != '/')
258 return uri; /* Just a local path - no host part */
260 path = strchr(uri + 2, '/');
261 if (!path)
262 return NULL; /* //something */
264 if (path - uri == 2)
265 return path; /* ///path */
266 if (strlen(host) == path - uri - 2 &&
267 strncmp(uri + 2, host, path - uri - 2) == 0)
268 return path; /* //myhost/path */
270 return NULL; /* From a different host */
272 else
274 if (strncasecmp(uri, "file:", 5))
275 return NULL; /* Don't know this format */
277 uri += 5;
279 if (*uri == '/')
280 return get_local_path(uri);
282 return NULL;
286 /* Convert a list of URIs into a list of strings.
287 * Lines beginning with # are skipped.
288 * The text block passed in is zero terminated (after the final CRLF)
290 static GSList *uri_list_to_gslist(char *uri_list)
292 GSList *list = NULL;
294 while (*uri_list)
296 char *linebreak;
297 char *uri;
298 int length;
300 linebreak = strchr(uri_list, 13);
302 if (!linebreak || linebreak[1] != 10)
304 delayed_error("uri_list_to_gslist",
305 "Incorrect or missing line break "
306 "in text/uri-list data");
307 return list;
310 length = linebreak - uri_list;
312 if (length && uri_list[0] != '#')
314 uri = g_malloc(sizeof(char) * (length + 1));
315 strncpy(uri, uri_list, length);
316 uri[length] = 0;
317 list = g_slist_append(list, uri);
320 uri_list = linebreak + 2;
323 return list;
326 /* Append all the URIs in the selection to the string */
327 static void create_uri_list(GString *string,
328 Collection *collection,
329 FilerWindow *filer_window)
331 GString *leader;
332 int i, num_selected;
334 leader = g_string_new("file://");
335 if (!o_no_hostnames)
336 g_string_append(leader, our_host_name());
337 g_string_append(leader, filer_window->path);
338 if (leader->str[leader->len - 1] != '/')
339 g_string_append_c(leader, '/');
341 num_selected = collection->number_selected;
343 for (i = 0; num_selected > 0; i++)
345 if (collection->items[i].selected)
347 DirItem *item = (DirItem *) collection->items[i].data;
349 g_string_append(string, leader->str);
350 g_string_append(string, item->leafname);
351 g_string_append(string, "\r\n");
352 num_selected--;
356 g_string_free(leader, TRUE);
359 /* DRAGGING FROM US */
361 /* The user has held the mouse button down over an item and moved -
362 * start a drag.
364 * We always provide text/uri-list. If we are dragging a single, regular file
365 * then we also offer application/octet-stream and the type of the file.
367 void drag_selection(Collection *collection,
368 GdkEventMotion *event,
369 gint number_selected,
370 gpointer user_data)
372 FilerWindow *filer_window = (FilerWindow *) user_data;
373 GtkWidget *widget;
374 MaskedPixmap *image;
375 GdkDragContext *context;
376 GtkTargetList *target_list;
377 GtkTargetEntry target_table[] =
379 {"text/uri-list", 0, TARGET_URI_LIST},
380 {"application/octet-stream", 0, TARGET_RAW},
381 {"", 0, TARGET_RAW},
383 DirItem *item;
385 if (number_selected == 1)
386 item = selected_item(collection);
387 else
388 item = NULL;
390 widget = GTK_WIDGET(collection);
392 if (item && item->mime_type)
394 MIME_type *t = item->mime_type;
396 target_table[2].target = g_strconcat(t->media_type, "/",
397 t->subtype, NULL);
398 target_list = gtk_target_list_new(target_table, 3);
399 g_free(target_table[2].target);
401 else
402 target_list = gtk_target_list_new(target_table, 1);
404 context = gtk_drag_begin(widget,
405 target_list,
406 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK,
407 (event->state & GDK_BUTTON1_MASK) ? 1 : 2,
408 (GdkEvent *) event);
409 g_dataset_set_data(context, "filer_window", filer_window);
411 image = item ? item->image : &default_pixmap[TYPE_MULTIPLE];
413 gtk_drag_set_icon_pixmap(context,
414 gtk_widget_get_colormap(widget),
415 image->pixmap,
416 image->mask,
417 0, 0);
420 /* Called when a remote app wants us to send it some data.
421 * TODO: Maybe we should handle errors better (ie, let the remote app know
422 * the drag has failed)?
424 void drag_data_get(GtkWidget *widget,
425 GdkDragContext *context,
426 GtkSelectionData *selection_data,
427 guint info,
428 guint32 time)
430 char *to_send = "E"; /* Default to sending an error */
431 long to_send_length = 1;
432 gboolean delete_once_sent = FALSE;
433 GdkAtom type = XA_STRING;
434 GString *string;
435 FilerWindow *filer_window;
436 DirItem *item;
438 filer_window = g_dataset_get_data(context, "filer_window");
439 g_return_if_fail(filer_window != NULL);
441 switch (info)
443 case TARGET_RAW:
444 item = selected_item(filer_window->collection);
445 if (item && load_file(make_path(filer_window->path,
446 item->leafname)->str,
447 &to_send, &to_send_length))
449 delete_once_sent = TRUE;
450 if (item->mime_type)
451 type = type_to_atom(item->mime_type);
452 else
453 type = application_octet_stream;
454 break;
456 return;
457 case TARGET_URI_LIST:
458 string = g_string_new(NULL);
459 create_uri_list(string,
460 COLLECTION(widget),
461 filer_window);
462 to_send = string->str;
463 to_send_length = string->len;
464 delete_once_sent = TRUE;
465 g_string_free(string, FALSE);
466 break;
467 default:
468 delayed_error("drag_data_get",
469 "Internal error - bad info type\n");
470 break;
473 gtk_selection_data_set(selection_data,
474 type,
476 to_send,
477 to_send_length);
479 if (delete_once_sent)
480 g_free(to_send);
483 /* DRAGGING TO US */
485 /* Set up this filer window as a drop target. Called once, when the
486 * filer window is first created.
488 void drag_set_dest(GtkWidget *widget, FilerWindow *filer_window)
490 GtkTargetEntry target_table[] =
492 {"text/uri-list", 0, TARGET_URI_LIST},
493 {"XdndDirectSave0", 0, TARGET_XDS},
494 {"application/octet-stream", 0, TARGET_RAW},
497 gtk_drag_dest_set(widget,
498 0, /* GTK_DEST_DEFAULT_MOTION, */
499 target_table,
500 sizeof(target_table) / sizeof(*target_table),
501 GDK_ACTION_COPY | GDK_ACTION_MOVE
502 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
504 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
505 GTK_SIGNAL_FUNC(drag_motion), filer_window);
506 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
507 GTK_SIGNAL_FUNC(drag_leave), filer_window);
508 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
509 GTK_SIGNAL_FUNC(drag_drop), filer_window);
510 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
511 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
514 /* Decide if panel drag is OK, setting drop_dest_type and drop_dest_path
515 * on the context as needed.
517 static gboolean panel_drag_ok(FilerWindow *filer_window,
518 GdkDragContext *context,
519 int item)
521 DirItem *fileitem = NULL;
522 char *old_path;
523 char *new_path;
524 char *type = NULL; /* Quiet gcc */
526 if (item >= 0)
527 fileitem = (DirItem *)
528 filer_window->collection->items[item].data;
530 if (item == -1)
531 new_path = NULL; /* Drag to panel background - disallow */
532 else if (fileitem->flags &
533 (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
535 if (provides(context, text_uri_list))
537 type = drop_dest_prog;
538 new_path = make_path(filer_window->path,
539 fileitem->leafname)->str;
541 else
542 new_path = NULL;
544 else if (fileitem->base_type == TYPE_DIRECTORY)
546 type = drop_dest_dir;
547 new_path = make_path(filer_window->path,
548 fileitem->leafname)->str;
550 else
551 new_path = NULL;
553 if (new_path && access(new_path, W_OK))
554 new_path = NULL;
556 old_path = g_dataset_get_data(context, "drop_dest_path");
557 if (old_path == new_path ||
558 (old_path && new_path && strcmp(old_path, new_path) == 0))
560 return new_path != NULL; /* Same as before */
563 if (new_path)
565 g_dataset_set_data(context, "drop_dest_type", type);
566 g_dataset_set_data_full(context, "drop_dest_path",
567 g_strdup(new_path), g_free);
569 else
571 item = -1;
572 g_dataset_set_data(context, "drop_dest_path", NULL);
575 collection_set_cursor_item(filer_window->collection, item);
577 return new_path != NULL;
580 /* Called during the drag when the mouse is in a widget registered
581 * as a drop target. Returns TRUE if we can accept the drop.
583 static gboolean drag_motion(GtkWidget *widget,
584 GdkDragContext *context,
585 gint x,
586 gint y,
587 guint time)
589 FilerWindow *filer_window;
590 int item;
592 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
593 g_return_val_if_fail(filer_window != NULL, TRUE);
595 if (gtk_drag_get_source_widget(context) == widget)
596 return FALSE; /* Not within a single widget! */
598 if (filer_window->panel == FALSE)
600 if (access(filer_window->path, W_OK))
601 return FALSE; /* We can't write here */
602 gdk_drag_status(context, context->suggested_action, time);
603 return TRUE;
606 /* OK, this is a drag to a panel.
607 * Allow drags to directories, applications and X bit files only.
609 item = collection_get_item(filer_window->collection, x, y);
611 if (panel_drag_ok(filer_window, context, item))
613 gdk_drag_status(context, context->suggested_action, time);
614 return TRUE;
617 return FALSE;
620 /* Remove panel highlights */
621 static void drag_leave(GtkWidget *widget,
622 GdkDragContext *context)
624 FilerWindow *filer_window;
626 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
627 g_return_if_fail(filer_window != NULL);
629 collection_set_cursor_item(filer_window->collection, -1);
632 /* User has tried to drop some data on us. Decide what format we would
633 * like the data in.
635 static gboolean drag_drop(GtkWidget *widget,
636 GdkDragContext *context,
637 gint x,
638 gint y,
639 guint time)
641 char *error = NULL;
642 char *leafname = NULL;
643 FilerWindow *filer_window;
644 GdkAtom target = GDK_NONE;
645 char *dest_path;
646 char *dest_type = NULL;
648 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
649 g_return_val_if_fail(filer_window != NULL, TRUE);
651 dest_path = g_dataset_get_data(context, "drop_dest_path");
652 if (dest_path == NULL)
654 if (filer_window->panel)
655 error = "Bad drop on panel";
656 else
658 dest_path = filer_window->path;
659 dest_type = drop_dest_dir;
662 else
664 dest_type = g_dataset_get_data(context, "drop_dest_type");
667 if (error)
669 /* Do nothing */
671 else if (dest_type == drop_dest_dir
672 && provides(context, XdndDirectSave0))
674 leafname = get_xds_prop(context);
675 if (leafname)
677 if (strchr(leafname, '/'))
679 error = "XDS protocol error: "
680 "leafname may not contain '/'\n";
681 g_free(leafname);
683 leafname = NULL;
685 else
687 GString *uri;
689 uri = g_string_new(NULL);
690 g_string_sprintf(uri, "file://%s%s",
691 our_host_name(),
692 make_path(dest_path,
693 leafname)->str);
694 set_xds_prop(context, uri->str);
695 g_string_free(uri, TRUE);
697 target = XdndDirectSave0;
698 g_dataset_set_data_full(context, "leafname",
699 leafname, g_free);
702 else
703 error = "XdndDirectSave0 target provided, but the atom "
704 "XdndDirectSave0 (type text/plain) did not "
705 "contain a leafname\n";
707 else if (provides(context, text_uri_list))
708 target = text_uri_list;
709 else
711 if (dest_type == drop_dest_dir)
712 error = "Sorry - I require a target type of "
713 "text/uri-list or XdndDirectSave0.";
714 else
715 error = "Sorry - I require a target type of "
716 "text/uri-list.";
719 if (error)
721 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
723 delayed_error("ROX-Filer", error);
725 else
726 gtk_drag_get_data(widget, context, target, time);
728 return TRUE;
731 /* Called when some data arrives from the remote app (which we asked for
732 * in drag_drop).
734 static void drag_data_received(GtkWidget *widget,
735 GdkDragContext *context,
736 gint x,
737 gint y,
738 GtkSelectionData *selection_data,
739 guint info,
740 guint32 time)
742 if (!selection_data->data)
744 /* Timeout? */
745 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
746 return;
749 switch (info)
751 case TARGET_XDS:
752 got_data_xds_reply(widget, context,
753 selection_data, time);
754 break;
755 case TARGET_RAW:
756 got_data_raw(widget, context, selection_data, time);
757 break;
758 case TARGET_URI_LIST:
759 got_uri_list(widget, context, selection_data, time);
760 break;
761 default:
762 gtk_drag_finish(context, FALSE, FALSE, time);
763 delayed_error("drag_data_received", "Unknown target");
764 break;
768 static void got_data_xds_reply(GtkWidget *widget,
769 GdkDragContext *context,
770 GtkSelectionData *selection_data,
771 guint32 time)
773 gboolean mark_unsafe = TRUE;
774 char response = *selection_data->data;
775 char *error = NULL;
777 if (selection_data->length != 1)
778 response = '?';
780 if (response == 'F')
782 /* Sender couldn't save there - ask for another
783 * type if possible.
785 if (provides(context, application_octet_stream))
787 mark_unsafe = FALSE; /* Wait and see */
789 gtk_drag_get_data(widget, context,
790 application_octet_stream, time);
792 else
793 error = "Remote app can't or won't send me "
794 "the data - sorry";
796 else if (response == 'S')
798 FilerWindow *filer_window;
800 /* Success - data is saved */
801 mark_unsafe = FALSE; /* It really is safe */
802 gtk_drag_finish(context, TRUE, FALSE, time);
804 filer_window = gtk_object_get_data(GTK_OBJECT(widget),
805 "filer_window");
806 g_return_if_fail(filer_window != NULL);
808 update_dir(filer_window, TRUE);
810 else if (response != 'E')
812 error = "XDS protocol error: "
813 "return code should be 'S', 'F' or 'E'\n";
815 /* else: error has been reported by the sender */
817 if (mark_unsafe)
819 set_xds_prop(context, "");
820 /* Unsave also implies that the drag failed */
821 gtk_drag_finish(context, FALSE, FALSE, time);
824 if (error)
825 delayed_error("ROX-Filer", error);
828 static void got_data_raw(GtkWidget *widget,
829 GdkDragContext *context,
830 GtkSelectionData *selection_data,
831 guint32 time)
833 FilerWindow *filer_window;
834 char *leafname;
835 int fd;
836 char *error = NULL;
837 char *dest_path;
839 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
840 g_return_if_fail(filer_window != NULL);
842 leafname = g_dataset_get_data(context, "leafname");
843 if (!leafname)
844 leafname = "UntitledData";
846 dest_path = get_dest_path(filer_window, context);
848 fd = open(make_path(dest_path, leafname)->str,
849 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
850 S_IRUSR | S_IRGRP | S_IROTH |
851 S_IWUSR | S_IWGRP | S_IWOTH);
853 if (fd == -1)
854 error = g_strerror(errno);
855 else
857 if (write(fd,
858 selection_data->data,
859 selection_data->length) == -1)
860 error = g_strerror(errno);
862 if (close(fd) == -1 && !error)
863 error = g_strerror(errno);
865 update_dir(filer_window, TRUE);
868 if (error)
870 if (provides(context, XdndDirectSave0))
871 set_xds_prop(context, "");
872 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
873 delayed_error("Error saving file", error);
875 else
876 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
879 /* Execute this program, passing all the URIs in the list as arguments.
880 * URIs that are files on the local machine will be passed as simple
881 * pathnames. The uri_list should be freed after this function returns.
883 static void run_with_files(char *path, GSList *uri_list)
885 char **argv;
886 int argc = 0;
887 struct stat info;
889 if (stat(path, &info))
891 delayed_error("ROX-Filer", "Program not found - deleted?");
892 return;
895 argv = g_malloc(sizeof(char *) * (g_slist_length(uri_list) + 2));
897 if (S_ISDIR(info.st_mode))
898 argv[argc++] = make_path(path, "AppRun")->str;
899 else
900 argv[argc++] = path;
902 while (uri_list)
904 char *uri = (char *) uri_list->data;
905 char *local;
907 local = get_local_path(uri);
908 if (local)
909 argv[argc++] = local;
910 else
911 argv[argc++] = uri;
912 uri_list = uri_list->next;
915 argv[argc++] = NULL;
917 if (!spawn_full(argv, getenv("HOME")))
918 delayed_error("ROX-Filer", "Failed to fork() child process");
921 /* We've got a list of URIs from somewhere (probably another filer window).
922 * If the files are on the local machine then try to copy them ourselves,
923 * otherwise, if there was only one file and application/octet-stream was
924 * provided, get the data via the X server.
926 static void got_uri_list(GtkWidget *widget,
927 GdkDragContext *context,
928 GtkSelectionData *selection_data,
929 guint32 time)
931 FilerWindow *filer_window;
932 GSList *uri_list;
933 char *error = NULL;
934 GSList *next_uri;
935 gboolean send_reply = TRUE;
936 char *dest_path;
937 char *type;
939 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
940 g_return_if_fail(filer_window != NULL);
942 dest_path = get_dest_path(filer_window, context);
943 type = g_dataset_get_data(context, "drop_dest_type");
945 uri_list = uri_list_to_gslist(selection_data->data);
947 if (!uri_list)
948 error = "No URIs in the text/uri-list (nothing to do!)";
949 else if (type == drop_dest_prog)
950 run_with_files(dest_path, uri_list);
951 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
953 /* There is one URI in the list, and it's not on the local
954 * machine. Get it via the X server if possible.
957 if (provides(context, application_octet_stream))
959 char *leaf;
960 leaf = strrchr(uri_list->data, '/');
961 if (leaf)
962 leaf++;
963 else
964 leaf = uri_list->data;
965 g_dataset_set_data_full(context, "leafname",
966 g_strdup(leaf), g_free);
967 gtk_drag_get_data(widget, context,
968 application_octet_stream, time);
969 send_reply = FALSE;
971 else
972 error = "Can't get data from remote machine "
973 "(application/octet-stream not provided)";
975 else
977 GSList *local_paths = NULL;
978 GSList *next;
980 /* Either one local URI, or a list. If everything in the list
981 * isn't local then we are stuck.
984 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
986 char *path;
988 path = get_local_path((char *) next_uri->data);
990 if (path)
991 local_paths = g_slist_append(local_paths,
992 g_strdup(path));
993 else
994 error = "Some of these files are on a "
995 "different machine - they will be "
996 "ignored - sorry";
999 if (!local_paths)
1001 error = "None of these files are on the local machine "
1002 "- I can't operate on multiple remote files - "
1003 "sorry.";
1005 else if (context->action == GDK_ACTION_MOVE)
1006 action_move(local_paths, dest_path);
1007 else if (context->action == GDK_ACTION_COPY)
1008 action_copy(local_paths, dest_path);
1009 else if (context->action == GDK_ACTION_LINK)
1010 action_link(local_paths, dest_path);
1011 else
1012 error = "Unknown action requested";
1014 for (next = local_paths; next; next = next->next)
1015 g_free(next->data);
1016 g_slist_free(local_paths);
1019 if (error)
1021 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
1022 delayed_error("Error getting file list", error);
1024 else if (send_reply)
1025 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
1027 next_uri = uri_list;
1028 while (next_uri)
1030 g_free(next_uri->data);
1031 next_uri = next_uri->next;
1033 g_slist_free(uri_list);