r262: Added a check in the dnd code that the requested type is valid
[rox-filer.git] / ROX-Filer / src / dnd.c
blob8a6418ed220cd90acadc42e5a78e9db9b0aef05a
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 static gboolean provides(GdkDragContext *context, GdkAtom target);
60 static void set_xds_prop(GdkDragContext *context, char *text);
61 static gboolean drag_motion(GtkWidget *widget,
62 GdkDragContext *context,
63 gint x,
64 gint y,
65 guint time);
66 static void drag_leave(GtkWidget *widget,
67 GdkDragContext *context);
68 static void drag_data_received(GtkWidget *widget,
69 GdkDragContext *context,
70 gint x,
71 gint y,
72 GtkSelectionData *selection_data,
73 guint info,
74 guint32 time);
75 static void got_data_xds_reply(GtkWidget *widget,
76 GdkDragContext *context,
77 GtkSelectionData *selection_data,
78 guint32 time);
79 static void got_data_raw(GtkWidget *widget,
80 GdkDragContext *context,
81 GtkSelectionData *selection_data,
82 guint32 time);
83 static GSList *uri_list_to_gslist(char *uri_list);
84 static void got_uri_list(GtkWidget *widget,
85 GdkDragContext *context,
86 GtkSelectionData *selection_data,
87 guint32 time);
88 static GtkWidget *create_options();
89 static void update_options();
90 static void set_options();
91 static void save_options();
92 static char *load_no_hostnames(char *data);
93 static char *drag_to_icons(char *data);
95 /* Possible values for drop_dest_type (can also be NULL).
96 * In either case, drop_dest_path is the app/file/dir to use.
98 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
99 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
101 static OptionsSection options =
103 N_("Drag and Drop options"),
104 create_options,
105 update_options,
106 set_options,
107 save_options
110 enum
112 TARGET_RAW,
113 TARGET_URI_LIST,
114 TARGET_XDS,
117 GdkAtom XdndDirectSave0;
118 GdkAtom xa_text_plain;
119 GdkAtom text_uri_list;
120 GdkAtom application_octet_stream;
122 void dnd_init()
124 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
125 xa_text_plain = gdk_atom_intern("text/plain", FALSE);
126 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
127 application_octet_stream = gdk_atom_intern("application/octet-stream",
128 FALSE);
130 options_sections = g_slist_prepend(options_sections, &options);
131 option_register("dnd_no_hostnames", load_no_hostnames);
132 option_register("dnd_drag_to_icons", drag_to_icons);
135 /* OPTIONS */
137 static gboolean o_no_hostnames = FALSE;
138 static gboolean o_drag_to_icons = TRUE;
139 static GtkWidget *toggle_no_hostnames;
140 static GtkWidget *toggle_drag_to_icons;
142 /* Build up some option widgets to go in the options dialog, but don't
143 * fill them in yet.
145 static GtkWidget *create_options()
147 GtkWidget *vbox, *label;
149 vbox = gtk_vbox_new(FALSE, 0);
150 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
152 label = gtk_label_new(_("Some older applications don't support XDND "
153 "fully and may need to have this option turned on. "
154 "Use this if dragging files to an application shows "
155 "a + sign on the pointer but the drop doesn't work."));
156 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
157 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
159 toggle_no_hostnames =
160 gtk_check_button_new_with_label(_("Don't use hostnames"));
161 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
163 toggle_drag_to_icons =
164 gtk_check_button_new_with_label(_("Allow dragging to icons in "
165 "filer windows"));
166 gtk_box_pack_start(GTK_BOX(vbox), toggle_drag_to_icons, FALSE, TRUE, 0);
168 return vbox;
171 static void update_options()
173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
174 o_no_hostnames);
175 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_drag_to_icons),
176 o_drag_to_icons);
179 static void set_options()
181 o_no_hostnames = gtk_toggle_button_get_active(
182 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
183 o_drag_to_icons = gtk_toggle_button_get_active(
184 GTK_TOGGLE_BUTTON(toggle_drag_to_icons));
187 static void save_options()
189 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
190 option_write("dnd_drag_to_icons", o_drag_to_icons ? "1" : "0");
193 static char *load_no_hostnames(char *data)
195 o_no_hostnames = atoi(data) != 0;
196 return NULL;
199 static char *drag_to_icons(char *data)
201 o_drag_to_icons = atoi(data) != 0;
202 return NULL;
205 /* SUPPORT FUNCTIONS */
207 /* Set the XdndDirectSave0 property on the source window for this context */
208 static void set_xds_prop(GdkDragContext *context, char *text)
210 gdk_property_change(context->source_window,
211 XdndDirectSave0,
212 xa_text_plain, 8,
213 GDK_PROP_MODE_REPLACE,
214 text,
215 strlen(text));
218 static char *get_xds_prop(GdkDragContext *context)
220 guchar *prop_text;
221 gint length;
223 if (gdk_property_get(context->source_window,
224 XdndDirectSave0,
225 xa_text_plain,
226 0, MAXURILEN,
227 FALSE,
228 NULL, NULL,
229 &length, &prop_text) && prop_text)
231 /* Terminate the string */
232 prop_text = g_realloc(prop_text, length + 1);
233 prop_text[length] = '\0';
234 return prop_text;
237 return NULL;
240 /* Is the sender willing to supply this target type? */
241 static gboolean provides(GdkDragContext *context, GdkAtom target)
243 GList *targets = context->targets;
245 while (targets && ((GdkAtom) targets->data != target))
246 targets = targets->next;
248 return targets != NULL;
251 /* Convert a list of URIs into a list of strings.
252 * Lines beginning with # are skipped.
253 * The text block passed in is zero terminated (after the final CRLF)
255 static GSList *uri_list_to_gslist(char *uri_list)
257 GSList *list = NULL;
259 while (*uri_list)
261 char *linebreak;
262 char *uri;
263 int length;
265 linebreak = strchr(uri_list, 13);
267 if (!linebreak || linebreak[1] != 10)
269 delayed_error("uri_list_to_gslist",
270 _("Incorrect or missing line "
271 "break in text/uri-list data"));
272 return list;
275 length = linebreak - uri_list;
277 if (length && uri_list[0] != '#')
279 uri = g_malloc(sizeof(char) * (length + 1));
280 strncpy(uri, uri_list, length);
281 uri[length] = 0;
282 list = g_slist_append(list, uri);
285 uri_list = linebreak + 2;
288 return list;
291 /* Append all the URIs in the selection to the string */
292 static void create_uri_list(GString *string,
293 Collection *collection,
294 FilerWindow *filer_window)
296 GString *leader;
297 int i, num_selected;
299 leader = g_string_new("file://");
300 if (!o_no_hostnames)
301 g_string_append(leader, our_host_name());
302 g_string_append(leader, filer_window->path);
303 if (leader->str[leader->len - 1] != '/')
304 g_string_append_c(leader, '/');
306 num_selected = collection->number_selected;
308 for (i = 0; num_selected > 0; i++)
310 if (collection->items[i].selected)
312 DirItem *item = (DirItem *) collection->items[i].data;
314 g_string_append(string, leader->str);
315 g_string_append(string, item->leafname);
316 g_string_append(string, "\r\n");
317 num_selected--;
321 g_string_free(leader, TRUE);
324 /* DRAGGING FROM US */
326 /* The user has held the mouse button down over an item and moved -
327 * start a drag.
329 * We always provide text/uri-list. If we are dragging a single, regular file
330 * then we also offer application/octet-stream and the type of the file.
332 void drag_selection(Collection *collection,
333 GdkEventMotion *event,
334 gint number_selected,
335 gpointer user_data)
337 FilerWindow *filer_window = (FilerWindow *) user_data;
338 GtkWidget *widget;
339 MaskedPixmap *image;
340 GdkDragContext *context;
341 GtkTargetList *target_list;
342 GtkTargetEntry target_table[] =
344 {"text/uri-list", 0, TARGET_URI_LIST},
345 {"application/octet-stream", 0, TARGET_RAW},
346 {"", 0, TARGET_RAW},
348 DirItem *item;
349 GdkDragAction actions;
351 if (number_selected == 1)
352 item = selected_item(collection);
353 else
354 item = NULL;
356 widget = GTK_WIDGET(collection);
358 if (item && item->mime_type)
360 MIME_type *t = item->mime_type;
362 target_table[2].target = g_strconcat(t->media_type, "/",
363 t->subtype, NULL);
364 target_list = gtk_target_list_new(target_table, 3);
365 g_free(target_table[2].target);
367 else
368 target_list = gtk_target_list_new(target_table, 1);
370 if (event->state & GDK_BUTTON1_MASK)
371 actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK;
372 else
373 actions = GDK_ACTION_MOVE;
375 context = gtk_drag_begin(widget,
376 target_list,
377 actions,
378 (event->state & GDK_BUTTON1_MASK) ? 1 :
379 (event->state & GDK_BUTTON2_MASK) ? 2 : 3,
380 (GdkEvent *) event);
381 g_dataset_set_data(context, "filer_window", filer_window);
383 image = item ? item->image : default_pixmap[TYPE_MULTIPLE];
385 gtk_drag_set_icon_pixmap(context,
386 gtk_widget_get_colormap(widget),
387 image->pixmap,
388 image->mask,
389 0, 0);
392 /* Called when a remote app wants us to send it some data.
393 * TODO: Maybe we should handle errors better (ie, let the remote app know
394 * the drag has failed)?
396 void drag_data_get(GtkWidget *widget,
397 GdkDragContext *context,
398 GtkSelectionData *selection_data,
399 guint info,
400 guint32 time)
402 char *to_send = "E"; /* Default to sending an error */
403 long to_send_length = 1;
404 gboolean delete_once_sent = FALSE;
405 GdkAtom type = XA_STRING;
406 GString *string;
407 FilerWindow *filer_window;
408 DirItem *item;
410 filer_window = g_dataset_get_data(context, "filer_window");
411 g_return_if_fail(filer_window != NULL);
413 switch (info)
415 case TARGET_RAW:
416 item = selected_item(filer_window->collection);
417 if (item && load_file(make_path(filer_window->path,
418 item->leafname)->str,
419 &to_send, &to_send_length))
421 delete_once_sent = TRUE;
422 type = selection_data->type;
423 if (gdk_atom_name(type))
424 break; /* OK */
425 delayed_error(PROJECT, _("Invalid type "
426 "requested (not a valid atom)"));
427 return;
429 g_warning("drag_data_get: Can't find selected item\n");
430 return;
431 case TARGET_URI_LIST:
432 string = g_string_new(NULL);
433 create_uri_list(string,
434 COLLECTION(widget),
435 filer_window);
436 to_send = string->str;
437 to_send_length = string->len;
438 delete_once_sent = TRUE;
439 g_string_free(string, FALSE);
440 break;
441 default:
442 delayed_error("drag_data_get",
443 _("Internal error - bad info type"));
444 break;
447 gtk_selection_data_set(selection_data,
448 type,
450 to_send,
451 to_send_length);
453 if (delete_once_sent)
454 g_free(to_send);
456 collection_clear_selection(filer_window->collection);
459 /* DRAGGING TO US */
461 /* Set up this filer window as a drop target. Called once, when the
462 * filer window is first created.
464 void drag_set_dest(FilerWindow *filer_window)
466 GtkWidget *widget = GTK_WIDGET(filer_window->collection);
467 GtkTargetEntry target_table[] =
469 {"text/uri-list", 0, TARGET_URI_LIST},
470 {"XdndDirectSave0", 0, TARGET_XDS},
471 {"application/octet-stream", 0, TARGET_RAW},
474 gtk_drag_dest_set(widget,
475 0, /* GTK_DEST_DEFAULT_MOTION, */
476 target_table,
477 sizeof(target_table) / sizeof(*target_table),
478 GDK_ACTION_COPY | GDK_ACTION_MOVE
479 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
481 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
482 GTK_SIGNAL_FUNC(drag_motion), filer_window);
483 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
484 GTK_SIGNAL_FUNC(drag_leave), filer_window);
485 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
486 GTK_SIGNAL_FUNC(drag_drop), filer_window);
487 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
488 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
491 /* Called during the drag when the mouse is in a widget registered
492 * as a drop target. Returns TRUE if we can accept the drop.
494 static gboolean drag_motion(GtkWidget *widget,
495 GdkDragContext *context,
496 gint x,
497 gint y,
498 guint time)
500 FilerWindow *filer_window;
501 DirItem *item;
502 int item_number;
503 GdkDragAction action = context->suggested_action;
504 char *new_path = NULL;
505 char *type = NULL;
507 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
508 g_return_val_if_fail(filer_window != NULL, TRUE);
510 if (o_drag_to_icons || filer_window->panel_type != PANEL_NO)
511 item_number = collection_get_item(filer_window->collection,
512 x, y);
513 else
514 item_number = -1;
516 item = item_number >= 0
517 ? (DirItem *) filer_window->collection->items[item_number].data
518 : NULL;
520 if (item)
522 /* If we didn't drop onto a directory, application or
523 * executable file then act as though the drop is to the
524 * window background.
526 if (item->base_type != TYPE_DIRECTORY
527 && !(item->flags & ITEM_FLAG_EXEC_FILE))
528 item = NULL;
531 if (!item)
533 /* Drop onto the window background */
534 collection_set_cursor_item(filer_window->collection,
535 -1);
537 if (gtk_drag_get_source_widget(context) == widget)
538 goto out;
540 if (access(filer_window->path, W_OK) != 0)
541 goto out; /* No write permission */
543 if (filer_window->panel_type != PANEL_NO)
545 if (context->actions & GDK_ACTION_LINK)
547 action = GDK_ACTION_LINK;
548 type = drop_dest_dir;
551 else
552 type = drop_dest_dir;
554 if (type)
555 new_path = g_strdup(filer_window->path);
557 else
559 /* Drop onto a program/directory of some sort */
561 if (gtk_drag_get_source_widget(context) == widget)
563 Collection *collection = filer_window->collection;
565 if (collection->items[item_number].selected)
566 goto out;
569 if (item->base_type == TYPE_DIRECTORY &&
570 !(item->flags & ITEM_FLAG_APPDIR))
572 if (provides(context, text_uri_list) ||
573 provides(context, XdndDirectSave0))
574 type = drop_dest_dir;
576 else
578 if (provides(context, text_uri_list) ||
579 provides(context, application_octet_stream))
580 type = drop_dest_prog;
583 if (type)
585 new_path = make_path(filer_window->path,
586 item->leafname)->str;
587 collection_set_cursor_item(filer_window->collection,
588 item_number);
592 out:
593 g_dataset_set_data(context, "drop_dest_type", type);
594 if (type)
596 gdk_drag_status(context, action, time);
597 g_dataset_set_data_full(context, "drop_dest_path",
598 g_strdup(new_path), g_free);
600 else
601 g_free(new_path);
603 return type != NULL;
606 /* Remove panel highlights */
607 static void drag_leave(GtkWidget *widget,
608 GdkDragContext *context)
610 FilerWindow *filer_window;
612 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
613 g_return_if_fail(filer_window != NULL);
615 collection_set_cursor_item(filer_window->collection, -1);
618 /* User has tried to drop some data on us. Decide what format we would
619 * like the data in.
621 static gboolean drag_drop(GtkWidget *widget,
622 GdkDragContext *context,
623 gint x,
624 gint y,
625 guint time)
627 char *error = NULL;
628 char *leafname = NULL;
629 FilerWindow *filer_window;
630 GdkAtom target = GDK_NONE;
631 char *dest_path;
632 char *dest_type = NULL;
634 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
635 g_return_val_if_fail(filer_window != NULL, TRUE);
637 dest_path = g_dataset_get_data(context, "drop_dest_path");
638 dest_type = g_dataset_get_data(context, "drop_dest_type");
640 g_return_val_if_fail(dest_path != NULL, TRUE);
642 if (dest_type == drop_dest_dir && provides(context, XdndDirectSave0))
644 leafname = get_xds_prop(context);
645 if (leafname)
647 if (strchr(leafname, '/'))
649 error = _("XDS protocol error: "
650 "leafname may not contain '/'\n");
651 g_free(leafname);
653 leafname = NULL;
655 else
657 GString *uri;
659 uri = g_string_new(NULL);
660 g_string_sprintf(uri, "file://%s%s",
661 our_host_name(),
662 make_path(dest_path,
663 leafname)->str);
664 set_xds_prop(context, uri->str);
665 g_string_free(uri, TRUE);
667 target = XdndDirectSave0;
668 g_dataset_set_data_full(context, "leafname",
669 leafname, g_free);
672 else
673 error = _(
674 "XdndDirectSave0 target provided, but the atom "
675 "XdndDirectSave0 (type text/plain) did not "
676 "contain a leafname\n");
678 else if (provides(context, text_uri_list))
679 target = text_uri_list;
680 else if (provides(context, application_octet_stream))
681 target = application_octet_stream;
682 else
684 if (dest_type == drop_dest_dir)
685 error = _("Sorry - I require a target type of "
686 "text/uri-list or XdndDirectSave0.");
687 else
688 error = _("Sorry - I require a target type of "
689 "text/uri-list or application/octet-stream.");
692 if (error)
694 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
696 delayed_error(PROJECT, error);
698 else
699 gtk_drag_get_data(widget, context, target, time);
701 return TRUE;
704 /* Called when some data arrives from the remote app (which we asked for
705 * in drag_drop).
707 static void drag_data_received(GtkWidget *widget,
708 GdkDragContext *context,
709 gint x,
710 gint y,
711 GtkSelectionData *selection_data,
712 guint info,
713 guint32 time)
715 if (!selection_data->data)
717 /* Timeout? */
718 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
719 return;
722 switch (info)
724 case TARGET_XDS:
725 got_data_xds_reply(widget, context,
726 selection_data, time);
727 break;
728 case TARGET_RAW:
729 got_data_raw(widget, context, selection_data, time);
730 break;
731 case TARGET_URI_LIST:
732 got_uri_list(widget, context, selection_data, time);
733 break;
734 default:
735 gtk_drag_finish(context, FALSE, FALSE, time);
736 delayed_error("drag_data_received",
737 _("Unknown target"));
738 break;
742 static void got_data_xds_reply(GtkWidget *widget,
743 GdkDragContext *context,
744 GtkSelectionData *selection_data,
745 guint32 time)
747 gboolean mark_unsafe = TRUE;
748 char response = *selection_data->data;
749 char *error = NULL;
750 char *dest_path;
752 dest_path = g_dataset_get_data(context, "drop_dest_path");
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 refresh_dirs(dest_path);
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(PROJECT, error);
805 static void got_data_raw(GtkWidget *widget,
806 GdkDragContext *context,
807 GtkSelectionData *selection_data,
808 guint32 time)
810 char *leafname;
811 int fd;
812 char *error = NULL;
813 char *dest_path;
815 g_return_if_fail(selection_data->data != NULL);
817 dest_path = g_dataset_get_data(context, "drop_dest_path");
819 if (g_dataset_get_data(context, "drop_dest_type") == drop_dest_prog)
821 /* The data needs to be sent to an application */
822 run_with_data(dest_path,
823 selection_data->data, selection_data->length);
824 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
825 return;
828 leafname = g_dataset_get_data(context, "leafname");
829 if (!leafname)
830 leafname = _("UntitledData");
832 fd = open(make_path(dest_path, leafname)->str,
833 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
834 S_IRUSR | S_IRGRP | S_IROTH |
835 S_IWUSR | S_IWGRP | S_IWOTH);
837 if (fd == -1)
838 error = g_strerror(errno);
839 else
841 if (write(fd,
842 selection_data->data,
843 selection_data->length) == -1)
844 error = g_strerror(errno);
846 if (close(fd) == -1 && !error)
847 error = g_strerror(errno);
849 refresh_dirs(dest_path);
852 if (error)
854 if (provides(context, XdndDirectSave0))
855 set_xds_prop(context, "");
856 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
857 delayed_error(_("Error saving file"), error);
859 else
860 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
863 /* We've got a list of URIs from somewhere (probably another filer window).
864 * If the files are on the local machine then try to copy them ourselves,
865 * otherwise, if there was only one file and application/octet-stream was
866 * provided, get the data via the X server.
868 static void got_uri_list(GtkWidget *widget,
869 GdkDragContext *context,
870 GtkSelectionData *selection_data,
871 guint32 time)
873 GSList *uri_list;
874 char *error = NULL;
875 GSList *next_uri;
876 gboolean send_reply = TRUE;
877 char *dest_path;
878 char *type;
880 dest_path = g_dataset_get_data(context, "drop_dest_path");
881 type = g_dataset_get_data(context, "drop_dest_type");
883 g_return_if_fail(dest_path != NULL);
885 uri_list = uri_list_to_gslist(selection_data->data);
887 if (!uri_list)
888 error = _("No URIs in the text/uri-list (nothing to do!)");
889 else if (type == drop_dest_prog)
890 run_with_files(dest_path, uri_list);
891 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
893 /* There is one URI in the list, and it's not on the local
894 * machine. Get it via the X server if possible.
897 if (provides(context, application_octet_stream))
899 char *leaf;
900 leaf = strrchr(uri_list->data, '/');
901 if (leaf)
902 leaf++;
903 else
904 leaf = uri_list->data;
905 g_dataset_set_data_full(context, "leafname",
906 g_strdup(leaf), g_free);
907 gtk_drag_get_data(widget, context,
908 application_octet_stream, time);
909 send_reply = FALSE;
911 else
912 error = _("Can't get data from remote machine "
913 "(application/octet-stream not provided)");
915 else
917 GSList *local_paths = NULL;
918 GSList *next;
920 /* Either one local URI, or a list. If everything in the list
921 * isn't local then we are stuck.
924 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
926 char *path;
928 path = get_local_path((char *) next_uri->data);
930 if (path)
931 local_paths = g_slist_append(local_paths,
932 g_strdup(path));
933 else
934 error = _("Some of these files are on a "
935 "different machine - they will be "
936 "ignored - sorry");
939 if (!local_paths)
941 error = _("None of these files are on the local "
942 "machine - I can't operate on multiple "
943 "remote files - sorry.");
945 else if (context->action == GDK_ACTION_MOVE)
946 action_move(local_paths, dest_path);
947 else if (context->action == GDK_ACTION_COPY)
948 action_copy(local_paths, dest_path, NULL);
949 else if (context->action == GDK_ACTION_LINK)
950 action_link(local_paths, dest_path);
951 else
952 error = _("Unknown action requested");
954 for (next = local_paths; next; next = next->next)
955 g_free(next->data);
956 g_slist_free(local_paths);
959 if (error)
961 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
962 delayed_error(_("Error getting file list"), error);
964 else if (send_reply)
965 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
967 next_uri = uri_list;
968 while (next_uri)
970 g_free(next_uri->data);
971 next_uri = next_uri->next;
973 g_slist_free(uri_list);