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)
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
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 */
35 #include <X11/Xatom.h>
37 #include "collection.h"
43 #include "gui_support.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
,
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
,
66 static void drag_leave(GtkWidget
*widget
,
67 GdkDragContext
*context
);
68 static void drag_data_received(GtkWidget
*widget
,
69 GdkDragContext
*context
,
72 GtkSelectionData
*selection_data
,
75 static void got_data_xds_reply(GtkWidget
*widget
,
76 GdkDragContext
*context
,
77 GtkSelectionData
*selection_data
,
79 static void got_data_raw(GtkWidget
*widget
,
80 GdkDragContext
*context
,
81 GtkSelectionData
*selection_data
,
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
,
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"),
117 GdkAtom XdndDirectSave0
;
118 GdkAtom xa_text_plain
;
119 GdkAtom text_uri_list
;
120 GdkAtom application_octet_stream
;
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",
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
);
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
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 "
166 gtk_box_pack_start(GTK_BOX(vbox
), toggle_drag_to_icons
, FALSE
, TRUE
, 0);
171 static void update_options()
173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_no_hostnames
),
175 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_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;
199 static char *drag_to_icons(char *data
)
201 o_drag_to_icons
= atoi(data
) != 0;
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
,
213 GDK_PROP_MODE_REPLACE
,
218 static char *get_xds_prop(GdkDragContext
*context
)
223 if (gdk_property_get(context
->source_window
,
229 &length
, &prop_text
) && prop_text
)
231 /* Terminate the string */
232 prop_text
= g_realloc(prop_text
, length
+ 1);
233 prop_text
[length
] = '\0';
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
)
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"));
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
);
282 list
= g_slist_append(list
, uri
);
285 uri_list
= linebreak
+ 2;
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
)
299 leader
= g_string_new("file://");
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");
321 g_string_free(leader
, TRUE
);
324 /* DRAGGING FROM US */
326 /* The user has held the mouse button down over an item and moved -
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
,
337 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
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
},
349 GdkDragAction actions
;
351 if (number_selected
== 1)
352 item
= selected_item(collection
);
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
, "/",
364 target_list
= gtk_target_list_new(target_table
, 3);
365 g_free(target_table
[2].target
);
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
;
373 actions
= GDK_ACTION_MOVE
;
375 context
= gtk_drag_begin(widget
,
378 (event
->state
& GDK_BUTTON1_MASK
) ? 1 :
379 (event
->state
& GDK_BUTTON2_MASK
) ? 2 : 3,
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
),
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
,
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
;
407 FilerWindow
*filer_window
;
410 filer_window
= g_dataset_get_data(context
, "filer_window");
411 g_return_if_fail(filer_window
!= NULL
);
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
))
425 delayed_error(PROJECT
, _("Invalid type "
426 "requested (not a valid atom)"));
429 g_warning("drag_data_get: Can't find selected item\n");
431 case TARGET_URI_LIST
:
432 string
= g_string_new(NULL
);
433 create_uri_list(string
,
436 to_send
= string
->str
;
437 to_send_length
= string
->len
;
438 delete_once_sent
= TRUE
;
439 g_string_free(string
, FALSE
);
442 delayed_error("drag_data_get",
443 _("Internal error - bad info type"));
447 gtk_selection_data_set(selection_data
,
453 if (delete_once_sent
)
456 collection_clear_selection(filer_window
->collection
);
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, */
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
,
500 FilerWindow
*filer_window
;
503 GdkDragAction action
= context
->suggested_action
;
504 char *new_path
= 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
,
516 item
= item_number
>= 0
517 ? (DirItem
*) filer_window
->collection
->items
[item_number
].data
522 /* If we didn't drop onto a directory, application or
523 * executable file then act as though the drop is to the
526 if (item
->base_type
!= TYPE_DIRECTORY
527 && !(item
->flags
& ITEM_FLAG_EXEC_FILE
))
533 /* Drop onto the window background */
534 collection_set_cursor_item(filer_window
->collection
,
537 if (gtk_drag_get_source_widget(context
) == widget
)
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
;
552 type
= drop_dest_dir
;
555 new_path
= g_strdup(filer_window
->path
);
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
)
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
;
578 if (provides(context
, text_uri_list
) ||
579 provides(context
, application_octet_stream
))
580 type
= drop_dest_prog
;
585 new_path
= make_path(filer_window
->path
,
586 item
->leafname
)->str
;
587 collection_set_cursor_item(filer_window
->collection
,
593 g_dataset_set_data(context
, "drop_dest_type", 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
);
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
621 static gboolean
drag_drop(GtkWidget
*widget
,
622 GdkDragContext
*context
,
628 char *leafname
= NULL
;
629 FilerWindow
*filer_window
;
630 GdkAtom target
= GDK_NONE
;
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
);
647 if (strchr(leafname
, '/'))
649 error
= _("XDS protocol error: "
650 "leafname may not contain '/'\n");
659 uri
= g_string_new(NULL
);
660 g_string_sprintf(uri
, "file://%s%s",
664 set_xds_prop(context
, uri
->str
);
665 g_string_free(uri
, TRUE
);
667 target
= XdndDirectSave0
;
668 g_dataset_set_data_full(context
, "leafname",
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
;
684 if (dest_type
== drop_dest_dir
)
685 error
= _("Sorry - I require a target type of "
686 "text/uri-list or XdndDirectSave0.");
688 error
= _("Sorry - I require a target type of "
689 "text/uri-list or application/octet-stream.");
694 gtk_drag_finish(context
, FALSE
, FALSE
, time
); /* Failure */
696 delayed_error(PROJECT
, error
);
699 gtk_drag_get_data(widget
, context
, target
, time
);
704 /* Called when some data arrives from the remote app (which we asked for
707 static void drag_data_received(GtkWidget
*widget
,
708 GdkDragContext
*context
,
711 GtkSelectionData
*selection_data
,
715 if (!selection_data
->data
)
718 gtk_drag_finish(context
, FALSE
, FALSE
, time
); /* Failure */
725 got_data_xds_reply(widget
, context
,
726 selection_data
, time
);
729 got_data_raw(widget
, context
, selection_data
, time
);
731 case TARGET_URI_LIST
:
732 got_uri_list(widget
, context
, selection_data
, time
);
735 gtk_drag_finish(context
, FALSE
, FALSE
, time
);
736 delayed_error("drag_data_received",
737 _("Unknown target"));
742 static void got_data_xds_reply(GtkWidget
*widget
,
743 GdkDragContext
*context
,
744 GtkSelectionData
*selection_data
,
747 gboolean mark_unsafe
= TRUE
;
748 char response
= *selection_data
->data
;
752 dest_path
= g_dataset_get_data(context
, "drop_dest_path");
754 if (selection_data
->length
!= 1)
759 /* Sender couldn't save there - ask for another
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
);
770 error
= _("Remote app can't or won't send me "
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
),
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 */
796 set_xds_prop(context
, "");
797 /* Unsave also implies that the drag failed */
798 gtk_drag_finish(context
, FALSE
, FALSE
, time
);
802 delayed_error(PROJECT
, error
);
805 static void got_data_raw(GtkWidget
*widget
,
806 GdkDragContext
*context
,
807 GtkSelectionData
*selection_data
,
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! */
828 leafname
= g_dataset_get_data(context
, "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
);
838 error
= g_strerror(errno
);
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
);
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
);
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
,
876 gboolean send_reply
= TRUE
;
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
);
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
))
900 leaf
= strrchr(uri_list
->data
, '/');
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
);
912 error
= _("Can't get data from remote machine "
913 "(application/octet-stream not provided)");
917 GSList
*local_paths
= NULL
;
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
)
928 path
= get_local_path((char *) next_uri
->data
);
931 local_paths
= g_slist_append(local_paths
,
934 error
= _("Some of these files are on a "
935 "different machine - they will be "
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
);
952 error
= _("Unknown action requested");
954 for (next
= local_paths
; next
; next
= next
->next
)
956 g_slist_free(local_paths
);
961 gtk_drag_finish(context
, FALSE
, FALSE
, time
); /* Failure */
962 delayed_error(_("Error getting file list"), error
);
965 gtk_drag_finish(context
, TRUE
, FALSE
, time
); /* Success! */
970 g_free(next_uri
->data
);
971 next_uri
= next_uri
->next
;
973 g_slist_free(uri_list
);