After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 453758 - Add "Send to removable media"
Add "Send to removable media"
Status: RESOLVED FIXED
Product: nautilus-sendto
Classification: Applications
Component: general
0.5
Other All
: Normal enhancement
: ---
Assigned To: nautilus-sendto-maint
nautilus-sendto-maint
Depends on: 498506
Blocks:
 
 
Reported: 2007-07-04 18:28 UTC by Leonardo Gregianin
Modified: 2009-02-06 15:10 UTC
See Also:
GNOME target: ---
GNOME version: Unversioned Enhancement


Attachments
corrected patch (6.66 KB, patch)
2009-01-11 23:03 UTC, Maxim Ermilov
needs-work Details | Review
corrected patch (6.89 KB, patch)
2009-01-14 01:06 UTC, Maxim Ermilov
needs-work Details | Review
corrected patch (7.58 KB, patch)
2009-01-16 00:10 UTC, Maxim Ermilov
needs-work Details | Review
corrected patch (9.56 KB, patch)
2009-01-21 15:14 UTC, Maxim Ermilov
none Details | Review

Description Leonardo Gregianin 2007-07-04 18:28:01 UTC
When USB device is mounted, nautilus-sendto send to USB device.
Comment 1 Bastien Nocera 2007-08-13 15:07:28 UTC
How useful is this compared to using nautilus to copy the file?
Comment 2 Leonardo Gregianin 2007-08-16 19:26:11 UTC
This would be form more practises to copy the file, in not need to open the destination folder.
Comment 3 Björn Martensen 2008-01-18 06:24:22 UTC
i'd like to have this too, as i rather often used it in thunar and i missed it ever since i switched to gnome and nautilus.
Comment 4 Nelson Benitez 2008-04-18 12:14:17 UTC
I also think this is very useful, I use it a lot in winxp, it's a typical situation that someone comes to your desk with a memory usb and says "could you copy here the xyz file", and I just have to find that file in my pc and right-click and select "send-to -> usb device".

It's just a shortcut to do some repetitive task quicker, I call that usability :) .
Comment 5 Baptiste Mille-Mathias 2008-12-19 08:16:42 UTC
just changing title to remove USB, as perhaps we want to support Firewire and e-sata :)
Comment 6 Bastien Nocera 2009-01-07 14:40:23 UTC
See patch in bug 543965 comment 1.
Comment 7 Bastien Nocera 2009-01-10 13:48:29 UTC
Apart from not applying cleanly anymore (files moved around in SVN), the patch has:

+		GFile *source = g_file_new_for_uri (iter->data);
+		gchar *file_name = g_file_get_basename (source);
+		gchar *dest_uri = g_strconcat (vol_uri, "/", file_name, NULL);
+		GFile *dest = g_file_new_for_uri (dest_uri);

This is wrong, and you should be using g_file_get_child () instead.

+		g_file_copy (source, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);

Just like for the nautilus-burn plugin, this won't work for directories.

The code should be shared between the 2 plugins, which I can do if you write the code for recursively copying directories properly.
Comment 8 Maxim Ermilov 2009-01-11 23:03:13 UTC
Created attachment 126238 [details] [review]
corrected patch
Comment 9 Bastien Nocera 2009-01-12 20:52:13 UTC
Comment on attachment 126238 [details] [review]
corrected patch

>Index: configure.in
>===================================================================
>--- configure.in	(revision 385)
>+++ configure.in	(working copy)
>@@ -36,6 +36,7 @@
> GUPNP_AV_REQUIRED=0.2.1
> EMPATHY_REQUIRED=2.23.91
> EMPATHY_GTK_REQUIRED=2.25.2
>+GIO_REQUIRED=2.18.0

Why 2.18?

> 
> AC_SUBST(GLIB_REQUIRED)
> AC_SUBST(GTK_REQUIRED)
>@@ -56,7 +57,8 @@
> 	 gmodule-2.0 >= $GLIB_REQUIRED		  \
> 	 gtk+-2.0    >= $GTK_REQUIRED             \
> 	 libglade-2.0 >= $GLADE_REQUIRED            \
>-	 gconf-2.0 >= $GCONF_REQUIRED)
>+	 gconf-2.0 >= $GCONF_REQUIRED               \
>+	 gio-2.0 >= $GIO_REQUIRED)

That should filed in a separate bug.

> # The full list of plugins
>-allowed_plugins="balsa bluetooth empathy evolution gaim gajim nautilus-burn pidgin sylpheed-claws thunderbird upnp"
>+allowed_plugins="balsa bluetooth empathy evolution gaim gajim nautilus-burn pidgin share sylpheed-claws thunderbird upnp"
> 
> plugin_error_or_ignore()
> {
>@@ -226,6 +228,9 @@
> 				add_plugin="0"
> 			fi
> 		;;
>+		share)
>+			add_plugin="1"
>+		;;

Please call the plugin something other than share. Call it "removable-devices" or something, share is too broad a term.

<snip>
>+GVolumeMonitor* vol_monitor = NULL;
>+GList *vol;

Please call it "volumes", or "volumes_list". "vol" makes me think there's only one item, not a list.

<snip>
>+static
>+GtkWidget* get_contacts_widget (NstPlugin *plugin)
>+{
>+	GtkListStore *store;
>+	GtkWidget *cb;
>+	GList *liter;

It's not an iter, and we usually call those just "*l"

>+	GtkTreeIter iter;
>+	GtkCellRenderer *renderer;
>+
>+	store = gtk_list_store_new (1, G_TYPE_STRING);

No icon for the devices?

<snip>
>+static
>+void copy_dir (GFile* source, GFile* dest)
>+{
>+	GFileEnumerator* en = g_file_enumerate_children (source, "*", G_FILE_QUERY_INFO_NONE, NULL, NULL);

No big assignments like that in the declarations.

>+	GFileInfo* info;
>+
>+	g_file_make_directory (dest, NULL, NULL);

You should check for errors here.

>+	while ((info = g_file_enumerator_next_file (en, NULL, NULL)) != NULL)
>+	{

Braces on the same line as the conditional.

>+		gchar *name = (gchar*)g_file_info_get_name (G_FILE_INFO (info));

Why do you need a cast here?

>+		if (name != NULL)
>+		{
>+			GFile *child = g_file_get_child (source, name);
>+			gchar *file_name = g_file_get_basename (child);
>+			GFile *dst = g_file_get_child (dest, file_name);
>+
>+			if (g_file_query_file_type (child, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) copy_dir (child, dst);
>+			else g_file_copy (child, dst, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);
>+
>+			g_free (file_name);
>+			g_object_unref (child);
>+			g_object_unref (dst);
>+		}

All this code looks like it's the same as in send_files below. Could you please merge those?

>+		

There's a tab here on an empty line, please remove those.

>+		g_free (name);
>+	}
>+
>+	g_object_unref (en);
>+}
>+
>+static
>+gboolean send_files (NstPlugin *plugin, GtkWidget *contact_widget,
>+			 GList *file_list)

Please use:
static gboolean <- same line
send_files (...) <- another line

>+{
>+	int choice = gtk_combo_box_get_active (GTK_COMBO_BOX (contact_widget));

Same problem with the declaration/assignment as above.

>+	GMount *dest_vol;
>+	GFile *vol_root;
>+	gchar *vol_uri;
>+	GList *iter;
>+
>+	if (choice < 0) return TRUE;

Please use 2 lines for that.

>+	dest_vol = g_list_nth_data (vol, choice);
>+	if (!G_IS_MOUNT (dest_vol)) return TRUE;
>+	vol_root = g_mount_get_root (dest_vol);
>+	vol_uri = g_file_get_uri (vol_root);
>+
>+
>+	for (iter = file_list; iter != NULL; iter = g_list_next (iter))
>+	{
>+		GFile *source;
>+		gchar *file_name;
>+		GFile *dest;
>+
>+		if (g_uri_parse_scheme (iter->data) == NULL) source = g_file_new_for_path (iter->data);
>+		else source = g_file_new_for_uri (iter->data);
>+
>+		file_name = g_file_get_basename (source);
>+		dest = g_file_get_child (vol_root, file_name);
>+
>+		if (g_file_query_file_type (source, G_FILE_QUERY_INFO_NONE, NULL) == G_FILE_TYPE_DIRECTORY) copy_dir (source, dest);
>+		else g_file_copy (source, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);
>+
>+		g_free (file_name);
>+		g_object_unref (source);
>+		g_object_unref (dest);
>+	}
>+
>+	g_object_unref (vol_root);
>+	g_free (vol_uri);
>+
>+	return TRUE;
>+}
>+
>+static
>+gboolean destroy (NstPlugin *plugin){
>+	g_list_free (vol);

You're leaking all the objects in the list. Each one of them needs a g_object_unref(). Use g_list_foreach for that.
Comment 10 Maxim Ermilov 2009-01-14 01:06:52 UTC
Created attachment 126398 [details] [review]
corrected patch
Comment 11 Bastien Nocera 2009-01-15 11:36:08 UTC
Comment on attachment 126398 [details] [review]
corrected patch

<snip>

>+static GtkWidget*
>+get_contacts_widget (NstPlugin *plugin)
>+{
>+	GtkListStore *store;
>+	GtkWidget *cb;
>+	GList *l;
>+	GtkTreeIter iter;
>+	GtkCellRenderer *text_renderer, *icon_renderer;
>+
>+	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_ICON);
>+
>+	for (l = volumes; l != NULL; l = g_list_next (l)) {

I prefer l = l->next instead of g_list_next().

>+
>+		gtk_list_store_append (store, &iter);
>+		gtk_list_store_set (store, &iter, 0, g_mount_get_name (l->data), 1,
>+				    g_mount_get_icon (l->data), -1);
>+	}
>+
>+	cb = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
>+
>+	text_renderer = gtk_cell_renderer_text_new ();
>+	icon_renderer = gtk_cell_renderer_pixbuf_new ();
>+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), icon_renderer, FALSE);
>+	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), text_renderer, TRUE);
>+
>+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cb), text_renderer, "text", 0,  NULL);
>+	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (cb), icon_renderer, "gicon", 1,  NULL);
>+	gtk_combo_box_set_active (GTK_COMBO_BOX (cb), 0);

You're not updating the combobox selection when volumes are added or removed. You should listen to the "volume-added", "volume-changed" and "volume-removed" signals.

>+static gboolean
>+copy_fobject (GFile* source, GFile* dst)
>+{
>+	GFileEnumerator* en;
>+	GFileInfo* info;
>+	GError *err = NULL;
>+	gchar *file_name = g_file_get_basename (source);
>+	GFile *dest = g_file_get_child (dst, file_name);

Again, please no assignments during declaration.

>+	if (g_file_query_file_type (source, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) {
>+		gboolean ret = g_file_copy (source, dest, G_FILE_COPY_NONE, NULL, NULL, NULL, NULL);

Same again.

>+	while ((info = g_file_enumerator_next_file (en, NULL, &err)) != NULL) {
>+		const gchar *name = g_file_info_get_name (G_FILE_INFO (info));

Same.

>+		if (name != NULL) {
>+			GFile *child = g_file_get_child (source, name);

And again.

>+		if (g_uri_parse_scheme (l->data) == NULL) source = g_file_new_for_path (l->data);
>+		else source = g_file_new_for_uri (l->data);

Please indent properly. You're leaking the output of g_uri_parse_scheme().
Comment 12 Maxim Ermilov 2009-01-16 00:10:00 UTC
Created attachment 126547 [details] [review]
corrected patch

add listen to the "mount-added", "mount-changed" and "mount-removed" && remove assignments during declaration.
Comment 13 Bastien Nocera 2009-01-19 18:11:07 UTC
+		gchar* scheme;

Use char, not gchar (everywhere)

+		if (scheme == NULL) source = g_file_new_for_path (l->data);
+		else source = g_file_new_for_uri (l->data);

Still the broken identation

+	g_signal_connect_swapped (G_OBJECT (vol_monitor), "mount-removed", G_CALLBACK (get_contacts_widget), plugin);
+	g_signal_connect_swapped (G_OBJECT (vol_monitor), "mount-added", G_CALLBACK (get_contacts_widget), plugin);
+	g_signal_connect_swapped (G_OBJECT (vol_monitor), "mount-changed", G_CALLBACK (get_contacts_widget), plugin);

Sorry about misleading you to use the volume-* signals.

This isn't quite good enough. We know which mount was added/removed, so we should remove just that one from the liststore, or add just that one, otherwise if the user removes a device when nautilus-sendto is running, then the user's selection will be lost. You were also leaking a ListStore for every change, as you weren't destroying the old one.

The rest looks alright.
Comment 14 Maxim Ermilov 2009-01-21 15:14:12 UTC
Created attachment 126924 [details] [review]
corrected patch
Comment 15 Bastien Nocera 2009-02-06 15:10:16 UTC
Made a few changes to your patch and committed. FYI, the changes were:
- no need to use the UUID (and some devices won't have any), just compare the mounts instead
- Remove use of "magic" numbers, use an enum for the column numbers instead
- No need to have a list replicating the same data as the GtkListStore, just use the list store itself

2009-02-06  Bastien Nocera  <hadess@hadess.net>

        * configure.in:
        * src/plugins/removable-devices/Makefile.am:
        * src/plugins/removable-devices/removable-devices.c
        (cb_mount_removed), (cb_mount_changed), (cb_mount_added), (init),
        (get_contacts_widget), (copy_fobject), (send_files), (destroy):
        Patch from Maxim Ermilov <zaspire@rambler.ru> adding a "Removable
        Devices" plugin (Closes: #453758)


Please test and open a new bug if it doesn't work as expected.