r214: Added support for mc's Virtual File System.
[rox-filer.git] / ROX-Filer / src / dnd.c
blob37a7b5b3fd388a711cf80af715ac390f6f13bfbd
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 "filer.h"
40 #include "action.h"
41 #include "pixmaps.h"
42 #include "gui_support.h"
43 #include "support.h"
44 #include "options.h"
45 #include "run.h"
47 #define MAXURILEN 4096 /* Longest URI to allow */
49 /* Static prototypes */
50 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context);
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);
94 /* Possible values for drop_dest_type (can also be NULL).
95 * In either case, drop_dest_path is the app/file/dir to use.
97 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
98 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
100 static OptionsSection options =
102 "Drag and Drop options",
103 create_options,
104 update_options,
105 set_options,
106 save_options
109 enum
111 TARGET_RAW,
112 TARGET_URI_LIST,
113 TARGET_XDS,
116 GdkAtom XdndDirectSave0;
117 GdkAtom xa_text_plain;
118 GdkAtom text_uri_list;
119 GdkAtom application_octet_stream;
121 void dnd_init()
123 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
124 xa_text_plain = gdk_atom_intern("text/plain", FALSE);
125 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
126 application_octet_stream = gdk_atom_intern("application/octet-stream",
127 FALSE);
129 options_sections = g_slist_prepend(options_sections, &options);
130 option_register("dnd_no_hostnames", load_no_hostnames);
133 /* OPTIONS */
135 static gboolean o_no_hostnames = FALSE;
136 static GtkWidget *toggle_no_hostnames;
138 /* Build up some option widgets to go in the options dialog, but don't
139 * fill them in yet.
141 static GtkWidget *create_options()
143 GtkWidget *vbox, *label;
145 vbox = gtk_vbox_new(FALSE, 0);
146 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
148 label = gtk_label_new("Some older applications don't support XDND "
149 "fully and may need to have this option turned on. "
150 "Use this if dragging files to an application shows "
151 "a + sign on the pointer but the drop doesn't work.");
152 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
153 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
155 toggle_no_hostnames =
156 gtk_check_button_new_with_label("Don't use hostnames");
157 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
159 return vbox;
162 static void update_options()
164 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
165 o_no_hostnames);
168 static void set_options()
170 o_no_hostnames = gtk_toggle_button_get_active(
171 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
174 static void save_options()
176 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
179 static char *load_no_hostnames(char *data)
181 o_no_hostnames = atoi(data) != 0;
182 return NULL;
185 /* SUPPORT FUNCTIONS */
187 static char *get_dest_path(FilerWindow *filer_window, GdkDragContext *context)
189 char *path;
191 path = g_dataset_get_data(context, "drop_dest_path");
193 return path ? path : filer_window->path;
196 /* Set the XdndDirectSave0 property on the source window for this context */
197 static void set_xds_prop(GdkDragContext *context, char *text)
199 gdk_property_change(context->source_window,
200 XdndDirectSave0,
201 xa_text_plain, 8,
202 GDK_PROP_MODE_REPLACE,
203 text,
204 strlen(text));
207 static char *get_xds_prop(GdkDragContext *context)
209 guchar *prop_text;
210 gint length;
212 if (gdk_property_get(context->source_window,
213 XdndDirectSave0,
214 xa_text_plain,
215 0, MAXURILEN,
216 FALSE,
217 NULL, NULL,
218 &length, &prop_text) && prop_text)
220 /* Terminate the string */
221 prop_text = g_realloc(prop_text, length + 1);
222 prop_text[length] = '\0';
223 return prop_text;
226 return NULL;
229 /* Is the sender willing to supply this target type? */
230 static gboolean provides(GdkDragContext *context, GdkAtom target)
232 GList *targets = context->targets;
234 while (targets && ((GdkAtom) targets->data != target))
235 targets = targets->next;
237 return targets != NULL;
240 /* Convert a list of URIs into a list of strings.
241 * Lines beginning with # are skipped.
242 * The text block passed in is zero terminated (after the final CRLF)
244 static GSList *uri_list_to_gslist(char *uri_list)
246 GSList *list = NULL;
248 while (*uri_list)
250 char *linebreak;
251 char *uri;
252 int length;
254 linebreak = strchr(uri_list, 13);
256 if (!linebreak || linebreak[1] != 10)
258 delayed_error("uri_list_to_gslist",
259 "Incorrect or missing line break "
260 "in text/uri-list data");
261 return list;
264 length = linebreak - uri_list;
266 if (length && uri_list[0] != '#')
268 uri = g_malloc(sizeof(char) * (length + 1));
269 strncpy(uri, uri_list, length);
270 uri[length] = 0;
271 list = g_slist_append(list, uri);
274 uri_list = linebreak + 2;
277 return list;
280 /* Append all the URIs in the selection to the string */
281 static void create_uri_list(GString *string,
282 Collection *collection,
283 FilerWindow *filer_window)
285 GString *leader;
286 int i, num_selected;
288 leader = g_string_new("file://");
289 if (!o_no_hostnames)
290 g_string_append(leader, our_host_name());
291 g_string_append(leader, filer_window->path);
292 if (leader->str[leader->len - 1] != '/')
293 g_string_append_c(leader, '/');
295 num_selected = collection->number_selected;
297 for (i = 0; num_selected > 0; i++)
299 if (collection->items[i].selected)
301 DirItem *item = (DirItem *) collection->items[i].data;
303 g_string_append(string, leader->str);
304 g_string_append(string, item->leafname);
305 g_string_append(string, "\r\n");
306 num_selected--;
310 g_string_free(leader, TRUE);
313 /* DRAGGING FROM US */
315 /* The user has held the mouse button down over an item and moved -
316 * start a drag.
318 * We always provide text/uri-list. If we are dragging a single, regular file
319 * then we also offer application/octet-stream and the type of the file.
321 void drag_selection(Collection *collection,
322 GdkEventMotion *event,
323 gint number_selected,
324 gpointer user_data)
326 FilerWindow *filer_window = (FilerWindow *) user_data;
327 GtkWidget *widget;
328 MaskedPixmap *image;
329 GdkDragContext *context;
330 GtkTargetList *target_list;
331 GtkTargetEntry target_table[] =
333 {"text/uri-list", 0, TARGET_URI_LIST},
334 {"application/octet-stream", 0, TARGET_RAW},
335 {"", 0, TARGET_RAW},
337 DirItem *item;
339 if (number_selected == 1)
340 item = selected_item(collection);
341 else
342 item = NULL;
344 widget = GTK_WIDGET(collection);
346 if (item && item->mime_type)
348 MIME_type *t = item->mime_type;
350 target_table[2].target = g_strconcat(t->media_type, "/",
351 t->subtype, NULL);
352 target_list = gtk_target_list_new(target_table, 3);
353 g_free(target_table[2].target);
355 else
356 target_list = gtk_target_list_new(target_table, 1);
358 context = gtk_drag_begin(widget,
359 target_list,
360 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK,
361 (event->state & GDK_BUTTON1_MASK) ? 1 : 2,
362 (GdkEvent *) event);
363 g_dataset_set_data(context, "filer_window", filer_window);
365 image = item ? item->image : &default_pixmap[TYPE_MULTIPLE];
367 gtk_drag_set_icon_pixmap(context,
368 gtk_widget_get_colormap(widget),
369 image->pixmap,
370 image->mask,
371 0, 0);
374 /* Called when a remote app wants us to send it some data.
375 * TODO: Maybe we should handle errors better (ie, let the remote app know
376 * the drag has failed)?
378 void drag_data_get(GtkWidget *widget,
379 GdkDragContext *context,
380 GtkSelectionData *selection_data,
381 guint info,
382 guint32 time)
384 char *to_send = "E"; /* Default to sending an error */
385 long to_send_length = 1;
386 gboolean delete_once_sent = FALSE;
387 GdkAtom type = XA_STRING;
388 GString *string;
389 FilerWindow *filer_window;
390 DirItem *item;
392 filer_window = g_dataset_get_data(context, "filer_window");
393 g_return_if_fail(filer_window != NULL);
395 switch (info)
397 case TARGET_RAW:
398 item = selected_item(filer_window->collection);
399 if (item && load_file(make_path(filer_window->path,
400 item->leafname)->str,
401 &to_send, &to_send_length))
403 delete_once_sent = TRUE;
404 type = selection_data->type;
405 break;
407 g_warning("drag_data_get: Can't find selected item\n");
408 return;
409 case TARGET_URI_LIST:
410 string = g_string_new(NULL);
411 create_uri_list(string,
412 COLLECTION(widget),
413 filer_window);
414 to_send = string->str;
415 to_send_length = string->len;
416 delete_once_sent = TRUE;
417 g_string_free(string, FALSE);
418 break;
419 default:
420 delayed_error("drag_data_get",
421 "Internal error - bad info type\n");
422 break;
425 gtk_selection_data_set(selection_data,
426 type,
428 to_send,
429 to_send_length);
431 if (delete_once_sent)
432 g_free(to_send);
434 collection_clear_selection(filer_window->collection);
437 /* DRAGGING TO US */
439 /* Set up this filer window as a drop target. Called once, when the
440 * filer window is first created.
442 void drag_set_dest(GtkWidget *widget, FilerWindow *filer_window)
444 GtkTargetEntry target_table[] =
446 {"text/uri-list", 0, TARGET_URI_LIST},
447 {"XdndDirectSave0", 0, TARGET_XDS},
448 {"application/octet-stream", 0, TARGET_RAW},
451 gtk_drag_dest_set(widget,
452 0, /* GTK_DEST_DEFAULT_MOTION, */
453 target_table,
454 sizeof(target_table) / sizeof(*target_table),
455 GDK_ACTION_COPY | GDK_ACTION_MOVE
456 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
458 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
459 GTK_SIGNAL_FUNC(drag_motion), filer_window);
460 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
461 GTK_SIGNAL_FUNC(drag_leave), filer_window);
462 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
463 GTK_SIGNAL_FUNC(drag_drop), filer_window);
464 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
465 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
468 /* Called during the drag when the mouse is in a widget registered
469 * as a drop target. Returns TRUE if we can accept the drop.
471 static gboolean drag_motion(GtkWidget *widget,
472 GdkDragContext *context,
473 gint x,
474 gint y,
475 guint time)
477 FilerWindow *filer_window;
478 DirItem *item;
479 int item_number;
480 GdkDragAction action = context->suggested_action;
481 char *new_path = NULL;
482 char *type = NULL;
484 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
485 g_return_val_if_fail(filer_window != NULL, TRUE);
487 item_number = collection_get_item(filer_window->collection, x, y);
489 item = item_number >= 0
490 ? (DirItem *) filer_window->collection->items[item_number].data
491 : NULL;
493 if (item)
495 /* If we didn't drop onto a directory, application or
496 * executable file then act as though the drop is to the
497 * window background.
499 if (item->base_type != TYPE_DIRECTORY
500 && !(item->flags & ITEM_FLAG_EXEC_FILE))
501 item = NULL;
504 if (!item)
506 /* Drop onto the window background */
507 collection_set_cursor_item(filer_window->collection,
508 -1);
510 if (gtk_drag_get_source_widget(context) == widget)
511 goto out;
513 if (access(filer_window->path, W_OK) != 0)
514 goto out; /* No write permission */
516 if (filer_window->panel_type != PANEL_NO)
518 if (context->actions & GDK_ACTION_LINK)
520 action = GDK_ACTION_LINK;
521 type = drop_dest_dir;
524 else
525 type = drop_dest_dir;
527 if (type)
528 new_path = g_strdup(filer_window->path);
530 else
532 /* Drop onto a program/directory of some sort */
533 if (!(provides(context, text_uri_list) ||
534 provides(context, application_octet_stream)))
535 goto out;
536 /* (actually, we should probably allow any data type) */
538 if (gtk_drag_get_source_widget(context) == widget)
540 Collection *collection = filer_window->collection;
542 if (collection->items[item_number].selected)
543 goto out;
546 if (item->base_type == TYPE_DIRECTORY &&
547 !(item->flags & ITEM_FLAG_APPDIR))
548 type = drop_dest_dir;
549 else
550 type = drop_dest_prog;
552 new_path = make_path(filer_window->path,
553 item->leafname)->str;
554 collection_set_cursor_item(filer_window->collection,
555 item_number);
558 out:
559 g_dataset_set_data(context, "drop_dest_type", type);
560 if (type)
562 gdk_drag_status(context, action, time);
563 g_dataset_set_data_full(context, "drop_dest_path",
564 g_strdup(new_path), g_free);
566 else
567 g_free(new_path);
569 return type != NULL;
572 /* Remove panel highlights */
573 static void drag_leave(GtkWidget *widget,
574 GdkDragContext *context)
576 FilerWindow *filer_window;
578 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
579 g_return_if_fail(filer_window != NULL);
581 collection_set_cursor_item(filer_window->collection, -1);
584 /* User has tried to drop some data on us. Decide what format we would
585 * like the data in.
587 static gboolean drag_drop(GtkWidget *widget,
588 GdkDragContext *context,
589 gint x,
590 gint y,
591 guint time)
593 char *error = NULL;
594 char *leafname = NULL;
595 FilerWindow *filer_window;
596 GdkAtom target = GDK_NONE;
597 char *dest_path;
598 char *dest_type = NULL;
600 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
601 g_return_val_if_fail(filer_window != NULL, TRUE);
603 dest_path = g_dataset_get_data(context, "drop_dest_path");
604 dest_type = g_dataset_get_data(context, "drop_dest_type");
606 g_return_val_if_fail(dest_path != NULL, TRUE);
608 if (dest_type == drop_dest_dir && provides(context, XdndDirectSave0))
610 leafname = get_xds_prop(context);
611 if (leafname)
613 if (strchr(leafname, '/'))
615 error = "XDS protocol error: "
616 "leafname may not contain '/'\n";
617 g_free(leafname);
619 leafname = NULL;
621 else
623 GString *uri;
625 uri = g_string_new(NULL);
626 g_string_sprintf(uri, "file://%s%s",
627 our_host_name(),
628 make_path(dest_path,
629 leafname)->str);
630 set_xds_prop(context, uri->str);
631 g_string_free(uri, TRUE);
633 target = XdndDirectSave0;
634 g_dataset_set_data_full(context, "leafname",
635 leafname, g_free);
638 else
639 error = "XdndDirectSave0 target provided, but the atom "
640 "XdndDirectSave0 (type text/plain) did not "
641 "contain a leafname\n";
643 else if (provides(context, text_uri_list))
644 target = text_uri_list;
645 else if (provides(context, application_octet_stream))
646 target = application_octet_stream;
647 else
649 if (dest_type == drop_dest_dir)
650 error = "Sorry - I require a target type of "
651 "text/uri-list or XdndDirectSave0.";
652 else
653 error = "Sorry - I require a target type of "
654 "text/uri-list or application/octet-stream.";
657 if (error)
659 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
661 delayed_error("ROX-Filer", error);
663 else
664 gtk_drag_get_data(widget, context, target, time);
666 return TRUE;
669 /* Called when some data arrives from the remote app (which we asked for
670 * in drag_drop).
672 static void drag_data_received(GtkWidget *widget,
673 GdkDragContext *context,
674 gint x,
675 gint y,
676 GtkSelectionData *selection_data,
677 guint info,
678 guint32 time)
680 if (!selection_data->data)
682 /* Timeout? */
683 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
684 return;
687 switch (info)
689 case TARGET_XDS:
690 got_data_xds_reply(widget, context,
691 selection_data, time);
692 break;
693 case TARGET_RAW:
694 got_data_raw(widget, context, selection_data, time);
695 break;
696 case TARGET_URI_LIST:
697 got_uri_list(widget, context, selection_data, time);
698 break;
699 default:
700 gtk_drag_finish(context, FALSE, FALSE, time);
701 delayed_error("drag_data_received", "Unknown target");
702 break;
706 static void got_data_xds_reply(GtkWidget *widget,
707 GdkDragContext *context,
708 GtkSelectionData *selection_data,
709 guint32 time)
711 gboolean mark_unsafe = TRUE;
712 char response = *selection_data->data;
713 char *error = NULL;
715 if (selection_data->length != 1)
716 response = '?';
718 if (response == 'F')
720 /* Sender couldn't save there - ask for another
721 * type if possible.
723 if (provides(context, application_octet_stream))
725 mark_unsafe = FALSE; /* Wait and see */
727 gtk_drag_get_data(widget, context,
728 application_octet_stream, time);
730 else
731 error = "Remote app can't or won't send me "
732 "the data - sorry";
734 else if (response == 'S')
736 FilerWindow *filer_window;
738 /* Success - data is saved */
739 mark_unsafe = FALSE; /* It really is safe */
740 gtk_drag_finish(context, TRUE, FALSE, time);
742 filer_window = gtk_object_get_data(GTK_OBJECT(widget),
743 "filer_window");
744 g_return_if_fail(filer_window != NULL);
746 update_dir(filer_window, TRUE);
748 else if (response != 'E')
750 error = "XDS protocol error: "
751 "return code should be 'S', 'F' or 'E'\n";
753 /* else: error has been reported by the sender */
755 if (mark_unsafe)
757 set_xds_prop(context, "");
758 /* Unsave also implies that the drag failed */
759 gtk_drag_finish(context, FALSE, FALSE, time);
762 if (error)
763 delayed_error("ROX-Filer", error);
766 static void got_data_raw(GtkWidget *widget,
767 GdkDragContext *context,
768 GtkSelectionData *selection_data,
769 guint32 time)
771 FilerWindow *filer_window;
772 char *leafname;
773 int fd;
774 char *error = NULL;
775 char *dest_path;
777 g_return_if_fail(selection_data->data != NULL);
779 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
780 g_return_if_fail(filer_window != NULL);
781 dest_path = get_dest_path(filer_window, context);
783 if (g_dataset_get_data(context, "drop_dest_type") == drop_dest_prog)
785 /* The data needs to be sent to an application */
786 run_with_data(dest_path,
787 selection_data->data, selection_data->length);
788 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
789 return;
792 leafname = g_dataset_get_data(context, "leafname");
793 if (!leafname)
794 leafname = "UntitledData";
796 fd = open(make_path(dest_path, leafname)->str,
797 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
798 S_IRUSR | S_IRGRP | S_IROTH |
799 S_IWUSR | S_IWGRP | S_IWOTH);
801 if (fd == -1)
802 error = g_strerror(errno);
803 else
805 if (write(fd,
806 selection_data->data,
807 selection_data->length) == -1)
808 error = g_strerror(errno);
810 if (close(fd) == -1 && !error)
811 error = g_strerror(errno);
813 update_dir(filer_window, TRUE);
816 if (error)
818 if (provides(context, XdndDirectSave0))
819 set_xds_prop(context, "");
820 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
821 delayed_error("Error saving file", error);
823 else
824 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
827 /* We've got a list of URIs from somewhere (probably another filer window).
828 * If the files are on the local machine then try to copy them ourselves,
829 * otherwise, if there was only one file and application/octet-stream was
830 * provided, get the data via the X server.
832 static void got_uri_list(GtkWidget *widget,
833 GdkDragContext *context,
834 GtkSelectionData *selection_data,
835 guint32 time)
837 FilerWindow *filer_window;
838 GSList *uri_list;
839 char *error = NULL;
840 GSList *next_uri;
841 gboolean send_reply = TRUE;
842 char *dest_path;
843 char *type;
845 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
846 g_return_if_fail(filer_window != NULL);
848 dest_path = get_dest_path(filer_window, context);
849 type = g_dataset_get_data(context, "drop_dest_type");
851 uri_list = uri_list_to_gslist(selection_data->data);
853 if (!uri_list)
854 error = "No URIs in the text/uri-list (nothing to do!)";
855 else if (type == drop_dest_prog)
856 run_with_files(dest_path, uri_list);
857 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
859 /* There is one URI in the list, and it's not on the local
860 * machine. Get it via the X server if possible.
863 if (provides(context, application_octet_stream))
865 char *leaf;
866 leaf = strrchr(uri_list->data, '/');
867 if (leaf)
868 leaf++;
869 else
870 leaf = uri_list->data;
871 g_dataset_set_data_full(context, "leafname",
872 g_strdup(leaf), g_free);
873 gtk_drag_get_data(widget, context,
874 application_octet_stream, time);
875 send_reply = FALSE;
877 else
878 error = "Can't get data from remote machine "
879 "(application/octet-stream not provided)";
881 else
883 GSList *local_paths = NULL;
884 GSList *next;
886 /* Either one local URI, or a list. If everything in the list
887 * isn't local then we are stuck.
890 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
892 char *path;
894 path = get_local_path((char *) next_uri->data);
896 if (path)
897 local_paths = g_slist_append(local_paths,
898 g_strdup(path));
899 else
900 error = "Some of these files are on a "
901 "different machine - they will be "
902 "ignored - sorry";
905 if (!local_paths)
907 error = "None of these files are on the local machine "
908 "- I can't operate on multiple remote files - "
909 "sorry.";
911 else if (context->action == GDK_ACTION_MOVE)
912 action_move(local_paths, dest_path);
913 else if (context->action == GDK_ACTION_COPY)
914 action_copy(local_paths, dest_path);
915 else if (context->action == GDK_ACTION_LINK)
916 action_link(local_paths, dest_path);
917 else
918 error = "Unknown action requested";
920 for (next = local_paths; next; next = next->next)
921 g_free(next->data);
922 g_slist_free(local_paths);
925 if (error)
927 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
928 delayed_error("Error getting file list", error);
930 else if (send_reply)
931 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
933 next_uri = uri_list;
934 while (next_uri)
936 g_free(next_uri->data);
937 next_uri = next_uri->next;
939 g_slist_free(uri_list);