r268: Tidied up the dnd code a bit. Fixed a bug which prevented data from being
[rox-filer/ma.git] / ROX-Filer / src / dnd.c
blobe846a453848019600aeb124d8c43d23815639135
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, 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 "config.h"
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <string.h>
34 #include <X11/Xlib.h>
35 #include <X11/Xatom.h>
36 #include <gtk/gtk.h>
37 #include "collection.h"
39 #include "dnd.h"
40 #include "filer.h"
41 #include "action.h"
42 #include "pixmaps.h"
43 #include "gui_support.h"
44 #include "support.h"
45 #include "options.h"
46 #include "run.h"
48 #define MAXURILEN 4096 /* Longest URI to allow */
50 /* Static prototypes */
51 static void create_uri_list(GString *string,
52 Collection *collection,
53 FilerWindow *filer_window);
54 static gboolean drag_drop(GtkWidget *widget,
55 GdkDragContext *context,
56 gint x,
57 gint y,
58 guint time,
59 FilerWindow *filer_window);
60 static gboolean provides(GdkDragContext *context, GdkAtom target);
61 static void set_xds_prop(GdkDragContext *context, char *text);
62 static gboolean drag_motion(GtkWidget *widget,
63 GdkDragContext *context,
64 gint x,
65 gint y,
66 guint time,
67 FilerWindow *filer_window);
68 static void drag_leave(GtkWidget *widget,
69 GdkDragContext *context,
70 guint32 time,
71 FilerWindow *filer_window);
72 static void drag_data_received(GtkWidget *widget,
73 GdkDragContext *context,
74 gint x,
75 gint y,
76 GtkSelectionData *selection_data,
77 guint info,
78 guint32 time,
79 FilerWindow *filer_window);
80 static void got_data_xds_reply(GtkWidget *widget,
81 GdkDragContext *context,
82 GtkSelectionData *selection_data,
83 guint32 time,
84 FilerWindow *filer_window);
85 static void got_data_raw(GtkWidget *widget,
86 GdkDragContext *context,
87 GtkSelectionData *selection_data,
88 guint32 time);
89 static GSList *uri_list_to_gslist(char *uri_list);
90 static void got_uri_list(GtkWidget *widget,
91 GdkDragContext *context,
92 GtkSelectionData *selection_data,
93 guint32 time);
94 static GtkWidget *create_options();
95 static void update_options();
96 static void set_options();
97 static void save_options();
98 static char *load_no_hostnames(char *data);
99 static char *drag_to_icons(char *data);
100 static void drag_end(GtkWidget *widget,
101 GdkDragContext *context,
102 FilerWindow *filer_window);
104 /* Possible values for drop_dest_type (can also be NULL).
105 * In either case, drop_dest_path is the app/file/dir to use.
107 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
108 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
110 static OptionsSection options =
112 N_("Drag and Drop options"),
113 create_options,
114 update_options,
115 set_options,
116 save_options
119 enum
121 TARGET_RAW,
122 TARGET_URI_LIST,
123 TARGET_XDS,
126 GdkAtom XdndDirectSave0;
127 GdkAtom xa_text_plain;
128 GdkAtom text_uri_list;
129 GdkAtom application_octet_stream;
131 void dnd_init()
133 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
134 xa_text_plain = gdk_atom_intern("text/plain", FALSE);
135 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
136 application_octet_stream = gdk_atom_intern("application/octet-stream",
137 FALSE);
139 options_sections = g_slist_prepend(options_sections, &options);
140 option_register("dnd_no_hostnames", load_no_hostnames);
141 option_register("dnd_drag_to_icons", drag_to_icons);
144 /* OPTIONS */
146 static gboolean o_no_hostnames = FALSE;
147 static gboolean o_drag_to_icons = TRUE;
148 static GtkWidget *toggle_no_hostnames;
149 static GtkWidget *toggle_drag_to_icons;
151 /* Build up some option widgets to go in the options dialog, but don't
152 * fill them in yet.
154 static GtkWidget *create_options()
156 GtkWidget *vbox, *label;
158 vbox = gtk_vbox_new(FALSE, 0);
159 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
161 label = gtk_label_new(_("Some older applications don't support XDND "
162 "fully and may need to have this option turned on. "
163 "Use this if dragging files to an application shows "
164 "a + sign on the pointer but the drop doesn't work."));
165 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
166 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
168 toggle_no_hostnames =
169 gtk_check_button_new_with_label(_("Don't use hostnames"));
170 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
172 toggle_drag_to_icons =
173 gtk_check_button_new_with_label(_("Allow dragging to icons in "
174 "filer windows"));
175 gtk_box_pack_start(GTK_BOX(vbox), toggle_drag_to_icons, FALSE, TRUE, 0);
177 return vbox;
180 static void update_options()
182 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
183 o_no_hostnames);
184 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_drag_to_icons),
185 o_drag_to_icons);
188 static void set_options()
190 o_no_hostnames = gtk_toggle_button_get_active(
191 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
192 o_drag_to_icons = gtk_toggle_button_get_active(
193 GTK_TOGGLE_BUTTON(toggle_drag_to_icons));
196 static void save_options()
198 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
199 option_write("dnd_drag_to_icons", o_drag_to_icons ? "1" : "0");
202 static char *load_no_hostnames(char *data)
204 o_no_hostnames = atoi(data) != 0;
205 return NULL;
208 static char *drag_to_icons(char *data)
210 o_drag_to_icons = atoi(data) != 0;
211 return NULL;
214 /* SUPPORT FUNCTIONS */
216 /* Set the XdndDirectSave0 property on the source window for this context */
217 static void set_xds_prop(GdkDragContext *context, char *text)
219 gdk_property_change(context->source_window,
220 XdndDirectSave0,
221 xa_text_plain, 8,
222 GDK_PROP_MODE_REPLACE,
223 text,
224 strlen(text));
227 static char *get_xds_prop(GdkDragContext *context)
229 guchar *prop_text;
230 gint length;
232 if (gdk_property_get(context->source_window,
233 XdndDirectSave0,
234 xa_text_plain,
235 0, MAXURILEN,
236 FALSE,
237 NULL, NULL,
238 &length, &prop_text) && prop_text)
240 /* Terminate the string */
241 prop_text = g_realloc(prop_text, length + 1);
242 prop_text[length] = '\0';
243 return prop_text;
246 return NULL;
249 /* Is the sender willing to supply this target type? */
250 static gboolean provides(GdkDragContext *context, GdkAtom target)
252 GList *targets = context->targets;
254 while (targets && ((GdkAtom) targets->data != target))
255 targets = targets->next;
257 return targets != NULL;
260 /* Convert a list of URIs into a list of strings.
261 * Lines beginning with # are skipped.
262 * The text block passed in is zero terminated (after the final CRLF)
264 static GSList *uri_list_to_gslist(char *uri_list)
266 GSList *list = NULL;
268 while (*uri_list)
270 char *linebreak;
271 char *uri;
272 int length;
274 linebreak = strchr(uri_list, 13);
276 if (!linebreak || linebreak[1] != 10)
278 delayed_error("uri_list_to_gslist",
279 _("Incorrect or missing line "
280 "break in text/uri-list data"));
281 return list;
284 length = linebreak - uri_list;
286 if (length && uri_list[0] != '#')
288 uri = g_malloc(sizeof(char) * (length + 1));
289 strncpy(uri, uri_list, length);
290 uri[length] = 0;
291 list = g_slist_append(list, uri);
294 uri_list = linebreak + 2;
297 return list;
300 /* Append all the URIs in the selection to the string */
301 static void create_uri_list(GString *string,
302 Collection *collection,
303 FilerWindow *filer_window)
305 GString *leader;
306 int i, num_selected;
308 leader = g_string_new("file://");
309 if (!o_no_hostnames)
310 g_string_append(leader, our_host_name());
311 g_string_append(leader, filer_window->path);
312 if (leader->str[leader->len - 1] != '/')
313 g_string_append_c(leader, '/');
315 num_selected = collection->number_selected;
317 for (i = 0; num_selected > 0; i++)
319 if (collection->items[i].selected)
321 DirItem *item = (DirItem *) collection->items[i].data;
323 g_string_append(string, leader->str);
324 g_string_append(string, item->leafname);
325 g_string_append(string, "\r\n");
326 num_selected--;
330 g_string_free(leader, TRUE);
333 /* DRAGGING FROM US */
335 /* The user has held the mouse button down over an item and moved -
336 * start a drag.
338 * We always provide text/uri-list. If we are dragging a single, regular file
339 * then we also offer application/octet-stream and the type of the file.
341 void drag_selection(Collection *collection,
342 GdkEventMotion *event,
343 gint number_selected,
344 gpointer user_data)
346 GtkWidget *widget;
347 MaskedPixmap *image;
348 GdkDragContext *context;
349 GtkTargetList *target_list;
350 GtkTargetEntry target_table[] =
352 {"text/uri-list", 0, TARGET_URI_LIST},
353 {"application/octet-stream", 0, TARGET_RAW},
354 {"", 0, TARGET_RAW},
356 DirItem *item;
357 GdkDragAction actions;
359 if (number_selected == 1)
360 item = selected_item(collection);
361 else
362 item = NULL;
364 widget = GTK_WIDGET(collection);
366 if (item && item->mime_type)
368 MIME_type *t = item->mime_type;
370 target_table[2].target = g_strconcat(t->media_type, "/",
371 t->subtype, NULL);
372 target_list = gtk_target_list_new(target_table, 3);
373 g_free(target_table[2].target);
375 else
376 target_list = gtk_target_list_new(target_table, 1);
378 if (event->state & GDK_BUTTON1_MASK)
379 actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK;
380 else
381 actions = GDK_ACTION_MOVE;
383 context = gtk_drag_begin(widget,
384 target_list,
385 actions,
386 (event->state & GDK_BUTTON1_MASK) ? 1 :
387 (event->state & GDK_BUTTON2_MASK) ? 2 : 3,
388 (GdkEvent *) event);
390 image = item ? item->image : default_pixmap[TYPE_MULTIPLE];
392 gtk_drag_set_icon_pixmap(context,
393 gtk_widget_get_colormap(widget),
394 image->pixmap,
395 image->mask,
396 0, 0);
399 static void drag_end(GtkWidget *widget,
400 GdkDragContext *context,
401 FilerWindow *filer_window)
403 collection_clear_selection(filer_window->collection);
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,
415 FilerWindow *filer_window)
417 char *to_send = "E"; /* Default to sending an error */
418 long to_send_length = 1;
419 gboolean delete_once_sent = FALSE;
420 GdkAtom type = XA_STRING;
421 GString *string;
422 DirItem *item;
424 switch (info)
426 case TARGET_RAW:
427 item = selected_item(filer_window->collection);
428 if (item && load_file(make_path(filer_window->path,
429 item->leafname)->str,
430 &to_send, &to_send_length))
432 delete_once_sent = TRUE;
433 type = selection_data->target;
434 break;
436 g_warning("drag_data_get: Can't find selected item\n");
437 return;
438 case TARGET_URI_LIST:
439 string = g_string_new(NULL);
440 create_uri_list(string,
441 COLLECTION(widget),
442 filer_window);
443 to_send = string->str;
444 to_send_length = string->len;
445 delete_once_sent = TRUE;
446 g_string_free(string, FALSE);
447 break;
448 default:
449 delayed_error("drag_data_get",
450 _("Internal error - bad info type"));
451 break;
454 gtk_selection_data_set(selection_data,
455 type,
457 to_send,
458 to_send_length);
460 if (delete_once_sent)
461 g_free(to_send);
464 /* DRAGGING TO US */
466 /* Set up this filer window as a drop target. Called once, when the
467 * filer window is first created.
469 void drag_set_dest(FilerWindow *filer_window)
471 GtkWidget *widget = GTK_WIDGET(filer_window->collection);
472 GtkTargetEntry target_table[] =
474 {"text/uri-list", 0, TARGET_URI_LIST},
475 {"XdndDirectSave0", 0, TARGET_XDS},
476 {"application/octet-stream", 0, TARGET_RAW},
479 gtk_drag_dest_set(widget,
480 0, /* GTK_DEST_DEFAULT_MOTION, */
481 target_table,
482 sizeof(target_table) / sizeof(*target_table),
483 GDK_ACTION_COPY | GDK_ACTION_MOVE
484 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
486 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
487 GTK_SIGNAL_FUNC(drag_motion), filer_window);
488 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
489 GTK_SIGNAL_FUNC(drag_leave), filer_window);
490 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
491 GTK_SIGNAL_FUNC(drag_drop), filer_window);
492 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
493 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
495 gtk_signal_connect(GTK_OBJECT(widget), "drag_end",
496 GTK_SIGNAL_FUNC(drag_end), filer_window);
499 /* Called during the drag when the mouse is in a widget registered
500 * as a drop target. Returns TRUE if we can accept the drop.
502 static gboolean drag_motion(GtkWidget *widget,
503 GdkDragContext *context,
504 gint x,
505 gint y,
506 guint time,
507 FilerWindow *filer_window)
509 DirItem *item;
510 int item_number;
511 GdkDragAction action = context->suggested_action;
512 char *new_path = NULL;
513 char *type = NULL;
515 if (o_drag_to_icons || filer_window->panel_type != PANEL_NO)
516 item_number = collection_get_item(filer_window->collection,
517 x, y);
518 else
519 item_number = -1;
521 item = item_number >= 0
522 ? (DirItem *) filer_window->collection->items[item_number].data
523 : NULL;
525 if (item)
527 /* If we didn't drop onto a directory, application or
528 * executable file then act as though the drop is to the
529 * window background.
531 if (item->base_type != TYPE_DIRECTORY
532 && !(item->flags & ITEM_FLAG_EXEC_FILE))
533 item = NULL;
536 if (!item)
538 /* Drop onto the window background */
539 collection_set_cursor_item(filer_window->collection,
540 -1);
542 if (gtk_drag_get_source_widget(context) == widget)
543 goto out;
545 if (access(filer_window->path, W_OK) != 0)
546 goto out; /* No write permission */
548 if (filer_window->panel_type != PANEL_NO)
550 if (context->actions & GDK_ACTION_LINK)
552 action = GDK_ACTION_LINK;
553 type = drop_dest_dir;
556 else
557 type = drop_dest_dir;
559 if (type)
560 new_path = g_strdup(filer_window->path);
562 else
564 /* Drop onto a program/directory of some sort */
566 if (gtk_drag_get_source_widget(context) == widget)
568 Collection *collection = filer_window->collection;
570 if (collection->items[item_number].selected)
571 goto out;
574 if (item->base_type == TYPE_DIRECTORY &&
575 !(item->flags & ITEM_FLAG_APPDIR))
577 if (provides(context, text_uri_list) ||
578 provides(context, XdndDirectSave0))
579 type = drop_dest_dir;
581 else
583 if (provides(context, text_uri_list) ||
584 provides(context, application_octet_stream))
585 type = drop_dest_prog;
588 if (type)
590 new_path = make_path(filer_window->path,
591 item->leafname)->str;
592 collection_set_cursor_item(filer_window->collection,
593 item_number);
597 out:
598 g_dataset_set_data(context, "drop_dest_type", type);
599 if (type)
601 gdk_drag_status(context, action, time);
602 g_dataset_set_data_full(context, "drop_dest_path",
603 g_strdup(new_path), g_free);
605 else
606 g_free(new_path);
608 return type != NULL;
611 /* Remove panel highlights */
612 static void drag_leave(GtkWidget *widget,
613 GdkDragContext *context,
614 guint32 time,
615 FilerWindow *filer_window)
617 collection_set_cursor_item(filer_window->collection, -1);
620 /* User has tried to drop some data on us. Decide what format we would
621 * like the data in.
623 static gboolean drag_drop(GtkWidget *widget,
624 GdkDragContext *context,
625 gint x,
626 gint y,
627 guint time,
628 FilerWindow *filer_window)
630 char *error = NULL;
631 char *leafname = NULL;
632 GdkAtom target = GDK_NONE;
633 char *dest_path;
634 char *dest_type = NULL;
636 g_print("[ filer_window = %s ]\n", filer_window->path);
638 dest_path = g_dataset_get_data(context, "drop_dest_path");
639 dest_type = g_dataset_get_data(context, "drop_dest_type");
641 g_return_val_if_fail(dest_path != NULL, TRUE);
643 if (dest_type == drop_dest_dir && provides(context, XdndDirectSave0))
645 leafname = get_xds_prop(context);
646 if (leafname)
648 if (strchr(leafname, '/'))
650 error = _("XDS protocol error: "
651 "leafname may not contain '/'\n");
652 g_free(leafname);
654 leafname = NULL;
656 else
658 GString *uri;
660 uri = g_string_new(NULL);
661 g_string_sprintf(uri, "file://%s%s",
662 our_host_name(),
663 make_path(dest_path,
664 leafname)->str);
665 set_xds_prop(context, uri->str);
666 g_string_free(uri, TRUE);
668 target = XdndDirectSave0;
669 g_dataset_set_data_full(context, "leafname",
670 leafname, g_free);
673 else
674 error = _(
675 "XdndDirectSave0 target provided, but the atom "
676 "XdndDirectSave0 (type text/plain) did not "
677 "contain a leafname\n");
679 else if (provides(context, text_uri_list))
680 target = text_uri_list;
681 else if (provides(context, application_octet_stream))
682 target = application_octet_stream;
683 else
685 if (dest_type == drop_dest_dir)
686 error = _("Sorry - I require a target type of "
687 "text/uri-list or XdndDirectSave0.");
688 else
689 error = _("Sorry - I require a target type of "
690 "text/uri-list or application/octet-stream.");
693 if (error)
695 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
697 delayed_error(PROJECT, error);
699 else
700 gtk_drag_get_data(widget, context, target, time);
702 return TRUE;
705 /* Called when some data arrives from the remote app (which we asked for
706 * in drag_drop).
708 static void drag_data_received(GtkWidget *widget,
709 GdkDragContext *context,
710 gint x,
711 gint y,
712 GtkSelectionData *selection_data,
713 guint info,
714 guint32 time,
715 FilerWindow *filer_window)
717 if (!selection_data->data)
719 /* Timeout? */
720 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
721 return;
724 switch (info)
726 case TARGET_XDS:
727 got_data_xds_reply(widget, context,
728 selection_data, time,
729 filer_window);
730 break;
731 case TARGET_RAW:
732 got_data_raw(widget, context, selection_data, time);
733 break;
734 case TARGET_URI_LIST:
735 got_uri_list(widget, context, selection_data, time);
736 break;
737 default:
738 gtk_drag_finish(context, FALSE, FALSE, time);
739 delayed_error("drag_data_received",
740 _("Unknown target"));
741 break;
745 static void got_data_xds_reply(GtkWidget *widget,
746 GdkDragContext *context,
747 GtkSelectionData *selection_data,
748 guint32 time,
749 FilerWindow *filer_window)
751 gboolean mark_unsafe = TRUE;
752 char response = *selection_data->data;
753 char *error = NULL;
754 char *dest_path;
756 dest_path = g_dataset_get_data(context, "drop_dest_path");
758 if (selection_data->length != 1)
759 response = '?';
761 if (response == 'F')
763 /* Sender couldn't save there - ask for another
764 * type if possible.
766 if (provides(context, application_octet_stream))
768 mark_unsafe = FALSE; /* Wait and see */
770 gtk_drag_get_data(widget, context,
771 application_octet_stream, time);
773 else
774 error = _("Remote app can't or won't send me "
775 "the data - sorry");
777 else if (response == 'S')
779 /* Success - data is saved */
780 mark_unsafe = FALSE; /* It really is safe */
781 gtk_drag_finish(context, TRUE, FALSE, time);
783 refresh_dirs(dest_path);
785 else if (response != 'E')
787 error = _("XDS protocol error: "
788 "return code should be 'S', 'F' or 'E'\n");
790 /* else: error has been reported by the sender */
792 if (mark_unsafe)
794 set_xds_prop(context, "");
795 /* Unsave also implies that the drag failed */
796 gtk_drag_finish(context, FALSE, FALSE, time);
799 if (error)
800 delayed_error(PROJECT, error);
803 static void got_data_raw(GtkWidget *widget,
804 GdkDragContext *context,
805 GtkSelectionData *selection_data,
806 guint32 time)
808 char *leafname;
809 int fd;
810 char *error = NULL;
811 char *dest_path;
813 g_return_if_fail(selection_data->data != NULL);
815 dest_path = g_dataset_get_data(context, "drop_dest_path");
817 if (g_dataset_get_data(context, "drop_dest_type") == drop_dest_prog)
819 /* The data needs to be sent to an application */
820 run_with_data(dest_path,
821 selection_data->data, selection_data->length);
822 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
823 return;
826 leafname = g_dataset_get_data(context, "leafname");
827 if (!leafname)
828 leafname = _("UntitledData");
830 fd = open(make_path(dest_path, leafname)->str,
831 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
832 S_IRUSR | S_IRGRP | S_IROTH |
833 S_IWUSR | S_IWGRP | S_IWOTH);
835 if (fd == -1)
836 error = g_strerror(errno);
837 else
839 if (write(fd,
840 selection_data->data,
841 selection_data->length) == -1)
842 error = g_strerror(errno);
844 if (close(fd) == -1 && !error)
845 error = g_strerror(errno);
847 refresh_dirs(dest_path);
850 if (error)
852 if (provides(context, XdndDirectSave0))
853 set_xds_prop(context, "");
854 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
855 delayed_error(_("Error saving file"), error);
857 else
858 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
861 /* We've got a list of URIs from somewhere (probably another filer window).
862 * If the files are on the local machine then try to copy them ourselves,
863 * otherwise, if there was only one file and application/octet-stream was
864 * provided, get the data via the X server.
866 static void got_uri_list(GtkWidget *widget,
867 GdkDragContext *context,
868 GtkSelectionData *selection_data,
869 guint32 time)
871 GSList *uri_list;
872 char *error = NULL;
873 GSList *next_uri;
874 gboolean send_reply = TRUE;
875 char *dest_path;
876 char *type;
878 dest_path = g_dataset_get_data(context, "drop_dest_path");
879 type = g_dataset_get_data(context, "drop_dest_type");
881 g_return_if_fail(dest_path != NULL);
883 uri_list = uri_list_to_gslist(selection_data->data);
885 if (!uri_list)
886 error = _("No URIs in the text/uri-list (nothing to do!)");
887 else if (type == drop_dest_prog)
888 run_with_files(dest_path, uri_list);
889 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
891 /* There is one URI in the list, and it's not on the local
892 * machine. Get it via the X server if possible.
895 if (provides(context, application_octet_stream))
897 char *leaf;
898 leaf = strrchr(uri_list->data, '/');
899 if (leaf)
900 leaf++;
901 else
902 leaf = uri_list->data;
903 g_dataset_set_data_full(context, "leafname",
904 g_strdup(leaf), g_free);
905 gtk_drag_get_data(widget, context,
906 application_octet_stream, time);
907 send_reply = FALSE;
909 else
910 error = _("Can't get data from remote machine "
911 "(application/octet-stream not provided)");
913 else
915 GSList *local_paths = NULL;
916 GSList *next;
918 /* Either one local URI, or a list. If everything in the list
919 * isn't local then we are stuck.
922 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
924 char *path;
926 path = get_local_path((char *) next_uri->data);
928 if (path)
929 local_paths = g_slist_append(local_paths,
930 g_strdup(path));
931 else
932 error = _("Some of these files are on a "
933 "different machine - they will be "
934 "ignored - sorry");
937 if (!local_paths)
939 error = _("None of these files are on the local "
940 "machine - I can't operate on multiple "
941 "remote files - sorry.");
943 else if (context->action == GDK_ACTION_MOVE)
944 action_move(local_paths, dest_path);
945 else if (context->action == GDK_ACTION_COPY)
946 action_copy(local_paths, dest_path, NULL);
947 else if (context->action == GDK_ACTION_LINK)
948 action_link(local_paths, dest_path);
949 else
950 error = _("Unknown action requested");
952 for (next = local_paths; next; next = next->next)
953 g_free(next->data);
954 g_slist_free(local_paths);
957 if (error)
959 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
960 delayed_error(_("Error getting file list"), error);
962 else if (send_reply)
963 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
965 next_uri = uri_list;
966 while (next_uri)
968 g_free(next_uri->data);
969 next_uri = next_uri->next;
971 g_slist_free(uri_list);