[gthumb/ext: 21/79] implemented the paste command



commit f9adcaa5fbb00937e910b07d593287a2b6600dd4
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Tue Jun 23 23:59:00 2009 +0200

    implemented the paste command

 extensions/catalogs/callbacks.c                |    4 +-
 extensions/catalogs/dlg-add-to-catalog.c       |  166 +++++++---------
 extensions/catalogs/gth-catalog.c              |   87 +++++----
 extensions/catalogs/gth-catalog.h              |   43 ++--
 extensions/catalogs/gth-file-source-catalogs.c |  132 ++++++++++++
 extensions/file_manager/actions.c              |  256 +++++++++++++++++++++++-
 extensions/file_manager/actions.h              |    2 +-
 extensions/file_manager/callbacks.c            |   18 +-
 extensions/search/gth-search-task.c            |    2 +-
 gthumb/gio-utils.c                             |  127 +------------
 gthumb/gio-utils.h                             |    7 +-
 gthumb/gth-browser.c                           |  158 +---------------
 gthumb/gth-browser.h                           |    5 +-
 gthumb/gth-file-data.c                         |   14 +-
 gthumb/gth-file-source-vfs.c                   |  184 +++++++++++++++++
 gthumb/gth-file-source.c                       |   74 +++++++-
 gthumb/gth-file-source.h                       |   12 +
 gthumb/gth-thumb-loader.c                      |   14 +-
 18 files changed, 837 insertions(+), 468 deletions(-)
---
diff --git a/extensions/catalogs/callbacks.c b/extensions/catalogs/callbacks.c
index 7591b43..e9d63bc 100644
--- a/extensions/catalogs/callbacks.c
+++ b/extensions/catalogs/callbacks.c
@@ -160,7 +160,7 @@ catalogs__gth_browser_update_sensitivity_cb (GthBrowser *browser)
 	g_object_set (action, "sensitive", sensitive, NULL);
 
 	action = gtk_action_group_get_action (data->actions, "Edit_RemoveFromCatalog");
-	sensitive = (n_selected > 0) && GTH_IS_FILE_SOURCE_CATALOGS (gth_browser_get_file_source (browser));
+	sensitive = (n_selected > 0) && GTH_IS_FILE_SOURCE_CATALOGS (gth_browser_get_location_source (browser));
 	g_object_set (action, "sensitive", sensitive, NULL);
 }
 
@@ -232,7 +232,7 @@ catalogs__gth_browser_load_location_after_cb (GthBrowser   *browser,
 
 	data = g_object_get_data (G_OBJECT (browser), BROWSER_DATA_KEY);
 
-	if (GTH_IS_FILE_SOURCE_CATALOGS (gth_browser_get_file_source (browser))) {
+	if (GTH_IS_FILE_SOURCE_CATALOGS (gth_browser_get_location_source (browser))) {
 		if (data->vfs_merge_id == 0) {
 			GError *error = NULL;
 
diff --git a/extensions/catalogs/dlg-add-to-catalog.c b/extensions/catalogs/dlg-add-to-catalog.c
index 05e5952..54108eb 100644
--- a/extensions/catalogs/dlg-add-to-catalog.c
+++ b/extensions/catalogs/dlg-add-to-catalog.c
@@ -45,14 +45,12 @@ typedef struct {
 
 
 static void
-destroy_cb (GtkWidget  *widget, 
+destroy_cb (GtkWidget  *widget,
 	    DialogData *data)
 {
 	g_free (data->buffer);
-	if (data->catalog != NULL)
-		g_object_unref (data->catalog);
-	if (data->gio_file != NULL)
-		g_object_unref (data->gio_file);
+	_g_object_unref (data->catalog);
+	_g_object_unref (data->gio_file);
 	_g_object_list_unref (data->files);
 	_g_object_unref (data->selected_catalog);
 	g_object_unref (data->builder);
@@ -66,80 +64,72 @@ get_selected_catalog (DialogData *data)
 {
 	GthFileData *file_data = NULL;
 	GFile       *file;
-	
+
 	file = gth_folder_tree_get_selected_or_parent (GTH_FOLDER_TREE (data->source_tree));
 	if (file != NULL) {
 		GthFileSource *file_source;
 		GFileInfo     *info;
-		
+
 		file_source = gth_main_get_file_source (file);
 		info = gth_file_source_get_file_info (file_source, file);
-		if (g_file_info_get_attribute_boolean (info, "gthumb::no-child")) 
+		if (g_file_info_get_attribute_boolean (info, "gthumb::no-child"))
 			file_data = gth_file_data_new (file, info);
-			
+
 		g_object_unref (info);
 		g_object_unref (file);
 	}
-	
+
 	return file_data;
 }
 
 
-static void 
+static void
 catalog_save_done_cb (void     *buffer,
 		      gsize     count,
 		      GError   *error,
 		      gpointer  user_data)
 {
 	DialogData *data = user_data;
-	
+
 	if (error != NULL) {
 		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not add the files to the catalog"), &error);
 		return;
 	}
-	
-	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("view_destination_checkbutton")))) 
+
+	gth_monitor_folder_changed (gth_main_get_default_monitor (),
+				    data->selected_catalog->file,
+				    data->files,
+				    GTH_MONITOR_EVENT_CREATED);
+
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("view_destination_checkbutton"))))
 		gth_browser_go_to (data->browser, data->selected_catalog->file);
-	
+
 	gtk_widget_destroy (data->dialog);
 }
 
 
-static void 
-catalog_buffer_ready_cb (void     *buffer,
-			 gsize     count,
-			 GError   *error,
-			 gpointer  user_data)
+static void
+catalog_ready_cb (GObject  *catalog,
+		  GError   *error,
+		  gpointer  user_data)
 {
 	DialogData *data = user_data;
 	GList      *scan;
-	
-	if (error != NULL) {
-		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not add the files to the catalog"), &error);
-		return;
-	}
-	
-	data->catalog = gth_hook_invoke_get ("gth-catalog-load-from-data", buffer);	
-	if (data->catalog == NULL) {
-		error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, _("Invalid file format"));
-		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not add the files to the catalog"), &error);
-		return;
-	}
-	
-	gth_catalog_load_from_data (data->catalog, buffer, count, &error);
+
 	if (error != NULL) {
 		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not add the files to the catalog"), &error);
 		return;
 	}
 
+	data->catalog = (GthCatalog *) catalog;
+
 	for (scan = data->files; scan; scan = scan->next) {
 		GthFileData *file_to_add = scan->data;
-		
 		gth_catalog_insert_file (data->catalog, -1, file_to_add->file);
 	}
-	
+
 	data->buffer = gth_catalog_to_data (data->catalog, &data->length);
-	g_write_file_async (data->gio_file, 
+	g_write_file_async (data->gio_file,
 			    data->buffer,
 			    data->length,
 			    G_PRIORITY_DEFAULT,
@@ -153,21 +143,13 @@ static void
 add_button_clicked_cb (GtkWidget  *widget,
 		       DialogData *data)
 {
-	GthFileSource *catalog_source;
-	
 	data->selected_catalog = get_selected_catalog (data);
-	if (data->selected_catalog == NULL) 
+	if (data->selected_catalog == NULL)
 		return;
-	
-	catalog_source = g_object_new (GTH_TYPE_FILE_SOURCE_CATALOGS, NULL);
-	data->gio_file = gth_file_source_to_gio_file (catalog_source, data->selected_catalog->file);
-	g_load_file_async (data->gio_file, 
-			   G_PRIORITY_DEFAULT, 
-			   NULL, 
-			   catalog_buffer_ready_cb,
-			   data);
-
-	g_object_unref (catalog_source);
+
+	gth_catalog_load_from_file (data->selected_catalog->file,
+				    catalog_ready_cb,
+				    data);
 }
 
 
@@ -182,12 +164,12 @@ source_tree_open_cb (GthFolderTree *folder_tree,
 
 static void
 source_tree_selection_changed_cb (GtkTreeSelection *treeselection,
-                                  gpointer          user_data) 
+                                  gpointer          user_data)
 {
 	DialogData  *data = user_data;
 	GthFileData *file_data;
-	
-	file_data = get_selected_catalog (data);	
+
+	file_data = get_selected_catalog (data);
 	gtk_widget_set_sensitive (GTK_WIDGET (GET_WIDGET ("add_button")), file_data != NULL);
 	_g_object_unref (file_data);
 }
@@ -197,27 +179,27 @@ static GFile *
 get_catalog_parent (GFile *selected_parent)
 {
 	GFile *parent = NULL;
-	
+
 	if (selected_parent != NULL) {
 		GthFileSource *file_source;
 		GFileInfo     *info;
-		
+
 		file_source = gth_main_get_file_source (selected_parent);
 		info = gth_file_source_get_file_info (file_source, selected_parent);
-		if ((g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) &&  
+		if ((g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) &&
 		    ! g_file_info_get_attribute_boolean (info, "gthumb::no-child"))
-		{ 
+		{
 			parent = g_file_dup (selected_parent);
 		}
 		else
 			parent = g_file_get_parent (selected_parent);
-			
+
 		g_object_unref (info);
 		g_object_unref (file_source);
 	}
 	else
 		parent = g_file_new_for_uri ("catalog:///");
-		
+
 	return parent;
 }
 
@@ -232,57 +214,57 @@ new_catalog_button_clicked_cb (GtkWidget  *widget,
 	GFile         *gio_parent;
 	GError        *error;
 	GFile         *gio_file;
-	
+
 	selected_parent = gth_folder_tree_get_selected_or_parent (GTH_FOLDER_TREE (data->source_tree));
 	if (selected_parent != NULL) {
 		GthFileSource *file_source;
 		GFileInfo     *info;
-	
+
 		file_source = gth_main_get_file_source (selected_parent);
 		info = gth_file_source_get_file_info (file_source, selected_parent);
 		if (g_file_info_get_attribute_boolean (info, "gthumb::no-child"))
 			parent = g_file_get_parent (selected_parent);
 		else
 			parent = g_file_dup (selected_parent);
-			
+
 		g_object_unref (info);
 		g_object_unref (file_source);
 	}
 	else
 		parent = g_file_new_for_uri ("catalog:///");
-	
+
 	file_source = gth_main_get_file_source (parent);
 	gio_parent = gth_file_source_to_gio_file (file_source, parent);
 	gio_file = _g_file_create_unique (gio_parent, _("New Catalog"), ".catalog", &error);
 	if (gio_file != NULL) {
-		GFile        *file;		
+		GFile        *file;
 		GList        *list;
 		GFileInfo    *info;
 		GthFileData  *file_data;
 		GList        *file_data_list;
 
-		file = gth_catalog_file_from_gio_file (gio_file, NULL);		
+		file = gth_catalog_file_from_gio_file (gio_file, NULL);
 		info = gth_file_source_get_file_info (file_source, file);
 		file_data = gth_file_data_new (file, info);
 		file_data_list = g_list_prepend (NULL, file_data);
 		gth_folder_tree_add_children (GTH_FOLDER_TREE (data->source_tree), parent, file_data_list);
 		gth_folder_tree_start_editing (GTH_FOLDER_TREE (data->source_tree), file);
-		
-		list = g_list_prepend (NULL, g_object_ref (file));	
+
+		list = g_list_prepend (NULL, g_object_ref (file));
 		gth_monitor_folder_changed (gth_main_get_default_monitor (),
 					    parent,
 					    list,
 					    GTH_MONITOR_EVENT_CREATED);
-		
-		_g_object_list_unref (list);		
+
+		_g_object_list_unref (list);
 		g_list_free (file_data_list);
 		g_object_unref (file_data);
-		g_object_unref (info);	
+		g_object_unref (info);
 		g_object_unref (file);
 	}
 	else
 		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not create the catalog"), &error);
-		
+
 	g_object_unref (gio_file);
 	g_object_unref (gio_parent);
 	g_object_unref (file_source);
@@ -298,7 +280,7 @@ new_library_button_clicked_cb (GtkWidget  *widget,
 	GFile  *parent;
 	GFile  *new_library;
 	GError *error = NULL;
-	
+
 	display_name = _gtk_request_dialog_run (GTK_WINDOW (data->dialog),
 						GTK_DIALOG_MODAL,
 						_("Enter the library name: "),
@@ -308,29 +290,29 @@ new_library_button_clicked_cb (GtkWidget  *widget,
 						_("C_reate"));
 	if (display_name == NULL)
 		return;
-	
+
 	selected_catalog = gth_folder_tree_get_selected (GTH_FOLDER_TREE (data->source_tree));
-	parent = get_catalog_parent (selected_catalog);	
+	parent = get_catalog_parent (selected_catalog);
 	new_library = g_file_get_child_for_display_name (parent, display_name, &error);
-	
+
 	if ((new_library != NULL) && (strchr (display_name, '/') != NULL)) {
 		error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME, _("The name \"%s\" is not valid because it contains the character \"/\". " "Please use a different name."), display_name);
 		g_object_unref (new_library);
 		new_library = NULL;
 	}
-	
-	if (error == NULL) {	
+
+	if (error == NULL) {
 		GFile *gio_file;
-		
+
 		gio_file = gth_file_source_to_gio_file (data->file_source, new_library);
 		g_file_make_directory (new_library, NULL, &error);
-		
+
 		g_object_unref (gio_file);
 	}
-	
+
 	if (error != NULL)
 		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not create a new library"), &error);
-	
+
 	if (new_library != NULL)
 		g_object_unref (new_library);
 	g_object_unref (parent);
@@ -346,25 +328,25 @@ dlg_add_to_catalog (GthBrowser *browser,
 	DialogData       *data;
 	GFile            *base;
 	GtkTreeSelection *selection;
-	 
+
 	data = g_new0 (DialogData, 1);
 	data->browser = browser;
-	data->builder = _gtk_builder_new_from_file ("add-to-catalog.ui", "catalogs"); 
+	data->builder = _gtk_builder_new_from_file ("add-to-catalog.ui", "catalogs");
 	data->files = _g_object_list_ref (list);
 	data->file_source = g_object_new (GTH_TYPE_FILE_SOURCE_CATALOGS, NULL);
-	
+
 	data->dialog = _gtk_builder_get_widget (data->builder, "add_to_catalog_dialog");
-	
-	base = g_file_new_for_uri ("catalog:///");	
+
+	base = g_file_new_for_uri ("catalog:///");
 	data->source_tree = gth_source_tree_new (base);
 	g_object_unref (base);
-	
+
 	gtk_widget_show (data->source_tree);
-	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("catalog_list_scrolled_window")), data->source_tree); 
+	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("catalog_list_scrolled_window")), data->source_tree);
 
-	gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("catalogs_label")), data->source_tree);	
+	gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("catalogs_label")), data->source_tree);
 	gtk_widget_set_sensitive (GTK_WIDGET (GET_WIDGET ("add_button")), FALSE);
-	
+
 	/* Set the signals handlers. */
 
 	g_signal_connect (G_OBJECT (data->dialog),
@@ -391,13 +373,13 @@ dlg_add_to_catalog (GthBrowser *browser,
 			  "clicked",
 			  G_CALLBACK (new_library_button_clicked_cb),
 			  data);
-		  		  
+
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->source_tree));
 	g_signal_connect (selection,
 			  "changed",
 			  G_CALLBACK (source_tree_selection_changed_cb),
 			  data);
-	
+
 	/* run dialog. */
 
 	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (browser));
diff --git a/extensions/catalogs/gth-catalog.c b/extensions/catalogs/gth-catalog.c
index 8db743f..1d78b1a 100644
--- a/extensions/catalogs/gth-catalog.c
+++ b/extensions/catalogs/gth-catalog.c
@@ -411,10 +411,10 @@ catalog_file_info_ready_cb (GObject      *source_object,
 
 
 static void
-catalog_buffer_ready_cb (void     *buffer,
-			 gsize     count,
-			 GError   *error,
-			 gpointer  user_data)
+list__catalog_buffer_ready_cb (void     *buffer,
+			       gsize     count,
+			       GError   *error,
+			       gpointer  user_data)
 {
 	ListData   *list_data = user_data;
 	GthCatalog *catalog = list_data->catalog;
@@ -473,7 +473,7 @@ gth_catalog_list_async (GthCatalog           *catalog,
 	g_load_file_async (catalog->priv->file,
 			   G_PRIORITY_DEFAULT,
 			   catalog->priv->cancellable,
-			   catalog_buffer_ready_cb,
+			   list__catalog_buffer_ready_cb,
 			   list_data);
 }
 
@@ -671,43 +671,54 @@ gth_catalog_get_display_name (GFile *file)
 }
 
 
-#if 0
-GthCatalogType
-gth_catalog_data_get_type (const void *buffer,
-			   gsize       count)
-{
-	GthCatalogType  type = GTH_CATALOG_TYPE_INVALID;
-	char           *text_buffer;
+/* -- gth_catalog_load_from_file --*/
 
-	text_buffer = (char*) buffer;
-	if (strncmp (text_buffer, "<?xml ", 6) == 0) {
-		DomDocument *doc;
-		GError      *error = NULL;
-		DomElement  *root;
-
-		doc = dom_document_new ();
-		if (! dom_document_load (doc, text_buffer, count, &error)) {
-			g_warning ("%s", error->message);
-			g_clear_error (&error);
-		}
 
-		root = DOM_ELEMENT (doc)->first_child;
-		if (g_strcmp0 (root->tag_name, "catalog") == 0)
-			type = GTH_CATALOG_TYPE_CATALOG;
-		else if (g_strcmp0 (root->tag_name, "search") == 0)
-			type = GTH_CATALOG_TYPE_SEARCH;
+typedef struct {
+	ReadyCallback ready_func;
+	gpointer      user_data;
+} LoadData;
 
-		g_object_unref (doc);
-	}
-	else {
-		/* Old catalog format */
 
-		if (strncmp (text_buffer, "# Search", 8) == 0)
-			type = GTH_CATALOG_TYPE_SEARCH;
-		else
-			type = GTH_CATALOG_TYPE_CATALOG;
+static void
+load__catalog_buffer_ready_cb (void     *buffer,
+			       gsize     count,
+			       GError   *error,
+			       gpointer  user_data)
+{
+	LoadData   *load_data = user_data;
+	GthCatalog *catalog = NULL;
+
+	if (error == NULL) {
+		catalog = gth_hook_invoke_get ("gth-catalog-load-from-data", buffer);
+		if (catalog != NULL)
+			gth_catalog_load_from_data (catalog, buffer, count, &error);
 	}
 
-	return type;
+	load_data->ready_func (G_OBJECT (catalog), error, load_data->user_data);
+
+	g_free (load_data);
+}
+
+
+void
+gth_catalog_load_from_file (GFile         *file,
+			    ReadyCallback  ready_func,
+			    gpointer       user_data)
+{
+	LoadData *load_data;
+	GFile    *gio_file;
+
+	load_data = g_new0 (LoadData, 1);
+	load_data->ready_func = ready_func;
+	load_data->user_data = user_data;
+
+	gio_file = gth_catalog_file_to_gio_file (file);
+	g_load_file_async (gio_file,
+			   G_PRIORITY_DEFAULT,
+			   NULL,
+			   load__catalog_buffer_ready_cb,
+			   load_data);
+
+	g_object_unref (gio_file);
 }
-#endif
diff --git a/extensions/catalogs/gth-catalog.h b/extensions/catalogs/gth-catalog.h
index b8cb2f2..8120579 100644
--- a/extensions/catalogs/gth-catalog.h
+++ b/extensions/catalogs/gth-catalog.h
@@ -19,7 +19,7 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
  */
- 
+
 #ifndef GTH_CATALOG_H
 #define GTH_CATALOG_H
 
@@ -54,14 +54,14 @@ struct _GthCatalog
 struct _GthCatalogClass
 {
 	GObjectClass __parent_class;
-	
+
 	/*< virtual functions >*/
-	
+
 	void   (*load_from_data) (GthCatalog  *catalog,
 				  const void  *buffer,
 				  gsize        count,
-				  GError     **error);  
-	char * (*to_data)        (GthCatalog  *catalog, 
+				  GError     **error);
+	char * (*to_data)        (GthCatalog  *catalog,
 		     	          gsize       *length);
 };
 
@@ -73,14 +73,14 @@ typedef void (*CatalogReadyCallback) (GthCatalog *catalog,
 GType         gth_catalog_get_type       (void) G_GNUC_CONST;
 GthCatalog *  gth_catalog_new            (void);
 void          gth_catalog_set_file       (GthCatalog           *catalog,
-					  GFile                *file);				  
-GFile *       gth_catalog_get_file       (GthCatalog           *catalog);			  
+					  GFile                *file);
+GFile *       gth_catalog_get_file       (GthCatalog           *catalog);
 void          gth_catalog_load_from_data (GthCatalog           *catalog,
 					  const void           *buffer,
 					  gsize                 count,
-					  GError              **error);  
-char *        gth_catalog_to_data        (GthCatalog           *catalog, 
-		     			  gsize                *length);					  					     
+					  GError              **error);
+char *        gth_catalog_to_data        (GthCatalog           *catalog,
+		     			  gsize                *length);
 void          gth_catalog_set_file_list  (GthCatalog           *catalog,
 					  GList                *file_list);
 GList *       gth_catalog_get_file_list  (GthCatalog           *catalog);
@@ -94,18 +94,21 @@ void          gth_catalog_list_async     (GthCatalog           *catalog,
 					  GCancellable         *cancellable,
 					  CatalogReadyCallback  ready_func,
 					  gpointer              user_data);
-void          gth_catalog_cancel         (GthCatalog           *catalog);		     
+void          gth_catalog_cancel         (GthCatalog           *catalog);
 
 /* utils */
 
 GFile *        gth_catalog_get_base                (void);
-GFile *        gth_catalog_file_to_gio_file        (GFile      *file);
-GFile *        gth_catalog_file_from_gio_file      (GFile      *file,
-						    GFile      *catalog);
-GFile *        gth_catalog_file_from_relative_path (const char *name,
-						    const char *file_extension);
-char *         gth_catalog_get_relative_path       (GFile      *file);
-GIcon *        gth_catalog_get_icon                (GFile      *file);
-char *         gth_catalog_get_display_name        (GFile      *file);
-						   
+GFile *        gth_catalog_file_to_gio_file        (GFile         *file);
+GFile *        gth_catalog_file_from_gio_file      (GFile         *file,
+						    GFile         *catalog);
+GFile *        gth_catalog_file_from_relative_path (const char    *name,
+						    const char    *file_extension);
+char *         gth_catalog_get_relative_path       (GFile         *file);
+GIcon *        gth_catalog_get_icon                (GFile         *file);
+char *         gth_catalog_get_display_name        (GFile         *file);
+void           gth_catalog_load_from_file          (GFile         *file,
+						    ReadyCallback  ready_func,
+						    gpointer       user_data);
+
 #endif /*GTH_CATALOG_H*/
diff --git a/extensions/catalogs/gth-file-source-catalogs.c b/extensions/catalogs/gth-file-source-catalogs.c
index 6c0be92..8e739d0 100644
--- a/extensions/catalogs/gth-file-source-catalogs.c
+++ b/extensions/catalogs/gth-file-source-catalogs.c
@@ -379,6 +379,137 @@ gth_file_source_catalogs_read_attributes (GthFileSource *file_source,
 }
 
 
+/* -- gth_file_source_vfs_copy -- */
+
+
+typedef struct {
+	GthFileSourceVfs *file_source;
+	GFile            *destination;
+	GList            *file_list;
+	ReadyCallback     callback;
+	gpointer          user_data;
+	GList            *files;
+	GthCatalog       *catalog;
+	char             *buffer;
+	gsize             length;
+} CopyOpData;
+
+
+static void
+copy_op_data_free (CopyOpData *cod)
+{
+	g_free (cod->buffer);
+	_g_object_unref (cod->catalog);
+	_g_object_list_unref (cod->files);
+	_g_object_list_unref (cod->file_list);
+	g_object_unref (cod->destination);
+	g_object_unref (cod->file_source);
+	g_free (cod);
+}
+
+
+static void
+catalog_save_done_cb (void     *buffer,
+		      gsize     count,
+		      GError   *error,
+		      gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+
+	if (error == NULL)
+		gth_monitor_folder_changed (gth_main_get_default_monitor (),
+				            cod->destination,
+				            cod->files,
+					    GTH_MONITOR_EVENT_CREATED);
+
+	cod->callback (G_OBJECT (cod->file_source), error, cod->user_data);
+	copy_op_data_free (cod);
+}
+
+
+static void
+catalog_ready_cb (GObject  *catalog,
+		  GError   *error,
+		  gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+	GList      *scan;
+	GFile      *gio_file;
+
+	if (error != NULL) {
+		cod->callback (G_OBJECT (cod->file_source), error, cod->user_data);
+		copy_op_data_free (cod);
+		return;
+	}
+
+	cod->catalog = (GthCatalog *) catalog;
+
+	for (scan = cod->files; scan; scan = scan->next)
+		gth_catalog_insert_file (cod->catalog, -1, (GFile *) scan->data);
+
+	cod->buffer = gth_catalog_to_data (cod->catalog, &cod->length);
+	gio_file = gth_catalog_file_to_gio_file (cod->destination);
+	g_write_file_async (gio_file,
+			    cod->buffer,
+			    cod->length,
+			    G_PRIORITY_DEFAULT,
+			    NULL,
+			    catalog_save_done_cb,
+			    cod);
+
+	g_object_unref (gio_file);
+}
+
+
+static void
+copy__file_list_info_ready_cb (GList    *files,
+			       GError   *error,
+			       gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+	GList      *scan;
+
+	for (scan = files; scan; scan = scan->next) {
+		GthFileData *file_data = scan->data;
+
+		switch (g_file_info_get_file_type (file_data->info)) {
+		case G_FILE_TYPE_REGULAR:
+		case G_FILE_TYPE_SYMBOLIC_LINK:
+			cod->files = g_list_prepend (cod->files, g_object_ref (file_data->file));
+			break;
+		default:
+			break;
+		}
+	}
+	cod->files = g_list_reverse (cod->files);
+
+	gth_catalog_load_from_file (cod->destination, catalog_ready_cb, cod);
+}
+
+
+static void
+gth_file_source_catalogs_copy (GthFileSource *file_source,
+			       GFile         *destination,
+			       GList         *file_list, /* GFile * list */
+			       ReadyCallback  callback,
+			       gpointer       data)
+{
+	CopyOpData *cod;
+
+	cod = g_new0 (CopyOpData, 1);
+	cod->file_source = g_object_ref (file_source);
+	cod->destination = g_object_ref (destination);
+	cod->file_list = _g_object_list_ref (file_list);
+	cod->callback = callback;
+	cod->user_data = data;
+
+	g_query_info_async (cod->file_list,
+			    G_FILE_ATTRIBUTE_STANDARD_TYPE,
+			    GTH_FILE_SOURCE_CATALOGS (file_source)->priv->cancellable,
+			    copy__file_list_info_ready_cb,
+			    cod);
+}
+
 static void
 gth_file_source_catalogs_cancel (GthFileSource *file_source)
 {
@@ -423,6 +554,7 @@ gth_file_source_catalogs_class_init (GthFileSourceCatalogsClass *class)
 	file_source_class->get_file_info = gth_file_source_catalogs_get_file_info;
 	file_source_class->list = gth_file_source_catalogs_list;
 	file_source_class->read_attributes = gth_file_source_catalogs_read_attributes;
+	file_source_class->copy = gth_file_source_catalogs_copy;
 	file_source_class->cancel = gth_file_source_catalogs_cancel;
 }
 
diff --git a/extensions/file_manager/actions.c b/extensions/file_manager/actions.c
index 9704d85..46f187e 100644
--- a/extensions/file_manager/actions.c
+++ b/extensions/file_manager/actions.c
@@ -41,11 +41,147 @@ gth_browser_action_rename_folder (GtkAction  *action,
 }
 
 
+/* -- gth_browser_clipboard_copy / gth_browser_clipboard_cut -- */
+
+
+typedef struct {
+	char     **uris;
+	int        n_uris;
+	gboolean   cut;
+} ClipboardData;
+
+
+static char *
+clipboard_data_convert_to_text (ClipboardData *clipboard_data,
+				gboolean       formatted,
+				gsize         *len)
+{
+	GString *uris;
+	int      i;
+
+	if (formatted)
+		uris = g_string_new (clipboard_data->cut ? "cut" : "copy");
+	else
+		uris = g_string_new (NULL);
+
+	for (i = 0; i < clipboard_data->n_uris; i++) {
+		if (formatted) {
+			g_string_append_c (uris, '\n');
+			g_string_append (uris, clipboard_data->uris[i]);
+		}
+		else {
+			GFile *file;
+			char  *name;
+
+			if (i > 0)
+				g_string_append_c (uris, '\n');
+			file = g_file_new_for_uri (clipboard_data->uris[i]);
+			name = g_file_get_parse_name (file);
+			g_string_append (uris, name);
+
+			g_free (name);
+			g_object_unref (file);
+		}
+	}
+
+	if (len != NULL)
+		*len = uris->len;
+
+	return g_string_free (uris, FALSE);
+}
+
+
+static void
+clipboard_get_cb (GtkClipboard     *clipboard,
+		  GtkSelectionData *selection_data,
+		  guint             info,
+		  gpointer          user_data_or_owner)
+{
+	ClipboardData *clipboard_data = user_data_or_owner;
+
+	if (gtk_targets_include_uri (&selection_data->target, 1)) {
+		gtk_selection_data_set_uris (selection_data, clipboard_data->uris);
+	}
+	else if (gtk_targets_include_text (&selection_data->target, 1)) {
+		char  *str;
+		gsize  len;
+
+		str = clipboard_data_convert_to_text (clipboard_data, FALSE, &len);
+		gtk_selection_data_set_text (selection_data, str, len);
+		g_free (str);
+	}
+	else if (selection_data->target == GNOME_COPIED_FILES) {
+		char  *str;
+		gsize  len;
+
+		str = clipboard_data_convert_to_text (clipboard_data, TRUE, &len);
+		gtk_selection_data_set (selection_data, GNOME_COPIED_FILES, 8, (guchar *) str, len);
+		g_free (str);
+	}
+}
+
+
+static void
+clipboard_clear_cb (GtkClipboard *clipboard,
+		    gpointer      user_data_or_owner)
+{
+	ClipboardData *data = user_data_or_owner;
+
+	g_strfreev (data->uris);
+	g_free (data);
+}
+
+
+static void
+_gth_browser_clipboard_copy_or_cut (GthBrowser *browser,
+				    gboolean    cut)
+{
+	ClipboardData  *data;
+	GList          *items;
+	GList          *file_list;
+	GtkTargetList  *target_list;
+	GtkTargetEntry *targets;
+	int             n_targets;
+	GList          *scan;
+	int             i;
+
+	items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (browser)));
+	file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (browser)), items);
+
+	data = g_new0 (ClipboardData, 1);
+	data->cut = cut;
+	data->n_uris = g_list_length (file_list);
+	data->uris = g_new (char *, data->n_uris + 1);
+	for (scan = file_list, i = 0; scan; scan = scan->next, i++) {
+		GthFileData *file_data = scan->data;
+		data->uris[i] = g_file_get_uri (file_data->file);
+	}
+	data->uris[data->n_uris] = NULL;
+
+	target_list = gtk_target_list_new (NULL, 0);
+	gtk_target_list_add (target_list, GNOME_COPIED_FILES, 0, 0);
+	gtk_target_list_add_uri_targets (target_list, 0);
+	gtk_target_list_add_text_targets (target_list, 0);
+	targets = gtk_target_table_new_from_list (target_list, &n_targets);
+	gtk_clipboard_set_with_data (gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (browser)), GDK_SELECTION_CLIPBOARD),
+				     targets,
+				     n_targets,
+				     clipboard_get_cb,
+				     clipboard_clear_cb,
+				     data);
+
+	gtk_target_list_unref (target_list);
+	gtk_target_table_free (targets, n_targets);
+	_g_object_list_unref (file_list);
+	_gtk_tree_path_list_free (items);
+}
+
+
 void
 gth_browser_activate_action_edit_cut_files (GtkAction  *action,
 					    GthBrowser *browser)
 {
-	gth_browser_clipboard_cut (browser);
+	_gth_browser_clipboard_copy_or_cut (browser, TRUE);
 }
 
 
@@ -53,15 +189,125 @@ void
 gth_browser_activate_action_edit_copy_files (GtkAction  *action,
 					     GthBrowser *browser)
 {
-	gth_browser_clipboard_copy (browser);
+	_gth_browser_clipboard_copy_or_cut (browser, FALSE);
+}
+
+
+/* -- gth_browser_clipboard_paste -- */
+
+
+typedef struct {
+	GthBrowser    *browser;
+	GFile         *destination;
+	GthFileSource *file_source;
+	GList         *files;
+	gboolean       cut;
+} PasteData;
+
+
+static void
+paste_data_free (PasteData *paste_data)
+{
+	_g_object_list_unref (paste_data->files);
+	_g_object_unref (paste_data->file_source);
+	g_object_unref (paste_data->destination);
+	g_object_unref (paste_data->browser);
+	g_free (paste_data);
+}
+
+
+static void
+paste_done_cb (GObject  *object,
+	       GError   *error,
+	       gpointer  user_data)
+{
+	PasteData  *paste_data = user_data;
+	GthBrowser *browser = paste_data->browser;
+
+	if (error != NULL) {
+		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (browser), _("Could not copy the files"), &error);
+		paste_data_free (paste_data);
+		return;
+	}
+
+	if (paste_data->cut) {
+		if (! _g_delete_files (paste_data->files, TRUE, &error))
+			_gtk_error_dialog_from_gerror_show (GTK_WINDOW (browser), _("Could not delete the files"), &error);
+	}
+
+	paste_data_free (paste_data);
+}
+
+
+static void
+clipboard_received_cb (GtkClipboard     *clipboard,
+		       GtkSelectionData *selection_data,
+		       gpointer          user_data)
+{
+	GthBrowser  *browser = user_data;
+	char       **clipboard_data;
+	PasteData   *paste_data;
+	int          i;
+
+
+	clipboard_data = g_strsplit_set ((const char *) gtk_selection_data_get_data (selection_data), "\n\r", -1);
+	if (clipboard_data[0] == NULL) {
+		g_strfreev (clipboard_data);
+		return;
+	}
+
+	paste_data = g_new0 (PasteData, 1);
+	paste_data->browser = g_object_ref (browser);
+	paste_data->destination = g_object_ref (gth_browser_get_location (browser));
+	paste_data->cut = strcmp (clipboard_data[0], "cut") == 0;
+
+	paste_data->files = NULL;
+	for (i = 1; clipboard_data[i] != NULL; i++)
+		if (strcmp (clipboard_data[i], "") != 0)
+			paste_data->files = g_list_prepend (paste_data->files, g_file_new_for_uri (clipboard_data[i]));
+	paste_data->files = g_list_reverse (paste_data->files);
+
+	paste_data->file_source = gth_main_get_file_source (gth_browser_get_location (browser));
+
+	if (paste_data->cut && ! gth_file_source_can_cut (paste_data->file_source)) {
+		GtkWidget *dialog;
+		int        response;
+
+		dialog = _gtk_message_dialog_new (GTK_WINDOW (browser),
+						  GTK_DIALOG_MODAL,
+						  GTK_STOCK_DIALOG_QUESTION,
+						  _("Could not move the files"),
+						  _("Files cannot be moved to the current location, as alternative you can choose to copy them."),
+						  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+						  GTK_STOCK_COPY, GTK_RESPONSE_OK,
+						  NULL);
+		response = gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+
+		if (response == GTK_RESPONSE_CANCEL) {
+			paste_data_free (paste_data);
+			return;
+		}
+
+		paste_data->cut = FALSE;
+	}
+
+	gth_file_source_copy (paste_data->file_source,
+			      paste_data->destination,
+			      paste_data->files,
+			      paste_done_cb,
+			      paste_data);
 }
 
 
 void
-gth_browser_activate_action_edit_paste_in_folder (GtkAction  *action,
-						  GthBrowser *browser)
+gth_browser_activate_action_edit_paste (GtkAction  *action,
+					GthBrowser *browser)
 {
-	gth_browser_clipboard_paste (browser);
+	gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (browser), GDK_SELECTION_CLIPBOARD),
+					GNOME_COPIED_FILES,
+					clipboard_received_cb,
+					browser);
 }
 
 
diff --git a/extensions/file_manager/actions.h b/extensions/file_manager/actions.h
index 89327c2..06ce4fd 100644
--- a/extensions/file_manager/actions.h
+++ b/extensions/file_manager/actions.h
@@ -31,7 +31,7 @@ DEFINE_ACTION(gth_browser_action_new_folder)
 DEFINE_ACTION(gth_browser_action_rename_folder)
 DEFINE_ACTION(gth_browser_activate_action_edit_cut_files)
 DEFINE_ACTION(gth_browser_activate_action_edit_copy_files)
-DEFINE_ACTION(gth_browser_activate_action_edit_paste_in_folder)
+DEFINE_ACTION(gth_browser_activate_action_edit_paste)
 DEFINE_ACTION(gth_browser_activate_action_edit_duplicate)
 DEFINE_ACTION(gth_browser_activate_action_edit_rename)
 DEFINE_ACTION(gth_browser_activate_action_edit_trash)
diff --git a/extensions/file_manager/callbacks.c b/extensions/file_manager/callbacks.c
index 7b1c1c5..0a1ae3c 100644
--- a/extensions/file_manager/callbacks.c
+++ b/extensions/file_manager/callbacks.c
@@ -110,7 +110,7 @@ static GtkActionEntry action_entries[] = {
 	{ "Edit_PasteInFolder", GTK_STOCK_PASTE,
 	  NULL, NULL,
 	  NULL,
-	  G_CALLBACK (gth_browser_activate_action_edit_paste_in_folder) },
+	  G_CALLBACK (gth_browser_activate_action_edit_paste) },
 	{ "Edit_Duplicate", NULL,
 	  N_("D_uplicate"), "<control><shift>D",
 	  N_("Duplicate the selected files"),
@@ -184,7 +184,7 @@ static void
 file_manager_update_ui (BrowserData *data,
 			GthBrowser  *browser)
 {
-	if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_file_source (browser))) {
+	if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_location_source (browser))) {
 		if (data->vfs_merge_id == 0) {
 			GError *local_error = NULL;
 
@@ -216,7 +216,7 @@ file_manager_update_ui (BrowserData *data,
 		data->browser_merge_id = 0;
 	}
 
-	if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_file_source (browser))
+	if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_location_source (browser))
 	    && (gth_window_get_current_page (GTH_WINDOW (browser)) == GTH_BROWSER_PAGE_BROWSER))
 	{
 		if (data->browser_vfs_merge_id == 0) {
@@ -344,17 +344,21 @@ _gth_browser_update_paste_command_sensitivity (GthBrowser   *browser,
 void
 fm__gth_browser_update_sensitivity_cb (GthBrowser *browser)
 {
-	BrowserData *data;
-	int          n_selected;
-	gboolean     sensitive;
+	BrowserData   *data;
+	GthFileSource *file_source;
+	int            n_selected;
+	gboolean       sensitive;
 
 	data = g_object_get_data (G_OBJECT (browser), BROWSER_DATA_KEY);
 	g_return_if_fail (data != NULL);
 
+	file_source = gth_browser_get_location_source (browser);
 	n_selected = gth_file_selection_get_n_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (browser)));
 
-	sensitive = n_selected > 0;
+	sensitive = (n_selected > 0) && (file_source != NULL) && gth_file_source_can_cut (file_source);
 	set_action_sensitive (data, "Edit_CutFiles", sensitive);
+
+	sensitive = (n_selected > 0) && (file_source != NULL);
 	set_action_sensitive (data, "Edit_CopyFiles", sensitive);
 	set_action_sensitive (data, "Edit_Trash", sensitive);
 	set_action_sensitive (data, "Edit_Delete", sensitive);
diff --git a/extensions/search/gth-search-task.c b/extensions/search/gth-search-task.c
index e7544bf..addd18a 100644
--- a/extensions/search/gth-search-task.c
+++ b/extensions/search/gth-search-task.c
@@ -173,7 +173,7 @@ done_func (GError   *error,
 	search_result_real_file = gth_catalog_file_to_gio_file (task->priv->search_catalog);
 	g_write_file_async (search_result_real_file,
 			    data,
-				size,
+			    size,
 			    G_PRIORITY_DEFAULT,
 			    task->priv->cancellable,
 			    save_search_result_copy_done_cb,
diff --git a/gthumb/gio-utils.c b/gthumb/gio-utils.c
index 7813c07..834c31c 100644
--- a/gthumb/gio-utils.c
+++ b/gthumb/gio-utils.c
@@ -781,129 +781,6 @@ g_directory_list_async (GFile             *directory,
 }
 
 
-/* -- g_list_items_async -- */
-
-
-static void get_items_for_current_dir (GetFileListData *gfl);
-
-
-static gboolean
-get_items_for_next_dir_idle_cb (gpointer data)
-{
-	GetFileListData *gfl = data;
-
-	g_source_remove (gfl->visit_timeout);
-	gfl->visit_timeout = 0;
-
-	gfl->current_dir = g_list_next (gfl->current_dir);
-	get_items_for_current_dir (gfl);
-
-	return FALSE;
-}
-
-
-static void
-get_items_for_current_dir_done (GList    *files,
-				GList    *dirs,
-				GError   *error,
-				gpointer  data)
-{
-	GetFileListData *gfl = data;
-
-	if (error != NULL) {
-		if (gfl->done_func)
-			gfl->done_func (NULL, NULL, error, gfl->done_data);
-		_g_string_list_free (files);
-		_g_string_list_free (dirs);
-		get_file_list_data_free (gfl);
-		return;
-	}
-
-	gfl->files = g_list_concat (gfl->files, files);
-	gfl->dirs = g_list_concat (gfl->dirs, dirs);
-
-	gfl->visit_timeout = g_idle_add (get_items_for_next_dir_idle_cb, gfl);
-}
-
-
-static void
-get_items_for_current_dir (GetFileListData *gfl)
-{
-	const char *directory_name;
-	char       *directory_uri;
-	GFile      *directory;
-
-	if (gfl->current_dir == NULL) {
-		if (gfl->done_func) {
-			/* gfl->files/gfl->dirs must be deallocated in gfl->done_func */
-			gfl->done_func (gfl->files, gfl->dirs, NULL, gfl->done_data);
-			gfl->files = NULL;
-			gfl->dirs = NULL;
-		}
-		get_file_list_data_free (gfl);
-		return;
-	}
-
-	directory_name = _g_uri_get_basename ((char*) gfl->current_dir->data);
-	if (strcmp (gfl->base_dir, "/") == 0)
-		directory_uri = g_strconcat (gfl->base_dir, directory_name, NULL);
-	else
-		directory_uri = g_strconcat (gfl->base_dir, "/", directory_name, NULL);
-
-	directory = g_file_new_for_uri (directory_uri);
-	g_directory_list_all_async (directory,
-			   	    gfl->base_dir,
-				    TRUE,
-				    gfl->cancellable,
-			   	    get_items_for_current_dir_done,
-			   	    gfl);
-
-	g_object_unref (directory);
-	g_free (directory_uri);
-}
-
-
-void
-g_list_items_async (GList             *items,
-		    const char        *base_dir,
-		    GCancellable      *cancellable,
-		    ListReadyCallback  done_func,
-		    gpointer           done_data)
-{
-	GetFileListData *gfl;
-	int              base_len;
-	GList           *scan;
-
-	g_return_if_fail (base_dir != NULL);
-
-	gfl = g_new0 (GetFileListData, 1);
-	gfl->base_dir = g_strdup (base_dir);
-	gfl->cancellable = cancellable;
-	gfl->done_func = done_func;
-	gfl->done_data = done_data;
-
-	base_len = 0;
-	if (strcmp (base_dir, "/") != 0)
-		base_len = strlen (base_dir);
-
-	for (scan = items; scan; scan = scan->next) {
-		char *path = scan->data;
-
-		/* FIXME: this is not async */
-		if (_g_uri_is_dir (path)) {
-			gfl->to_visit = g_list_prepend (gfl->to_visit, g_strdup (path));
-		}
-		else {
-			char *rel_path = g_strdup (path + base_len + 1);
-			gfl->files = g_list_prepend (gfl->files, rel_path);
-		}
-	}
-
-	gfl->current_dir = gfl->to_visit;
-	get_items_for_current_dir (gfl);
-}
-
-
 /* -- g_query_info_async -- */
 
 
@@ -1204,8 +1081,8 @@ g_copy_files_async (GList                 *sources,
 	/* add the metadata sidecars if requested */
 
 	if (flags && G_FILE_COPY_ALL_METADATA) {
-		GList      *source_sidecars = NULL;
-		GList      *destination_sidecars = NULL;
+		GList *source_sidecars = NULL;
+		GList *destination_sidecars = NULL;
 
 
 		gth_hook_invoke ("add-sidecars", sources, &source_sidecars);
diff --git a/gthumb/gio-utils.h b/gthumb/gio-utils.h
index cd42ff9..0bcf4e1 100644
--- a/gthumb/gio-utils.h
+++ b/gthumb/gio-utils.h
@@ -90,12 +90,7 @@ void   g_directory_list_async        (GFile                 *directory,
 				      GCancellable          *cancellable,
 				      ListReadyCallback      done_func,
 				      gpointer               done_data);
-void   g_list_items_async            (GList                 *items,
-				      const char            *base_dir,
-				      GCancellable          *cancellable,
-				      ListReadyCallback      done_func,
-				      gpointer               done_data);
-void   g_query_info_async            (GList                 *files,
+void   g_query_info_async            (GList                 *files,        /* GFile * list */
 				      const char            *attributes,
 				      GCancellable          *cancellable,
 				      InfoReadyCallback      ready_func,
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index eb8555f..a25a77c 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -3074,7 +3074,7 @@ gth_browser_get_file_list_view (GthBrowser *browser)
 
 
 GthFileSource *
-gth_browser_get_file_source (GthBrowser *browser)
+gth_browser_get_location_source (GthBrowser *browser)
 {
 	return browser->priv->location_source;
 }
@@ -3941,159 +3941,3 @@ gth_browser_unfullscreen (GthBrowser *browser)
 	if (browser->priv->motion_signal != 0)
 		g_signal_handler_disconnect (browser, browser->priv->motion_signal);
 }
-
-
-/* -- clipboard -- */
-
-
-typedef struct {
-	char     **uris;
-	int        n_uris;
-	gboolean   cut;
-} ClipboardData;
-
-
-static char *
-clipboard_data_convert_to_text (ClipboardData *clipboard_data,
-				gboolean       formatted,
-				gsize         *len)
-{
-	GString *uris;
-	int      i;
-
-	if (formatted)
-		uris = g_string_new (clipboard_data->cut ? "cut" : "copy");
-	else
-		uris = g_string_new (NULL);
-
-	for (i = 0; i < clipboard_data->n_uris; i++) {
-		if (formatted) {
-			g_string_append_c (uris, '\n');
-			g_string_append (uris, clipboard_data->uris[i]);
-		}
-		else {
-			GFile *file;
-			char  *name;
-
-			if (i > 0)
-				g_string_append_c (uris, '\n');
-			file = g_file_new_for_uri (clipboard_data->uris[i]);
-			name = g_file_get_parse_name (file);
-			g_string_append (uris, name);
-
-			g_free (name);
-			g_object_unref (file);
-		}
-	}
-
-	if (len != NULL)
-		*len = uris->len;
-
-	return g_string_free (uris, FALSE);
-}
-
-
-static void
-clipboard_get_cb (GtkClipboard     *clipboard,
-		  GtkSelectionData *selection_data,
-		  guint             info,
-		  gpointer          user_data_or_owner)
-{
-	ClipboardData *clipboard_data = user_data_or_owner;
-
-	if (gtk_targets_include_uri (&selection_data->target, 1)) {
-		gtk_selection_data_set_uris (selection_data, clipboard_data->uris);
-	}
-	else if (gtk_targets_include_text (&selection_data->target, 1)) {
-		char  *str;
-		gsize  len;
-
-		str = clipboard_data_convert_to_text (clipboard_data, FALSE, &len);
-		gtk_selection_data_set_text (selection_data, str, len);
-		g_free (str);
-	}
-	else if (selection_data->target == GNOME_COPIED_FILES) {
-		char  *str;
-		gsize  len;
-
-		str = clipboard_data_convert_to_text (clipboard_data, TRUE, &len);
-		gtk_selection_data_set (selection_data, GNOME_COPIED_FILES, 8, (guchar *) str, len);
-		g_free (str);
-	}
-}
-
-
-static void
-clipboard_clear_cb (GtkClipboard *clipboard,
-		    gpointer      user_data_or_owner)
-{
-	ClipboardData *data = user_data_or_owner;
-
-	g_strfreev (data->uris);
-	g_free (data);
-}
-
-
-static void
-_gth_browser_clipboard_copy_or_cut (GthBrowser *browser,
-				    gboolean    cut)
-{
-	ClipboardData  *data;
-	GList          *items;
-	GList          *file_list;
-	GtkTargetList  *target_list;
-	GtkTargetEntry *targets;
-	int             n_targets;
-	GList          *scan;
-	int             i;
-
-	items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (browser)));
-	file_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (browser)), items);
-
-	data = g_new0 (ClipboardData, 1);
-	data->cut = cut;
-	data->n_uris = g_list_length (file_list);
-	data->uris = g_new (char *, data->n_uris + 1);
-	for (scan = file_list, i = 0; scan; scan = scan->next, i++) {
-		GthFileData *file_data = scan->data;
-		data->uris[i] = g_file_get_uri (file_data->file);
-	}
-	data->uris[data->n_uris] = NULL;
-
-	target_list = gtk_target_list_new (NULL, 0);
-	gtk_target_list_add (target_list, GNOME_COPIED_FILES, 0, 0);
-	gtk_target_list_add_uri_targets (target_list, 0);
-	gtk_target_list_add_text_targets (target_list, 0);
-	targets = gtk_target_table_new_from_list (target_list, &n_targets);
-	gtk_clipboard_set_with_data (gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (browser)), GDK_SELECTION_CLIPBOARD),
-				     targets,
-				     n_targets,
-				     clipboard_get_cb,
-				     clipboard_clear_cb,
-				     data);
-
-	gtk_target_list_unref (target_list);
-	gtk_target_table_free (targets, n_targets);
-	_g_object_list_unref (file_list);
-	_gtk_tree_path_list_free (items);
-}
-
-
-void
-gth_browser_clipboard_copy (GthBrowser *browser)
-{
-	_gth_browser_clipboard_copy_or_cut (browser, FALSE);
-}
-
-
-void
-gth_browser_clipboard_cut (GthBrowser *browser)
-{
-	_gth_browser_clipboard_copy_or_cut (browser, TRUE);
-}
-
-
-void
-gth_browser_clipboard_paste (GthBrowser *browser)
-{
-}
diff --git a/gthumb/gth-browser.h b/gthumb/gth-browser.h
index 322271e..5f817be 100644
--- a/gthumb/gth-browser.h
+++ b/gthumb/gth-browser.h
@@ -90,7 +90,7 @@ GtkUIManager *   gth_browser_get_ui_manager         (GthBrowser       *browser);
 GtkWidget *      gth_browser_get_statusbar          (GthBrowser       *browser);
 GtkWidget *      gth_browser_get_file_list          (GthBrowser       *browser);
 GtkWidget *      gth_browser_get_file_list_view     (GthBrowser       *browser);
-GthFileSource *  gth_browser_get_file_source        (GthBrowser       *browser);
+GthFileSource *  gth_browser_get_location_source    (GthBrowser       *browser);
 GthFileStore *   gth_browser_get_file_store         (GthBrowser       *browser);
 GtkWidget *      gth_browser_get_folder_tree        (GthBrowser       *browser);
 void             gth_browser_get_sort_order         (GthBrowser       *browser,
@@ -144,9 +144,6 @@ void             gth_browser_show_filterbar         (GthBrowser       *browser,
 gpointer         gth_browser_get_image_preloader    (GthBrowser       *browser);
 void             gth_browser_fullscreen             (GthBrowser       *browser);
 void             gth_browser_unfullscreen           (GthBrowser       *browser);
-void             gth_browser_clipboard_copy         (GthBrowser       *browser);
-void             gth_browser_clipboard_cut          (GthBrowser       *browser);
-void             gth_browser_clipboard_paste        (GthBrowser       *browser);
 
 G_END_DECLS
 
diff --git a/gthumb/gth-file-data.c b/gthumb/gth-file-data.c
index 03ff5b4..697cebf 100644
--- a/gthumb/gth-file-data.c
+++ b/gthumb/gth-file-data.c
@@ -168,13 +168,16 @@ void
 gth_file_data_set_file (GthFileData *self,
 			GFile       *file)
 {
+	if (file != NULL)
+		g_object_ref (file);
+
 	if (self->file != NULL) {
 		g_object_unref (self->file);
 		self->file = NULL;
 	}
 
 	if (file != NULL)
-		self->file = g_object_ref (file);
+		self->file = file;
 }
 
 
@@ -182,13 +185,14 @@ void
 gth_file_data_set_info (GthFileData *self,
 			GFileInfo   *info)
 {
-	if (self->info != NULL) {
+	if (info != NULL)
+		g_object_ref (info);
+
+	if (self->info != NULL)
 		g_object_unref (self->info);
-		self->info = NULL;
-	}
 
 	if (info != NULL)
-		self->info = g_object_ref (info);
+		self->info = info;
 	else
 		self->info = g_file_info_new ();
 }
diff --git a/gthumb/gth-file-source-vfs.c b/gthumb/gth-file-source-vfs.c
index deaaae1..f9352ad 100644
--- a/gthumb/gth-file-source-vfs.c
+++ b/gthumb/gth-file-source-vfs.c
@@ -292,6 +292,188 @@ cancel (GthFileSource *file_source)
 }
 
 
+/* -- gth_file_source_vfs_copy -- */
+
+
+typedef struct {
+	GthFileSourceVfs *file_source;
+	GFile            *destination;
+	GList            *file_list;
+	ReadyCallback     callback;
+	gpointer          user_data;
+	GList            *files;
+	GList            *dirs;
+	GList            *current_dir;
+} CopyOpData;
+
+
+static void
+copy_op_data_free (CopyOpData *cod)
+{
+	g_object_unref (cod->file_source);
+	g_object_unref (cod->destination);
+	_g_object_list_unref (cod->file_list);
+	_g_object_list_unref (cod->files);
+	_g_object_list_unref (cod->dirs);
+	g_free (cod);
+}
+
+
+static void
+copy__copy_files_done (GError   *error,
+		       gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+
+	cod->callback (G_OBJECT (cod->file_source), error, cod->user_data);
+	copy_op_data_free (cod);
+}
+
+
+static void
+copy__copy_files (CopyOpData *cod)
+{
+	GList *destinations;
+	GList *scan;
+
+	destinations = NULL;
+	for (scan = cod->files; scan; scan = scan->next) {
+		GFile *source = scan->data;
+		char  *source_basename;
+
+		source_basename = g_file_get_basename (source);
+		destinations = g_list_prepend (destinations, g_file_get_child (cod->destination, source_basename));
+
+		g_free (source_basename);
+	}
+
+	g_copy_files_async  (cod->files,
+			     destinations,
+			     G_FILE_COPY_NONE,
+			     G_PRIORITY_DEFAULT,
+			     cod->file_source->priv->cancellable,
+			     NULL,
+			     NULL,
+			     copy__copy_files_done,
+			     cod);
+
+	_g_object_list_unref (destinations);
+}
+
+
+static void copy__copy_current_dir (CopyOpData *cod);
+
+
+static void
+copy__copy_current_dir_done (GError   *error,
+			     gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+
+	if (error != NULL) {
+		cod->callback (G_OBJECT (cod->file_source), error, cod->user_data);
+		copy_op_data_free (cod);
+		return;
+	}
+
+	cod->current_dir = cod->current_dir->next;
+	copy__copy_current_dir (cod);
+}
+
+
+static void
+copy__copy_current_dir (CopyOpData *cod)
+{
+	GFile *source;
+	char  *source_basename;
+	GFile *destination;
+
+	if (cod->current_dir == NULL) {
+		copy__copy_files (cod);
+		return;
+	}
+
+	source = (GFile *) cod->current_dir->data;
+	source_basename = g_file_get_basename (source);
+	destination = g_file_get_child (cod->destination, source_basename);
+
+	g_directory_copy_async (source,
+				destination,
+				G_FILE_COPY_NONE,
+				G_PRIORITY_DEFAULT,
+				cod->file_source->priv->cancellable,
+				NULL,
+				NULL,
+				copy__copy_current_dir_done,
+				cod);
+
+	g_object_unref (destination);
+	g_free (source_basename);
+}
+
+
+static void
+copy__file_list_info_ready_cb (GList    *files,
+			       GError   *error,
+			       gpointer  user_data)
+{
+	CopyOpData *cod = user_data;
+	GList      *scan;
+
+	for (scan = files; scan; scan = scan->next) {
+		GthFileData *file_data = scan->data;
+
+		switch (g_file_info_get_file_type (file_data->info)) {
+		case G_FILE_TYPE_DIRECTORY:
+			cod->dirs = g_list_prepend (cod->dirs, g_object_ref (file_data->file));
+			break;
+		case G_FILE_TYPE_REGULAR:
+		case G_FILE_TYPE_SYMBOLIC_LINK:
+			cod->files = g_list_prepend (cod->files, g_object_ref (file_data->file));
+			break;
+		default:
+			break;
+		}
+	}
+	cod->files = g_list_reverse (cod->files);
+	cod->dirs = g_list_reverse (cod->dirs);
+
+	cod->current_dir = cod->dirs;
+	copy__copy_current_dir (cod);
+}
+
+
+static void
+gth_file_source_vfs_copy (GthFileSource *file_source,
+			  GFile         *destination,
+			  GList         *file_list, /* GFile * list */
+			  ReadyCallback  callback,
+			  gpointer       data)
+{
+	CopyOpData *cod;
+
+	cod = g_new0 (CopyOpData, 1);
+	cod->file_source = g_object_ref (file_source);
+	cod->destination = g_object_ref (destination);
+	cod->file_list = _g_object_list_ref (file_list);
+	cod->callback = callback;
+	cod->user_data = data;
+
+	g_query_info_async (cod->file_list,
+			    G_FILE_ATTRIBUTE_STANDARD_TYPE,
+			    cod->file_source->priv->cancellable,
+			    copy__file_list_info_ready_cb,
+			    cod);
+}
+
+
+static gboolean
+gth_file_source_vfs_can_cut (GthFileSource *file_source)
+{
+	return TRUE;
+}
+
+
 static void
 mount_monitor_mountpoints_changed_cb (GUnixMountMonitor *monitor,
 				      gpointer           user_data)
@@ -534,6 +716,8 @@ gth_file_source_vfs_class_init (GthFileSourceVfsClass *class)
 	file_source_class->list = list;
 	file_source_class->read_attributes = read_attributes;
 	file_source_class->cancel = cancel;
+	file_source_class->copy = gth_file_source_vfs_copy;
+	file_source_class->can_cut = gth_file_source_vfs_can_cut;
 	file_source_class->monitor_entry_points = monitor_entry_points;
 	file_source_class->monitor_directory = monitor_directory;
 }
diff --git a/gthumb/gth-file-source.c b/gthumb/gth-file-source.c
index 6e08f2a..12d946a 100644
--- a/gthumb/gth-file-source.c
+++ b/gthumb/gth-file-source.c
@@ -48,7 +48,8 @@ static GObjectClass *parent_class = NULL;
 typedef enum {
 	FILE_SOURCE_OP_LIST,
 	FILE_SOURCE_OP_READ_ATTRIBUTES,
-	FILE_SOURCE_OP_RENAME
+	FILE_SOURCE_OP_RENAME,
+	FILE_SOURCE_OP_COPY
 } FileSourceOp;
 
 
@@ -77,12 +78,21 @@ typedef struct {
 
 
 typedef struct {
+	GFile         *destination;
+	GList         *file_list;
+	ReadyCallback  callback;
+	gpointer       data;
+} CopyData;
+
+
+typedef struct {
 	GthFileSource *file_source;
 	FileSourceOp   op;
 	union {
 		ListData           list;
 		ReadAttributesData read_attributes;
 		RenameData         rename;
+		CopyData           copy;
 	} data;
 } FileSourceAsyncOp;
 
@@ -101,6 +111,10 @@ file_source_async_op_free (FileSourceAsyncOp *async_op)
 		g_object_unref (async_op->data.rename.file);
 		g_object_unref (async_op->data.rename.new_file);
 		break;
+	case FILE_SOURCE_OP_COPY:
+		g_object_unref (async_op->data.copy.destination);
+		_g_object_list_unref (async_op->data.copy.file_list);
+		break;
 	}
 
 	g_free (async_op);
@@ -171,6 +185,27 @@ gth_file_source_queue_rename (GthFileSource *file_source,
 
 
 static void
+gth_file_source_queue_copy (GthFileSource *file_source,
+			    GFile          *destination,
+			    GList          *file_list,
+			    ReadyCallback   callback,
+			    gpointer        data)
+{
+	FileSourceAsyncOp *async_op;
+
+	async_op = g_new0 (FileSourceAsyncOp, 1);
+	async_op->file_source = file_source;
+	async_op->op = FILE_SOURCE_OP_COPY;
+	async_op->data.copy.destination = g_file_dup (destination);
+	async_op->data.copy.file_list = _g_file_list_dup (file_list);
+	async_op->data.copy.callback = callback;
+	async_op->data.copy.data = data;
+
+	file_source->priv->queue = g_list_append (file_source->priv->queue, async_op);
+}
+
+
+static void
 gth_file_source_exec_next_in_queue (GthFileSource *file_source)
 {
 	GList             *head;
@@ -205,6 +240,13 @@ gth_file_source_exec_next_in_queue (GthFileSource *file_source)
 					async_op->data.rename.callback,
 					async_op->data.rename.data);
 		break;
+	case FILE_SOURCE_OP_COPY:
+		gth_file_source_copy (file_source,
+				      async_op->data.copy.destination,
+				      async_op->data.copy.file_list,
+				      async_op->data.copy.callback,
+				      async_op->data.copy.data);
+		break;
 	}
 
 	file_source_async_op_free (async_op);
@@ -327,6 +369,13 @@ base_rename (GthFileSource *file_source,
 }
 
 
+static gboolean
+base_can_cut (GthFileSource *file_source)
+{
+	return FALSE;
+}
+
+
 static void
 base_monitor_entry_points (GthFileSource *file_source)
 {
@@ -377,6 +426,7 @@ gth_file_source_class_init (GthFileSourceClass *class)
 	class->read_attributes = base_read_attributes;
 	class->cancel = base_cancel;
 	class->rename = base_rename;
+	class->can_cut = base_can_cut;
 	class->monitor_entry_points = base_monitor_entry_points;
 	class->monitor_directory = base_monitor_directory;
 }
@@ -561,6 +611,28 @@ gth_file_source_rename (GthFileSource  *file_source,
 
 
 void
+gth_file_source_copy (GthFileSource  *file_source,
+		      GFile          *destination,
+		      GList          *file_list, /* GFile * list */
+		      ReadyCallback   callback,
+		      gpointer        data)
+{
+	if (gth_file_source_is_active (file_source)) {
+		gth_file_source_queue_copy (file_source, destination, file_list, callback, data);
+		return;
+	}
+	GTH_FILE_SOURCE_GET_CLASS (G_OBJECT (file_source))->copy (file_source, destination, file_list, callback, data);
+}
+
+
+gboolean
+gth_file_source_can_cut (GthFileSource *file_source)
+{
+	return GTH_FILE_SOURCE_GET_CLASS (G_OBJECT (file_source))->can_cut (file_source);
+}
+
+
+void
 gth_file_source_monitor_entry_points (GthFileSource *file_source)
 {
 	GTH_FILE_SOURCE_GET_CLASS (G_OBJECT (file_source))->monitor_entry_points (file_source);
diff --git a/gthumb/gth-file-source.h b/gthumb/gth-file-source.h
index f3270d1..a026912 100644
--- a/gthumb/gth-file-source.h
+++ b/gthumb/gth-file-source.h
@@ -88,6 +88,12 @@ struct _GthFileSourceClass
 					       GFile          *new_file,
 					       ReadyCallback   callback,
 					       gpointer        data);
+	void         (*copy)                  (GthFileSource  *file_source,
+					       GFile          *destination,
+					       GList          *file_list, /* GFile * list */
+					       ReadyCallback   callback,
+					       gpointer        data);
+	gboolean     (*can_cut)               (GthFileSource  *file_source);
 	void         (*monitor_entry_points)  (GthFileSource  *file_source);
 	void         (*monitor_directory)     (GthFileSource  *file_source,
 					       GFile          *file,
@@ -133,6 +139,12 @@ void         gth_file_source_rename                (GthFileSource  *file_source,
 						    GFile          *new_file,
 						    ReadyCallback   callback,
 						    gpointer        data);
+void         gth_file_source_copy                  (GthFileSource  *file_source,
+						    GFile          *destination,
+						    GList          *file_list, /* GFile list */
+						    ReadyCallback   callback,
+						    gpointer        data);
+gboolean     gth_file_source_can_cut               (GthFileSource  *file_source);
 void         gth_file_source_monitor_entry_points  (GthFileSource  *file_source);
 void         gth_file_source_monitor_directory     (GthFileSource  *file_source,
 						    GFile          *file,
diff --git a/gthumb/gth-thumb-loader.c b/gthumb/gth-thumb-loader.c
index 1c080bf..164c3f5 100644
--- a/gthumb/gth-thumb-loader.c
+++ b/gthumb/gth-thumb-loader.c
@@ -522,15 +522,21 @@ gth_thumb_loader_set_file (GthThumbLoader *tloader,
 	tloader->priv->file = NULL;
 
 	if (fd != NULL) {
-		GFile *real_file = NULL;
+		GFile  *real_file = NULL;
+		GError *error = NULL;
 
 		tloader->priv->file = gth_file_data_dup (fd);
 
-		real_file = _g_file_resolve_all_symlinks (tloader->priv->file->file, NULL);
+		real_file = _g_file_resolve_all_symlinks (tloader->priv->file->file, &error);
+		if (real_file == NULL) {
+			g_warning ("%s", error->message);
+			g_clear_error (&error);
+			return;
+		}
+
 		gth_file_data_set_file (tloader->priv->file, real_file);
 
-		if (real_file != NULL)
-			g_object_unref (real_file);
+		g_object_unref (real_file);
 	}
 
 	gth_image_loader_set_file_data (tloader->priv->iloader, tloader->priv->file);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]