r323: Improved pinboard support. Files can be dropped onto the pinboard
[rox-filer/ma.git] / ROX-Filer / src / dnd.c
blob22b3ae7da37043e72d3adcd19b1d608b46f1bab6
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>
33 #include <sys/param.h>
35 #include <X11/Xlib.h>
36 #include <X11/Xatom.h>
37 #include <gtk/gtk.h>
38 #include "collection.h"
40 #include "dnd.h"
41 #include "filer.h"
42 #include "action.h"
43 #include "pixmaps.h"
44 #include "gui_support.h"
45 #include "support.h"
46 #include "options.h"
47 #include "run.h"
48 #include "pinboard.h"
50 #define MAXURILEN 4096 /* Longest URI to allow */
52 /* Static prototypes */
53 static void create_uri_list(GString *string,
54 Collection *collection,
55 FilerWindow *filer_window);
56 static gboolean drag_drop(GtkWidget *widget,
57 GdkDragContext *context,
58 gint x,
59 gint y,
60 guint time,
61 FilerWindow *filer_window);
62 static gboolean provides(GdkDragContext *context, GdkAtom target);
63 static void set_xds_prop(GdkDragContext *context, char *text);
64 static gboolean drag_motion(GtkWidget *widget,
65 GdkDragContext *context,
66 gint x,
67 gint y,
68 guint time,
69 FilerWindow *filer_window);
70 static void drag_leave(GtkWidget *widget,
71 GdkDragContext *context,
72 guint32 time,
73 FilerWindow *filer_window);
74 static void drag_data_received(GtkWidget *widget,
75 GdkDragContext *context,
76 gint x,
77 gint y,
78 GtkSelectionData *selection_data,
79 guint info,
80 guint32 time,
81 FilerWindow *filer_window);
82 static void desktop_drag_data_received(GtkWidget *widget,
83 GdkDragContext *context,
84 gint x,
85 gint y,
86 GtkSelectionData *selection_data,
87 guint info,
88 guint32 time,
89 FilerWindow *filer_window);
90 static void got_data_xds_reply(GtkWidget *widget,
91 GdkDragContext *context,
92 GtkSelectionData *selection_data,
93 guint32 time,
94 FilerWindow *filer_window);
95 static void got_data_raw(GtkWidget *widget,
96 GdkDragContext *context,
97 GtkSelectionData *selection_data,
98 guint32 time);
99 static GSList *uri_list_to_gslist(char *uri_list);
100 static void got_uri_list(GtkWidget *widget,
101 GdkDragContext *context,
102 GtkSelectionData *selection_data,
103 guint32 time);
104 static GtkWidget *create_options();
105 static void update_options();
106 static void set_options();
107 static void save_options();
108 static char *load_no_hostnames(char *data);
109 static char *drag_to_icons(char *data);
110 static void drag_end(GtkWidget *widget,
111 GdkDragContext *context,
112 FilerWindow *filer_window);
114 /* Possible values for drop_dest_type (can also be NULL).
115 * In either case, drop_dest_path is the app/file/dir to use.
117 static char *drop_dest_prog = "drop_dest_prog"; /* Run a program */
118 static char *drop_dest_dir = "drop_dest_dir"; /* Save to path */
120 static OptionsSection options =
122 N_("Drag and Drop options"),
123 create_options,
124 update_options,
125 set_options,
126 save_options
129 enum
131 TARGET_RAW,
132 TARGET_URI_LIST,
133 TARGET_RUN_ACTION,
134 TARGET_XDS,
137 GdkAtom XdndDirectSave0;
138 GdkAtom _rox_run_action;
139 GdkAtom xa_text_plain;
140 GdkAtom text_uri_list;
141 GdkAtom application_octet_stream;
143 void dnd_init()
145 XdndDirectSave0 = gdk_atom_intern("XdndDirectSave0", FALSE);
146 _rox_run_action = gdk_atom_intern("_ROX_RUN_ACTION", FALSE);
147 xa_text_plain = gdk_atom_intern("text/plain", FALSE);
148 text_uri_list = gdk_atom_intern("text/uri-list", FALSE);
149 application_octet_stream = gdk_atom_intern("application/octet-stream",
150 FALSE);
152 options_sections = g_slist_prepend(options_sections, &options);
153 option_register("dnd_no_hostnames", load_no_hostnames);
154 option_register("dnd_drag_to_icons", drag_to_icons);
157 /* OPTIONS */
159 static gboolean o_no_hostnames = FALSE;
160 static gboolean o_drag_to_icons = TRUE;
161 static GtkWidget *toggle_no_hostnames;
162 static GtkWidget *toggle_drag_to_icons;
164 /* Build up some option widgets to go in the options dialog, but don't
165 * fill them in yet.
167 static GtkWidget *create_options()
169 GtkWidget *vbox, *label;
171 vbox = gtk_vbox_new(FALSE, 0);
172 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
174 label = gtk_label_new(_("Some older applications don't support XDND "
175 "fully and may need to have this option turned on. "
176 "Use this if dragging files to an application shows "
177 "a + sign on the pointer but the drop doesn't work."));
178 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
179 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
181 toggle_no_hostnames =
182 gtk_check_button_new_with_label(_("Don't use hostnames"));
183 gtk_box_pack_start(GTK_BOX(vbox), toggle_no_hostnames, FALSE, TRUE, 0);
185 toggle_drag_to_icons =
186 gtk_check_button_new_with_label(_("Allow dragging to icons in "
187 "filer windows"));
188 gtk_box_pack_start(GTK_BOX(vbox), toggle_drag_to_icons, FALSE, TRUE, 0);
190 return vbox;
193 static void update_options()
195 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames),
196 o_no_hostnames);
197 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_drag_to_icons),
198 o_drag_to_icons);
201 static void set_options()
203 o_no_hostnames = gtk_toggle_button_get_active(
204 GTK_TOGGLE_BUTTON(toggle_no_hostnames));
205 o_drag_to_icons = gtk_toggle_button_get_active(
206 GTK_TOGGLE_BUTTON(toggle_drag_to_icons));
209 static void save_options()
211 option_write("dnd_no_hostnames", o_no_hostnames ? "1" : "0");
212 option_write("dnd_drag_to_icons", o_drag_to_icons ? "1" : "0");
215 static char *load_no_hostnames(char *data)
217 o_no_hostnames = atoi(data) != 0;
218 return NULL;
221 static char *drag_to_icons(char *data)
223 o_drag_to_icons = atoi(data) != 0;
224 return NULL;
227 /* SUPPORT FUNCTIONS */
229 /* Set the XdndDirectSave0 property on the source window for this context */
230 static void set_xds_prop(GdkDragContext *context, char *text)
232 gdk_property_change(context->source_window,
233 XdndDirectSave0,
234 xa_text_plain, 8,
235 GDK_PROP_MODE_REPLACE,
236 text,
237 strlen(text));
240 static char *get_xds_prop(GdkDragContext *context)
242 guchar *prop_text;
243 gint length;
245 if (gdk_property_get(context->source_window,
246 XdndDirectSave0,
247 xa_text_plain,
248 0, MAXURILEN,
249 FALSE,
250 NULL, NULL,
251 &length, &prop_text) && prop_text)
253 /* Terminate the string */
254 prop_text = g_realloc(prop_text, length + 1);
255 prop_text[length] = '\0';
256 return prop_text;
259 return NULL;
262 /* Is the sender willing to supply this target type? */
263 static gboolean provides(GdkDragContext *context, GdkAtom target)
265 GList *targets = context->targets;
267 while (targets && ((GdkAtom) targets->data != target))
268 targets = targets->next;
270 return targets != NULL;
273 /* Convert a list of URIs into a list of strings.
274 * Lines beginning with # are skipped.
275 * The text block passed in is zero terminated (after the final CRLF)
277 static GSList *uri_list_to_gslist(char *uri_list)
279 GSList *list = NULL;
281 while (*uri_list)
283 char *linebreak;
284 char *uri;
285 int length;
287 linebreak = strchr(uri_list, 13);
289 if (!linebreak || linebreak[1] != 10)
291 delayed_error("uri_list_to_gslist",
292 _("Incorrect or missing line "
293 "break in text/uri-list data"));
294 return list;
297 length = linebreak - uri_list;
299 if (length && uri_list[0] != '#')
301 uri = g_malloc(sizeof(char) * (length + 1));
302 strncpy(uri, uri_list, length);
303 uri[length] = 0;
304 list = g_slist_append(list, uri);
307 uri_list = linebreak + 2;
310 return list;
313 /* Append all the URIs in the selection to the string */
314 static void create_uri_list(GString *string,
315 Collection *collection,
316 FilerWindow *filer_window)
318 GString *leader;
319 int i, num_selected;
321 leader = g_string_new("file://");
322 if (!o_no_hostnames)
323 g_string_append(leader, our_host_name());
324 g_string_append(leader, filer_window->path);
325 if (leader->str[leader->len - 1] != '/')
326 g_string_append_c(leader, '/');
328 num_selected = collection->number_selected;
330 for (i = 0; num_selected > 0; i++)
332 if (collection->items[i].selected)
334 DirItem *item = (DirItem *) collection->items[i].data;
336 g_string_append(string, leader->str);
337 g_string_append(string, item->leafname);
338 g_string_append(string, "\r\n");
339 num_selected--;
343 g_string_free(leader, TRUE);
346 /* DRAGGING FROM US */
348 /* The user has held the mouse button down over an item and moved -
349 * start a drag.
351 * We always provide text/uri-list. If we are dragging a single, regular file
352 * then we also offer application/octet-stream and the type of the file.
354 * If the RUN_ACTION minibuffer is open then only drags to other executables
355 * shown in our windows are allowed.
357 void drag_selection(Collection *collection,
358 GdkEventMotion *event,
359 gint number_selected,
360 FilerWindow *filer_window)
362 GtkWidget *widget;
363 MaskedPixmap *image;
364 GdkDragContext *context;
365 GtkTargetList *target_list;
366 GtkTargetEntry target_table[] =
368 {"text/uri-list", 0, TARGET_URI_LIST},
369 {"application/octet-stream", 0, TARGET_RAW},
370 {"", 0, TARGET_RAW},
372 DirItem *item;
373 GdkDragAction actions;
375 if (number_selected == 1)
376 item = selected_item(collection);
377 else
378 item = NULL;
380 widget = GTK_WIDGET(collection);
382 if (filer_window->mini_type == MINI_RUN_ACTION)
384 GtkTargetEntry target_table[] = {
385 {"_ROX_RUN_ACTION", 0, TARGET_RUN_ACTION},
388 if (collection->number_selected != 1)
389 return;
391 target_list = gtk_target_list_new(target_table, 1);
393 else if (item && item->base_type == TYPE_FILE)
395 MIME_type *t = item->mime_type;
397 target_table[2].target = g_strconcat(t->media_type, "/",
398 t->subtype, NULL);
399 target_list = gtk_target_list_new(target_table, 3);
400 g_free(target_table[2].target);
402 else
403 target_list = gtk_target_list_new(target_table, 1);
405 if (event->state & GDK_BUTTON1_MASK)
406 actions = GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK;
407 else
408 actions = GDK_ACTION_MOVE;
410 context = gtk_drag_begin(widget,
411 target_list,
412 actions,
413 (event->state & GDK_BUTTON1_MASK) ? 1 :
414 (event->state & GDK_BUTTON2_MASK) ? 2 : 3,
415 (GdkEvent *) event);
417 image = item ? item->image : im_multiple;
419 gtk_drag_set_icon_pixmap(context,
420 gtk_widget_get_colormap(widget),
421 image->pixmap,
422 image->mask,
423 0, 0);
426 static void drag_end(GtkWidget *widget,
427 GdkDragContext *context,
428 FilerWindow *filer_window)
430 collection_clear_selection(filer_window->collection);
431 if (filer_window->mini_type == MINI_RUN_ACTION)
432 minibuffer_hide(filer_window);
435 /* Called when a remote app wants us to send it some data.
436 * TODO: Maybe we should handle errors better (ie, let the remote app know
437 * the drag has failed)?
439 void drag_data_get(GtkWidget *widget,
440 GdkDragContext *context,
441 GtkSelectionData *selection_data,
442 guint info,
443 guint32 time,
444 FilerWindow *filer_window)
446 char *to_send = "E"; /* Default to sending an error */
447 long to_send_length = 1;
448 gboolean delete_once_sent = FALSE;
449 GdkAtom type = XA_STRING;
450 GString *string;
451 DirItem *item;
453 switch (info)
455 case TARGET_RAW:
456 item = selected_item(filer_window->collection);
457 if (item && load_file(make_path(filer_window->path,
458 item->leafname)->str,
459 &to_send, &to_send_length))
461 delete_once_sent = TRUE;
462 type = selection_data->target;
463 break;
465 g_warning("drag_data_get: Can't find selected item\n");
466 return;
467 case TARGET_URI_LIST:
468 string = g_string_new(NULL);
469 create_uri_list(string,
470 COLLECTION(widget),
471 filer_window);
472 to_send = string->str;
473 to_send_length = string->len;
474 delete_once_sent = TRUE;
475 g_string_free(string, FALSE);
476 break;
477 case TARGET_RUN_ACTION:
478 item = selected_item(filer_window->collection);
479 if (item && item->mime_type)
481 MIME_type *type = item->mime_type;
482 to_send = g_strconcat(type->media_type, "/",
483 type->subtype, NULL);
484 to_send_length = strlen(to_send);
485 delete_once_sent = TRUE;
486 break;
488 g_warning("drag_data_get: Can't find MIME-type\n");
489 return;
490 default:
491 delayed_error("drag_data_get",
492 _("Internal error - bad info type"));
493 break;
496 gtk_selection_data_set(selection_data,
497 type,
499 to_send,
500 to_send_length);
502 if (delete_once_sent)
503 g_free(to_send);
506 /* DRAGGING TO US */
508 /* Set up this filer window as a drop target. Called once, when the
509 * filer window is first created.
511 void drag_set_dest(FilerWindow *filer_window)
513 GtkWidget *widget = GTK_WIDGET(filer_window->collection);
514 GtkTargetEntry target_table[] =
516 {"text/uri-list", 0, TARGET_URI_LIST},
517 {"XdndDirectSave0", 0, TARGET_XDS},
518 {"application/octet-stream", 0, TARGET_RAW},
519 {"_ROX_RUN_ACTION", 0, TARGET_RUN_ACTION},
522 gtk_drag_dest_set(widget,
523 0, /* GTK_DEST_DEFAULT_MOTION, */
524 target_table,
525 sizeof(target_table) / sizeof(*target_table),
526 GDK_ACTION_COPY | GDK_ACTION_MOVE
527 | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
529 gtk_signal_connect(GTK_OBJECT(widget), "drag_motion",
530 GTK_SIGNAL_FUNC(drag_motion), filer_window);
531 gtk_signal_connect(GTK_OBJECT(widget), "drag_leave",
532 GTK_SIGNAL_FUNC(drag_leave), filer_window);
533 gtk_signal_connect(GTK_OBJECT(widget), "drag_drop",
534 GTK_SIGNAL_FUNC(drag_drop), filer_window);
535 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
536 GTK_SIGNAL_FUNC(drag_data_received), filer_window);
538 gtk_signal_connect(GTK_OBJECT(widget), "drag_end",
539 GTK_SIGNAL_FUNC(drag_end), filer_window);
542 /* Like drag_set_dest, but for a pinboard-type widget.
543 * You must ensure that dnd events reach this widget (eg with
544 * setup_xdnd_proxy() for the root window).
546 void drag_set_pinboard_dest(GtkWidget *widget)
548 GtkTargetEntry target_table[] = {
549 {"text/uri-list", 0, TARGET_URI_LIST},
552 gtk_drag_dest_set(widget,
553 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
554 target_table,
555 sizeof(target_table) / sizeof(*target_table),
556 GDK_ACTION_LINK);
557 gtk_signal_connect(GTK_OBJECT(widget), "drag_data_received",
558 (GtkSignalFunc) desktop_drag_data_received,
559 NULL);
562 /* Called during the drag when the mouse is in a widget registered
563 * as a drop target. Returns TRUE if we can accept the drop.
565 static gboolean drag_motion(GtkWidget *widget,
566 GdkDragContext *context,
567 gint x,
568 gint y,
569 guint time,
570 FilerWindow *filer_window)
572 DirItem *item;
573 int item_number;
574 GdkDragAction action = context->suggested_action;
575 char *new_path = NULL;
576 char *type = NULL;
578 if (o_drag_to_icons || filer_window->panel_type != PANEL_NO)
579 item_number = collection_get_item(filer_window->collection,
580 x, y);
581 else
582 item_number = -1;
584 item = item_number >= 0
585 ? (DirItem *) filer_window->collection->items[item_number].data
586 : NULL;
588 if (provides(context, _rox_run_action))
590 /* This is a special internal type. The user is dragging
591 * to an executable item to set the run action.
593 if (!item)
594 goto out;
596 if (item->flags & (ITEM_FLAG_APPDIR | ITEM_FLAG_EXEC_FILE))
598 type = drop_dest_prog;
599 new_path = make_path(filer_window->path,
600 item->leafname)->str;
602 else
603 goto out;
606 else if (item)
608 /* If we didn't drop onto a directory, application or
609 * executable file then act as though the drop is to the
610 * window background.
612 if (item->base_type != TYPE_DIRECTORY
613 && !(item->flags & ITEM_FLAG_EXEC_FILE))
614 item = NULL;
617 if (!item)
619 /* Drop onto the window background */
620 collection_set_cursor_item(filer_window->collection,
621 -1);
623 if (gtk_drag_get_source_widget(context) == widget)
624 goto out;
626 if (access(filer_window->path, W_OK) != 0)
627 goto out; /* No write permission */
629 if (filer_window->panel_type != PANEL_NO)
631 if (context->actions & GDK_ACTION_LINK)
633 action = GDK_ACTION_LINK;
634 type = drop_dest_dir;
637 else
638 type = drop_dest_dir;
640 if (type)
641 new_path = g_strdup(filer_window->path);
643 else
645 /* Drop onto a program/directory of some sort */
647 if (gtk_drag_get_source_widget(context) == widget)
649 Collection *collection = filer_window->collection;
651 if (collection->items[item_number].selected)
652 goto out;
655 if (item->base_type == TYPE_DIRECTORY &&
656 !(item->flags & ITEM_FLAG_APPDIR))
658 if (provides(context, text_uri_list) ||
659 provides(context, XdndDirectSave0))
660 type = drop_dest_dir;
662 else
664 if (provides(context, text_uri_list) ||
665 provides(context, application_octet_stream))
666 type = drop_dest_prog;
669 if (type)
671 new_path = make_path(filer_window->path,
672 item->leafname)->str;
673 collection_set_cursor_item(filer_window->collection,
674 item_number);
678 out:
679 g_dataset_set_data(context, "drop_dest_type", type);
680 if (type)
682 gdk_drag_status(context, action, time);
683 g_dataset_set_data_full(context, "drop_dest_path",
684 g_strdup(new_path), g_free);
686 else
687 g_free(new_path);
689 return type != NULL;
692 /* Remove panel highlights */
693 static void drag_leave(GtkWidget *widget,
694 GdkDragContext *context,
695 guint32 time,
696 FilerWindow *filer_window)
698 collection_set_cursor_item(filer_window->collection, -1);
701 /* User has tried to drop some data on us. Decide what format we would
702 * like the data in.
704 static gboolean drag_drop(GtkWidget *widget,
705 GdkDragContext *context,
706 gint x,
707 gint y,
708 guint time,
709 FilerWindow *filer_window)
711 char *error = NULL;
712 char *leafname = NULL;
713 GdkAtom target = GDK_NONE;
714 char *dest_path;
715 char *dest_type = NULL;
717 dest_path = g_dataset_get_data(context, "drop_dest_path");
718 dest_type = g_dataset_get_data(context, "drop_dest_type");
720 g_return_val_if_fail(dest_path != NULL, TRUE);
722 if (dest_type == drop_dest_dir && provides(context, XdndDirectSave0))
724 leafname = get_xds_prop(context);
725 if (leafname)
727 if (strchr(leafname, '/'))
729 error = _("XDS protocol error: "
730 "leafname may not contain '/'\n");
731 g_free(leafname);
733 leafname = NULL;
735 else
737 GString *uri;
739 uri = g_string_new(NULL);
740 g_string_sprintf(uri, "file://%s%s",
741 our_host_name(),
742 make_path(dest_path,
743 leafname)->str);
744 set_xds_prop(context, uri->str);
745 g_string_free(uri, TRUE);
747 target = XdndDirectSave0;
748 g_dataset_set_data_full(context, "leafname",
749 leafname, g_free);
752 else
753 error = _(
754 "XdndDirectSave0 target provided, but the atom "
755 "XdndDirectSave0 (type text/plain) did not "
756 "contain a leafname\n");
758 else if (provides(context, text_uri_list))
759 target = text_uri_list;
760 else if (provides(context, application_octet_stream))
761 target = application_octet_stream;
762 else if (dest_type == drop_dest_prog &&
763 provides(context, _rox_run_action))
764 target = _rox_run_action;
765 else
767 if (dest_type == drop_dest_dir)
768 error = _("Sorry - I require a target type of "
769 "text/uri-list or XdndDirectSave0.");
770 else
771 error = _("Sorry - I require a target type of "
772 "text/uri-list or application/octet-stream.");
775 if (error)
777 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
779 delayed_error(PROJECT, error);
781 else
782 gtk_drag_get_data(widget, context, target, time);
784 return TRUE;
787 static void got_run_action(GtkWidget *widget,
788 GdkDragContext *context,
789 GtkSelectionData *selection_data,
790 guint32 time)
792 char *type = selection_data->data;
793 char *media, *sub;
794 char *dest_path, *link;
795 char buffer[MAXPATHLEN + 1];
796 int len;
798 g_return_if_fail(type != NULL);
800 dest_path = g_dataset_get_data(context, "drop_dest_path");
802 /* dest_path might be a symlink - dereference it if so */
803 len = readlink(dest_path, buffer, sizeof(buffer) - 1);
804 if (len > 0)
806 buffer[len] = '\0';
807 dest_path = buffer;
810 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
812 sub = strchr(type, '/');
813 g_return_if_fail(sub != NULL);
815 media = g_strndup(type, sub - type);
816 sub++;
818 link = type_ask_which_action(media, sub);
819 g_free(media);
820 if (!link)
821 return;
823 if (symlink(dest_path, link))
824 delayed_error(PROJECT, g_strerror(errno));
827 /* Called when a text/uri-list arrives */
828 static void desktop_drag_data_received(GtkWidget *widget,
829 GdkDragContext *context,
830 gint x,
831 gint y,
832 GtkSelectionData *selection_data,
833 guint info,
834 guint32 time,
835 FilerWindow *filer_window)
837 GSList *uris, *next;
838 gint dx, dy;
840 if (!selection_data->data)
842 /* Timeout? */
843 return;
846 gdk_window_get_position (widget->window, &dx, &dy);
847 x += dx;
848 y += dy;
850 uris = uri_list_to_gslist(selection_data->data);
852 for (next = uris; next; next = next->next)
854 guchar *path;
856 path = get_local_path((gchar *) next->data);
857 if (path)
859 pinboard_pin(path, NULL, x, y);
860 x += 64;
863 g_free(next->data);
866 if (uris)
867 g_slist_free(uris);
870 /* Called when some data arrives from the remote app (which we asked for
871 * in drag_drop).
873 static void drag_data_received(GtkWidget *widget,
874 GdkDragContext *context,
875 gint x,
876 gint y,
877 GtkSelectionData *selection_data,
878 guint info,
879 guint32 time,
880 FilerWindow *filer_window)
882 if (!selection_data->data)
884 /* Timeout? */
885 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
886 return;
889 switch (info)
891 case TARGET_XDS:
892 got_data_xds_reply(widget, context,
893 selection_data, time,
894 filer_window);
895 break;
896 case TARGET_RAW:
897 got_data_raw(widget, context, selection_data, time);
898 break;
899 case TARGET_URI_LIST:
900 got_uri_list(widget, context, selection_data, time);
901 break;
902 case TARGET_RUN_ACTION:
903 got_run_action(widget, context, selection_data, time);
904 break;
905 default:
906 gtk_drag_finish(context, FALSE, FALSE, time);
907 delayed_error("drag_data_received",
908 _("Unknown target"));
909 break;
913 static void got_data_xds_reply(GtkWidget *widget,
914 GdkDragContext *context,
915 GtkSelectionData *selection_data,
916 guint32 time,
917 FilerWindow *filer_window)
919 gboolean mark_unsafe = TRUE;
920 char response = *selection_data->data;
921 char *error = NULL;
922 char *dest_path;
924 dest_path = g_dataset_get_data(context, "drop_dest_path");
926 if (selection_data->length != 1)
927 response = '?';
929 if (response == 'F')
931 /* Sender couldn't save there - ask for another
932 * type if possible.
934 if (provides(context, application_octet_stream))
936 mark_unsafe = FALSE; /* Wait and see */
938 gtk_drag_get_data(widget, context,
939 application_octet_stream, time);
941 else
942 error = _("Remote app can't or won't send me "
943 "the data - sorry");
945 else if (response == 'S')
947 /* Success - data is saved */
948 mark_unsafe = FALSE; /* It really is safe */
949 gtk_drag_finish(context, TRUE, FALSE, time);
951 refresh_dirs(dest_path);
953 else if (response != 'E')
955 error = _("XDS protocol error: "
956 "return code should be 'S', 'F' or 'E'\n");
958 /* else: error has been reported by the sender */
960 if (mark_unsafe)
962 set_xds_prop(context, "");
963 /* Unsave also implies that the drag failed */
964 gtk_drag_finish(context, FALSE, FALSE, time);
967 if (error)
968 delayed_error(PROJECT, error);
971 static void got_data_raw(GtkWidget *widget,
972 GdkDragContext *context,
973 GtkSelectionData *selection_data,
974 guint32 time)
976 char *leafname;
977 int fd;
978 char *error = NULL;
979 char *dest_path;
981 g_return_if_fail(selection_data->data != NULL);
983 dest_path = g_dataset_get_data(context, "drop_dest_path");
985 if (g_dataset_get_data(context, "drop_dest_type") == drop_dest_prog)
987 /* The data needs to be sent to an application */
988 run_with_data(dest_path,
989 selection_data->data, selection_data->length);
990 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
991 return;
994 leafname = g_dataset_get_data(context, "leafname");
995 if (!leafname)
996 leafname = _("UntitledData");
998 fd = open(make_path(dest_path, leafname)->str,
999 O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY,
1000 S_IRUSR | S_IRGRP | S_IROTH |
1001 S_IWUSR | S_IWGRP | S_IWOTH);
1003 if (fd == -1)
1004 error = g_strerror(errno);
1005 else
1007 if (write(fd,
1008 selection_data->data,
1009 selection_data->length) == -1)
1010 error = g_strerror(errno);
1012 if (close(fd) == -1 && !error)
1013 error = g_strerror(errno);
1015 refresh_dirs(dest_path);
1018 if (error)
1020 if (provides(context, XdndDirectSave0))
1021 set_xds_prop(context, "");
1022 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
1023 delayed_error(_("Error saving file"), error);
1025 else
1026 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
1029 /* We've got a list of URIs from somewhere (probably another filer window).
1030 * If the files are on the local machine then try to copy them ourselves,
1031 * otherwise, if there was only one file and application/octet-stream was
1032 * provided, get the data via the X server.
1034 static void got_uri_list(GtkWidget *widget,
1035 GdkDragContext *context,
1036 GtkSelectionData *selection_data,
1037 guint32 time)
1039 GSList *uri_list;
1040 char *error = NULL;
1041 GSList *next_uri;
1042 gboolean send_reply = TRUE;
1043 char *dest_path;
1044 char *type;
1046 dest_path = g_dataset_get_data(context, "drop_dest_path");
1047 type = g_dataset_get_data(context, "drop_dest_type");
1049 g_return_if_fail(dest_path != NULL);
1051 uri_list = uri_list_to_gslist(selection_data->data);
1053 if (!uri_list)
1054 error = _("No URIs in the text/uri-list (nothing to do!)");
1055 else if (type == drop_dest_prog)
1056 run_with_files(dest_path, uri_list);
1057 else if ((!uri_list->next) && (!get_local_path(uri_list->data)))
1059 /* There is one URI in the list, and it's not on the local
1060 * machine. Get it via the X server if possible.
1063 if (provides(context, application_octet_stream))
1065 char *leaf;
1066 leaf = strrchr(uri_list->data, '/');
1067 if (leaf)
1068 leaf++;
1069 else
1070 leaf = uri_list->data;
1071 g_dataset_set_data_full(context, "leafname",
1072 g_strdup(leaf), g_free);
1073 gtk_drag_get_data(widget, context,
1074 application_octet_stream, time);
1075 send_reply = FALSE;
1077 else
1078 error = _("Can't get data from remote machine "
1079 "(application/octet-stream not provided)");
1081 else
1083 GSList *local_paths = NULL;
1084 GSList *next;
1086 /* Either one local URI, or a list. If everything in the list
1087 * isn't local then we are stuck.
1090 for (next_uri = uri_list; next_uri; next_uri = next_uri->next)
1092 char *path;
1094 path = get_local_path((char *) next_uri->data);
1096 if (path)
1097 local_paths = g_slist_append(local_paths,
1098 g_strdup(path));
1099 else
1100 error = _("Some of these files are on a "
1101 "different machine - they will be "
1102 "ignored - sorry");
1105 if (!local_paths)
1107 error = _("None of these files are on the local "
1108 "machine - I can't operate on multiple "
1109 "remote files - sorry.");
1111 else if (context->action == GDK_ACTION_MOVE)
1112 action_move(local_paths, dest_path);
1113 else if (context->action == GDK_ACTION_COPY)
1114 action_copy(local_paths, dest_path, NULL);
1115 else if (context->action == GDK_ACTION_LINK)
1116 action_link(local_paths, dest_path);
1117 else
1118 error = _("Unknown action requested");
1120 for (next = local_paths; next; next = next->next)
1121 g_free(next->data);
1122 g_slist_free(local_paths);
1125 if (error)
1127 gtk_drag_finish(context, FALSE, FALSE, time); /* Failure */
1128 delayed_error(_("Error getting file list"), error);
1130 else if (send_reply)
1131 gtk_drag_finish(context, TRUE, FALSE, time); /* Success! */
1133 next_uri = uri_list;
1134 while (next_uri)
1136 g_free(next_uri->data);
1137 next_uri = next_uri->next;
1139 g_slist_free(uri_list);