Re: Esperimental patch to implement tny_folder_copy_async operation



On Thu, May 10, 2007 at 03:02:32PM +0200, Philip Van Hoof wrote:
> On Thu, 2007-05-10 at 14:36 +0200, Javier Fernandez wrote:
> >  TNY_FOLDER_STATUS_CODE_XFER_FOLDER = 5,
> 
> + * After this method, tny_folder_get_all_count and 
> + * tny_folder_get_unread_count are guaranteed to be correct.

FIXED.

> 
> That's incorrect: it's after the method's callback happened, that these
> numbers are guaranteed to be correct
> 
> + *             g_print ("\t%s\n", tny_header_get_subject (header));
> 
> That %s will fail with gtk-doc
> 

FIXED.

> + TNY_FOLDER_STATUS_CODE_XFER_FOLDER ...
> 
> Can you call that one: TNY_FOLDER_STATUS_CODE_COPY_FOLDER ? You are
> copying it, not transferring it
> 
> 
 DONE.


-- 
Javier Fernández García-Boente
Ingeniero en Informática                 
mailto:jfernandez igalia com                    
Igalia http://www.igalia.com
Telf. +34 981 91 39 91  
Fax.  +34 981 91 39 49
 
Index: libtinymail-camel/tny-camel-folder.c
===================================================================
--- libtinymail-camel/tny-camel-folder.c	(revision 1935)
+++ libtinymail-camel/tny-camel-folder.c	(working copy)
@@ -1211,7 +1211,6 @@
 
 	info->msg = tny_msg_receive_strategy_perform_get_msg (priv->receive_strat, info->self, info->header, &err);
 
-
 	info->cancelled = camel_operation_cancel_check (cancel);
 
 	camel_operation_unregister (cancel);
@@ -1772,7 +1771,218 @@
 
 typedef struct 
 {
+	TnyFolder *self;
+	TnyFolderStore *into;
+	gchar *new_name;
+	gboolean delete_originals;
 	GError *err;
+	gpointer user_data;
+	guint depth;
+	TnyCopyFolderCallback callback;
+	TnyStatusCallback status_callback;
+	TnySessionCamel *session;
+	TnyIdleStopper *stopper;
+	gboolean cancelled;
+} CopyFolderInfo;
+
+
+static void
+tny_camel_folder_copy_async_destroyer (gpointer thr_user_data)
+{
+	CopyFolderInfo *info = (CopyFolderInfo *) thr_user_data;
+	TnyCamelFolderPriv *priv = TNY_CAMEL_FOLDER_GET_PRIVATE (info->self);
+
+	/* thread reference */
+	_tny_camel_folder_unreason (priv);
+	g_object_unref (G_OBJECT (info->self));
+
+	if (info->err)
+		g_error_free (info->err);
+
+	_tny_session_stop_operation (info->session);
+
+	tny_idle_stopper_destroy (info->stopper);
+	info->stopper = NULL;
+
+	g_free(info->new_name);
+	g_slice_free (CopyFolderInfo, info);
+}
+
+static gboolean
+tny_camel_folder_copy_async_callback (gpointer thr_user_data)
+{
+	CopyFolderInfo *info = (CopyFolderInfo *) thr_user_data;
+
+	if (info->callback)
+		info->callback (info->self, info->into, info->new_name, info->cancelled, &info->err, info->user_data);
+
+	/* Prevent status callbacks from being called after this
+	 * (can happen because the 2 idle callbacks have different priorities)
+	 * by causing tny_idle_stopper_is_stopped() to return TRUE. */
+	tny_idle_stopper_stop (info->stopper);
+
+	return FALSE;
+}
+
+static void
+tny_camel_folder_copy_async_status (struct _CamelOperation *op, const char *what, int sofar, int oftotal, void *thr_user_data)
+{
+	CopyFolderInfo *oinfo = thr_user_data;
+	TnyProgressInfo *info = NULL;
+
+	info = tny_progress_info_new (G_OBJECT (oinfo->self), oinfo->status_callback, 
+		TNY_FOLDER_STATUS, TNY_FOLDER_STATUS_CODE_COPY_FOLDER, what, sofar, 
+		oftotal, oinfo->stopper, oinfo->user_data);
+
+	if (oinfo->depth > 0)
+	{
+		g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+			tny_progress_info_idle_func, info, 
+			tny_progress_info_destroy);
+	} else {
+		tny_progress_info_idle_func (info);
+		tny_progress_info_destroy (info);
+	}
+
+	return;
+}
+
+
+static gpointer 
+tny_camel_folder_copy_async_thread (gpointer thr_user_data)
+{
+	CopyFolderInfo *info = thr_user_data;
+	TnyFolder *self = info->self;
+	TnyCamelFolderPriv *priv = TNY_CAMEL_FOLDER_GET_PRIVATE (self);
+	TnyCamelAccountPriv *apriv = TNY_CAMEL_ACCOUNT_GET_PRIVATE (priv->account);
+	CamelException ex = CAMEL_EXCEPTION_INITIALISER;
+	GError *err = NULL;
+
+	g_static_rec_mutex_lock (priv->folder_lock);
+
+	if (!load_folder_no_lock (priv))
+	{
+		tny_camel_folder_copy_async_destroyer (info);
+		g_static_rec_mutex_unlock (priv->folder_lock);
+		g_thread_exit (NULL);
+		return NULL;
+	}
+
+	info->cancelled = FALSE;
+
+	/* start camel operations */
+	_tny_camel_account_start_camel_operation (TNY_CAMEL_ACCOUNT (priv->account), 
+		tny_camel_folder_copy_async_status, info, 
+		"Fetching summary information for new messages in folder");
+
+	/* Do work */
+	tny_camel_folder_copy (self, info->into, info->new_name, info->delete_originals, &info->err);
+
+	/* Check cancellation and fill data */
+	info->cancelled = camel_operation_cancel_check (apriv->cancel);
+	priv->cached_length = camel_folder_get_message_count (priv->folder);
+
+	if (G_LIKELY (priv->folder) && CAMEL_IS_FOLDER (priv->folder) && G_LIKELY (priv->has_summary_cap))
+		priv->unread_length = (guint)camel_folder_get_unread_message_count (priv->folder);
+
+	/* Stop operation and check errors */
+	_tny_camel_account_stop_camel_operation (TNY_CAMEL_ACCOUNT (priv->account));
+	info->err = NULL;
+	if (camel_exception_is_set (&ex))
+	{
+		g_set_error (&err, TNY_FOLDER_ERROR, 
+			TNY_FOLDER_ERROR_REFRESH,
+			camel_exception_get_description (&ex));
+		if (err != NULL)
+			info->err = g_error_copy ((const GError *) err);
+	}
+
+
+	g_static_rec_mutex_unlock (priv->folder_lock);
+
+	/* Call calback if it exists */
+	if (info->callback)
+	{
+		if (info->depth > 0)
+		{
+			g_idle_add_full (G_PRIORITY_HIGH, 
+				tny_camel_folder_copy_async_callback, 
+				info, tny_camel_folder_copy_async_destroyer);
+		} else {
+			tny_camel_folder_copy_async_callback (info);
+			tny_camel_folder_copy_async_destroyer (info);
+		}
+	} else { /* Thread reference */
+		g_object_unref (G_OBJECT (self));
+		_tny_camel_folder_unreason (priv);
+	}
+	g_thread_exit (NULL);
+
+	return NULL;
+}
+
+static void
+tny_camel_folder_copy_async (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data)
+{
+	TNY_CAMEL_FOLDER_GET_CLASS (self)->copy_async_func (self, into, new_name, del, callback, status_callback, user_data);
+	return;
+}
+
+static void
+tny_camel_folder_copy_async_default (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data)
+{
+	CopyFolderInfo *info = NULL;
+	GThread *thread = NULL;
+	GError *err = NULL;
+	TnyCamelFolderPriv *priv = TNY_CAMEL_FOLDER_GET_PRIVATE (self);
+
+	if (!_tny_session_check_operation (TNY_FOLDER_PRIV_GET_SESSION(priv), &err, 
+			TNY_FOLDER_ERROR, TNY_FOLDER_ERROR_REFRESH))
+	{
+		if (callback)
+		        callback (self, info->into, info->new_name, TRUE, &info->err, user_data);
+		g_error_free (err);
+		return;
+	}
+
+	/* Idle info for the status callback: */
+
+	info = g_slice_new (CopyFolderInfo);
+	info->into = into;
+	info->new_name = g_strdup(new_name);
+	info->delete_originals = del;
+	info->session = TNY_FOLDER_PRIV_GET_SESSION (priv);
+	info->err = NULL;
+	info->self = self;
+	info->callback = callback;
+	info->status_callback = status_callback;
+	info->user_data = user_data;
+	info->depth = g_main_depth ();
+	
+	/* Use a ref count because we do not know which of the 2 idle callbacks 
+	 * will be the last, and we can only unref self in the last callback:
+	 * This is destroyed in the idle GDestroyNotify callback.*/
+
+	info->stopper = tny_idle_stopper_new();
+
+	/* thread reference */
+	g_object_ref (G_OBJECT (self));
+	_tny_camel_folder_reason (priv);
+
+	/* This will cause the idle status callback to be called,
+	 * via _tny_camel_account_start_camel_operation,
+	 * and also calls the idle main callback: */
+
+	thread = g_thread_create (tny_camel_folder_copy_async_thread,
+			info, FALSE, NULL);
+
+	return;
+}
+
+
+typedef struct 
+{
+	GError *err;
 	TnyFolder *self;
 	TnyTransferMsgsCallback callback;
 	TnyStatusCallback status_callback;
@@ -3282,6 +3492,7 @@
 	klass->transfer_msgs_func = tny_camel_folder_transfer_msgs;
 	klass->transfer_msgs_async_func = tny_camel_folder_transfer_msgs_async;
 	klass->copy_func = tny_camel_folder_copy;
+	klass->copy_async_func = tny_camel_folder_copy_async;
 	klass->poke_status_func = tny_camel_folder_poke_status;
 	klass->add_observer_func = tny_camel_folder_add_observer;
 	klass->remove_observer_func = tny_camel_folder_remove_observer;
@@ -3341,6 +3552,7 @@
 	class->transfer_msgs_func = tny_camel_folder_transfer_msgs_default;
 	class->transfer_msgs_async_func = tny_camel_folder_transfer_msgs_async_default;
 	class->copy_func = tny_camel_folder_copy_default;
+	class->copy_async_func = tny_camel_folder_copy_async_default;
 	class->poke_status_func = tny_camel_folder_poke_status_default;
 	class->add_observer_func = tny_camel_folder_add_observer_default;
 	class->remove_observer_func = tny_camel_folder_remove_observer_default;
Index: libtinymail-camel/tny-camel-folder.h
===================================================================
--- libtinymail-camel/tny-camel-folder.h	(revision 1935)
+++ libtinymail-camel/tny-camel-folder.h	(working copy)
@@ -77,6 +77,7 @@
 	void (*transfer_msgs_func) (TnyFolder *self, TnyList *headers, TnyFolder *folder_dst, gboolean delete_originals, GError **err);
 	void (*transfer_msgs_async_func) (TnyFolder *self, TnyList *header_list, TnyFolder *folder_dst, gboolean delete_originals, TnyTransferMsgsCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 	TnyFolder* (*copy_func) (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, GError **err);
+	void (*copy_async_func) (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 	void (*poke_status_func) (TnyFolder *self);
 	void (*add_observer_func) (TnyFolder *self, TnyFolderObserver *observer);
 	void (*remove_observer_func) (TnyFolder *self, TnyFolderObserver *observer);
Index: libtinymail/tny-status.h
===================================================================
--- libtinymail/tny-status.h	(revision 1935)
+++ libtinymail/tny-status.h	(working copy)
@@ -44,6 +44,7 @@
 	TNY_FOLDER_STATUS_CODE_GET_MSG = 2,
 	TNY_GET_MSG_QUEUE_STATUS_GET_MSG = 3,
 	TNY_FOLDER_STATUS_CODE_XFER_MSGS = 4,
+	TNY_FOLDER_STATUS_CODE_COPY_FOLDER = 5,
 };
 
 struct _TnyStatus 
Index: libtinymail/tny-shared.h
===================================================================
--- libtinymail/tny-shared.h	(revision 1935)
+++ libtinymail/tny-shared.h	(working copy)
@@ -74,6 +74,7 @@
 typedef struct _TnyFolderStoreQueryItem TnyFolderStoreQueryItem;
 typedef struct _TnyFolderStoreQueryItemClass TnyFolderStoreQueryItemClass;
 typedef void (*TnyGetFoldersCallback) (TnyFolderStore *self, TnyList *list, GError **err, gpointer user_data);
+typedef void (*TnyCopyFolderCallback) (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean cancelled, GError **err, gpointer user_data);
 typedef enum _TnyFolderSignal TnyFolderSignal;
 typedef enum _TnyDeviceSignal TnyDeviceSignal;
 typedef enum _TnyAccountStoreSignal TnyAccountStoreSignal;
Index: libtinymail/tny-folder.c
===================================================================
--- libtinymail/tny-folder.c	(revision 1935)
+++ libtinymail/tny-folder.c	(working copy)
@@ -335,6 +335,87 @@
 }
 
 /**
+ * tny_folder_copy_async:
+ * @self: a #TnyFolder object
+ * @into: a #TnyFolderStore object
+ * @new_name: the new name in @into
+ * @del: whether or not to delete the original location
+ * @err: a #GError object or NULL
+ *
+ * Copies @self to @into giving the new folder the name @new_name. Returns the
+ * newly created folder in @into, which will carry the name @new_name.
+ *
+ * After the method's callback happened, tny_folder_get_all_count and 
+ * tny_folder_get_unread_count are guaranteed to be correct.
+ *
+ * If you want to use this method, it's advised to let your application 
+ * use the #GMainLoop. All Gtk+ applications have this once gtk_main () is
+ * called.
+ * 
+ * When using a #GMainLoop this method will callback using g_idle_add_full.
+ * Without a #GMainLoop, which the libtinymail-camel implementation detects
+ * using (g_main_depth > 0), the callbacks will happen in a worker thread at an
+ * unknown moment in time (check your locking in this case).
+ *
+ * When using Gtk+, the callback doesn't need the gdk_threads_enter and 
+ * gdk_threads_leave guards (because it happens in the #GMainLoop).
+ *
+ * Example:
+ * <informalexample><programlisting>
+ * static void
+ * status_update_cb (GObject *sender, TnyStatus *status, gpointer user_data)
+ * {
+ *     g_print (".");
+ * }
+ * static void
+ * frolder_copy_cb (TnyFolder *folder, TnyFolderStore *into, const gchar *new_name, gboolean cancelled, GError **err, gpointer user_data)
+ * {
+ *     if (!cancelled)
+ *     {
+ *         TnyList *headers = tny_simple_list_new ();
+ *         TnyIterator *iter;
+ *         g_print ("done\nHeaders copied into %s are:", 
+ *                tny_folder_get_name (into));
+ *         tny_folder_get_headers (into, headers, FALSE);
+ *         iter = tny_list_create_iterator (headers);
+ *         while (!tny_iterator_is_done (iter))
+ *         {
+ *             TnyHeader *header = tny_iterator_current (iter);
+ *             g_print ("%s \n", tny_header_get_subject (header));
+ *             g_object_unref (G_OBJECT (header));
+ *             tny_iterator_next (iter);
+ *         }
+ *         g_object_unref (G_OBJECT (headers));
+ *     }
+ * }
+ * TnyFolder *folder = ...
+ * TnyFolderStore *into = ...
+ * gchar *new_name = ...
+ * g_print ("Getting headers ");
+ * tny_folder_copy_async (folder, into, new_name, 
+ *          folder_copy_cb, 
+ *          status_update_cb, NULL); 
+ * </programlisting></informalexample>
+ **/
+void 
+tny_folder_copy_async (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data)
+{
+	TnyFolder *retval;
+
+#ifdef DBC /* require */
+	TnyFolderStore *test;
+	g_assert (TNY_IS_FOLDER (self));
+	g_assert (TNY_IS_FOLDER_STORE (into));
+	g_assert (new_name);
+	g_assert (strlen (new_name) > 0);
+	g_assert (TNY_FOLDER_GET_IFACE (self)->copy_func_async != NULL);
+#endif
+
+	TNY_FOLDER_GET_IFACE (self)->copy_async_func (self, into, new_name, del, callback, status_callback, user_data);
+
+}
+
+/**
  * tny_folder_get_msg_remove_strategy:
  * @self: a TnyFolder object
  *
Index: libtinymail/tny-folder.h
===================================================================
--- libtinymail/tny-folder.h	(revision 1935)
+++ libtinymail/tny-folder.h	(working copy)
@@ -109,6 +109,7 @@
 	void (*transfer_msgs_func) (TnyFolder *self, TnyList *header_list, TnyFolder *folder_dst, gboolean delete_originals, GError **err);
 	void (*transfer_msgs_async_func) (TnyFolder *self, TnyList *header_list, TnyFolder *folder_dst, gboolean delete_originals, TnyTransferMsgsCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 	TnyFolder* (*copy_func) (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, GError **err);
+	void (*copy_async_func) (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 	void (*poke_status_func) (TnyFolder *self);
 	void (*add_observer_func) (TnyFolder *self, TnyFolderObserver *observer);
 	void (*remove_observer_func) (TnyFolder *self, TnyFolderObserver *observer);
@@ -145,6 +146,7 @@
 void tny_folder_transfer_msgs (TnyFolder *self, TnyList *header_list, TnyFolder *folder_dst, gboolean delete_originals, GError **err);
 void tny_folder_transfer_msgs_async (TnyFolder *self, TnyList *header_list, TnyFolder *folder_dst, gboolean delete_originals, TnyTransferMsgsCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 TnyFolder* tny_folder_copy (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del, GError **err);
+void tny_folder_copy_async (TnyFolder *self, TnyFolderStore *into, const gchar *new_name, gboolean del,  TnyCopyFolderCallback callback, TnyStatusCallback status_callback, gpointer user_data);
 void tny_folder_poke_status (TnyFolder *self);
 void tny_folder_add_observer (TnyFolder *self, TnyFolderObserver *observer);
 void tny_folder_remove_observer (TnyFolder *self, TnyFolderObserver *observer);


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