Do ignore empty recipients when checking names
[modest] / src / modest-tny-local-folders-account.c
index eecb30c..3410305 100644 (file)
  */
 
 
+#include <string.h>
+#include <stdio.h>
 #include <config.h>
 #include <glib/gi18n.h>
-
+#include <tny-error.h>
 #include <modest-tny-local-folders-account.h>
 #include <modest-tny-outbox-account.h>
 #include <modest-tny-folder.h>
+#include <tny-camel-folder.h>
 #include <tny-merge-folder.h>
 #include <tny-simple-list.h>
-
-#include <string.h>
-#include <stdio.h>
+#include <tny-gtk-lockable.h>
 
 G_DEFINE_TYPE (ModestTnyLocalFoldersAccount, 
        modest_tny_local_folders_account, 
@@ -51,74 +52,70 @@ typedef struct _ModestTnyLocalFoldersAccountPrivate ModestTnyLocalFoldersAccount
 
 struct _ModestTnyLocalFoldersAccountPrivate
 {
-       GSList *list_extra_folders;
+       TnyMergeFolder *outbox_folder;
 };
 
-static void
-modest_tny_local_folders_account_dispose (GObject *object)
-{
-  if (G_OBJECT_CLASS (modest_tny_local_folders_account_parent_class)->dispose)
-    G_OBJECT_CLASS (modest_tny_local_folders_account_parent_class)->dispose (object);
-}
+static void         get_folders    (TnyFolderStore *self, 
+                                   TnyList *list, 
+                                   TnyFolderStoreQuery *query,
+                                   gboolean refresh, 
+                                   GError **err);
 
+static TnyFolder*   create_folder  (TnyFolderStore *self, 
+                                   const gchar *name, 
+                                   GError **err);
 
-static void
-modest_tny_local_folders_account_remove_all_extra_folders (ModestTnyLocalFoldersAccount *store)
-{
-       ModestTnyLocalFoldersAccountPrivate *priv = 
-               TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (store);
-               
-       GSList *iter = priv->list_extra_folders;
-       while (iter)
-       {
-               TnyFolder *folder = (TnyFolder*)iter->data;
-               if (folder) {
-                       g_object_unref (folder);
-                       iter->data = NULL;
-               }
-                       
-               iter = g_slist_next (iter);
-       }
-       
-       g_slist_free (priv->list_extra_folders);
-       priv->list_extra_folders = NULL;
-}
+enum {
+       OUTBOX_DELETED_SIGNAL,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
 
 static void
 modest_tny_local_folders_account_finalize (GObject *object)
 {
-       G_OBJECT_CLASS (modest_tny_local_folders_account_parent_class)->finalize (object);
-  
-       ModestTnyLocalFoldersAccount *self = 
-               MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (object);
-               
-       modest_tny_local_folders_account_remove_all_extra_folders (self);
-}
-
-static void
-get_folders (TnyFolderStore *self, TnyList *list, TnyFolderStoreQuery *query, GError **err);
+       ModestTnyLocalFoldersAccountPrivate *priv;
 
-static void 
-get_folders_async (TnyFolderStore *self, TnyList *list, TnyGetFoldersCallback callback, TnyFolderStoreQuery *query, TnyStatusCallback status_callback, gpointer user_data);
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (object);
+       if (priv->outbox_folder) {
+               g_object_unref (priv->outbox_folder);
+               priv->outbox_folder = NULL;
+       }
+       G_OBJECT_CLASS (modest_tny_local_folders_account_parent_class)->finalize (object);
+}
 
 static void
 modest_tny_local_folders_account_class_init (ModestTnyLocalFoldersAccountClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
-       
+
        g_type_class_add_private (klass, sizeof (ModestTnyLocalFoldersAccountPrivate));
-       
-       object_class->dispose = modest_tny_local_folders_account_dispose;
+
        object_class->finalize = modest_tny_local_folders_account_finalize;
-         
+
+       /* Signals */
+
+       /* Note that this signal is removed before unsetting my own
+          reference to outbox, this means that by the time of this
+          call, modest_tny_local_folders_account_get_merged_outbox is
+          still valid. The reason is that the listeners of the signal
+          might want to do something with the outbox instance */
+       signals[OUTBOX_DELETED_SIGNAL] = g_signal_new
+       ("outbox-deleted", MODEST_TYPE_TNY_LOCAL_FOLDERS_ACCOUNT,
+       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET
+       (ModestTnyLocalFoldersAccountClass, outbox_deleted), NULL,
+       NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
        /* Override virtual functions from the parent class: */
-       TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->get_folders_func = get_folders;
-       TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->get_folders_async_func = get_folders_async;
+       TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->get_folders = get_folders;
+       TNY_CAMEL_STORE_ACCOUNT_CLASS(klass)->create_folder = create_folder;
 }
 
 static void
 modest_tny_local_folders_account_init (ModestTnyLocalFoldersAccount *self)
 {
+       /* Do nothing */
 }
 
 ModestTnyLocalFoldersAccount*
@@ -127,127 +124,200 @@ modest_tny_local_folders_account_new (void)
   return g_object_new (MODEST_TYPE_TNY_LOCAL_FOLDERS_ACCOUNT, NULL);
 }
 
-void
-modest_tny_local_folders_account_add_extra_folder (ModestTnyLocalFoldersAccount *store, 
-       TnyFolder *folder)
+/**********************************************************/
+/*          TnyCamelStoreAccount functions redefinitions  */
+/**********************************************************/
+static gboolean 
+modest_tny_local_folders_account_query_passes (TnyFolderStoreQuery *query, TnyFolder *folder)
 {
-       ModestTnyLocalFoldersAccountPrivate *priv = 
-               TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (store);
-               
-       /* Check that it isn't already in the list: */
-       GSList *exists = g_slist_find (priv->list_extra_folders, folder);
-       if (exists)
-               return;
-               
-       /* Add it: */
-       /* The reference is released in finalize: */
-       priv->list_extra_folders = g_slist_append (priv->list_extra_folders, folder);
-       g_object_ref (folder);
+       gboolean retval = FALSE;
+
+       if (query && (tny_list_get_length (tny_folder_store_query_get_items (query)) > 0)) {
+               TnyList *items = tny_folder_store_query_get_items (query);
+               TnyIterator *iterator;
+               iterator = tny_list_create_iterator (items);
+
+               while (!tny_iterator_is_done (iterator))
+               {
+                       TnyFolderStoreQueryItem *item = (TnyFolderStoreQueryItem*) tny_iterator_get_current (iterator);
+                       if (item) {
+                               TnyFolderStoreQueryOption options = tny_folder_store_query_item_get_options (item);
+                               const regex_t *regex = tny_folder_store_query_item_get_regex (item);
+
+                               if ((options & TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED) &&
+                                  tny_folder_is_subscribed (folder))
+                                       retval = TRUE;
+
+                               if ((options & TNY_FOLDER_STORE_QUERY_OPTION_UNSUBSCRIBED) &&
+                                   !(tny_folder_is_subscribed (folder)))
+                                       retval = TRUE;
+
+                               if (regex && options & TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_NAME)
+                                  if (regexec (regex, tny_folder_get_name (folder), 0, NULL, 0) == 0)
+                                       retval = TRUE;
+
+                               if (regex && options & TNY_FOLDER_STORE_QUERY_OPTION_MATCH_ON_ID)
+                                 if (regexec (regex, tny_folder_get_id (folder), 0, NULL, 0) == 0)
+                                       retval = TRUE;
+
+                               g_object_unref (G_OBJECT (item));
+                       }
+
+                       tny_iterator_next (iterator);
+               }
+                
+               g_object_unref (G_OBJECT (iterator));
+               g_object_unref (G_OBJECT (items));
+       } else
+               retval = TRUE;
+
+       return retval;
 }
 
 static void
-get_folders (TnyFolderStore *self, TnyList *list, TnyFolderStoreQuery *query, GError **err)
+get_folders (TnyFolderStore *self, 
+            TnyList *list, 
+            TnyFolderStoreQuery *query, 
+            gboolean refresh, 
+            GError **err)
 {
-       ModestTnyLocalFoldersAccountPrivate *priv = 
-               TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
-               
+       TnyCamelStoreAccountClass *parent_class;
+       ModestTnyLocalFoldersAccountPrivate *priv;
+
        /* Call the base class implementation: */
-       TnyCamelStoreAccountClass *parent_class = g_type_class_peek_parent (
-               MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
-       parent_class->get_folders_func (self, list, query, err);
+       parent_class = g_type_class_peek_parent (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
+       parent_class->get_folders (self, list, query, refresh, err);
        
-       /* Add our extra folders: */
-       GSList *iter = priv->list_extra_folders;
-       while (iter)
-       {
-               TnyFolder *folder = TNY_FOLDER (iter->data);
-               if (folder)
-                       tny_list_append (list, G_OBJECT (folder));
-                       
-               iter = g_slist_next (iter);
-       }
+       /* Add our extra folder only if it passes the query */
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
+               
+       if (priv->outbox_folder && 
+           modest_tny_local_folders_account_query_passes (query, TNY_FOLDER (priv->outbox_folder)))
+               tny_list_prepend (list, G_OBJECT (priv->outbox_folder));
 }
 
-static void 
-get_folders_async (TnyFolderStore *self, TnyList *list, TnyGetFoldersCallback callback, TnyFolderStoreQuery *query, TnyStatusCallback status_callback, gpointer user_data)
+static TnyFolder*
+create_folder (TnyFolderStore *self, 
+              const gchar *name, 
+              GError **err)
 {
+       TnyCamelStoreAccountClass *parent_class;
+
+       parent_class = g_type_class_peek_parent (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
+
+       /* If the folder name is been used by our extra folders */
+       if (modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (self), name)) {
+               g_set_error (err, TNY_ERROR_DOMAIN, 
+                            TNY_SERVICE_ERROR_FOLDER_CREATE,
+                            "Folder name already in use");
+               return NULL;
+       }
+
        /* Call the base class implementation: */
-       TnyCamelStoreAccountClass *parent_class = g_type_class_peek_parent (
-               MODEST_TNY_LOCAL_FOLDERS_ACCOUNT_GET_CLASS (self));
-       parent_class->get_folders_async_func (self, list, callback, query, status_callback, user_data);
+       return parent_class->create_folder (self, name, err);
 }
 
-static void
-add_account_folders_to_merged_folder (TnyAccount *account, TnyMergeFolder* merge_folder)
+/*****************************/
+/*      Public methods       */ 
+/*****************************/
+gboolean
+modest_tny_local_folders_account_folder_name_in_use (ModestTnyLocalFoldersAccount *self,
+                                                    const gchar *name)
 {
-       const gchar* account_id = tny_account_get_id (account);
-       const gboolean is_actual_local_folders_account = account_id && 
-               (strcmp (account_id, MODEST_ACTUAL_LOCAL_FOLDERS_ACCOUNT_ID) == 0);
-               
-       TnyList *list_outbox_folders = tny_simple_list_new ();
-       tny_folder_store_get_folders (TNY_FOLDER_STORE (account), 
-               list_outbox_folders, NULL, NULL);
-               
-       TnyIterator*  iter =  tny_list_create_iterator (list_outbox_folders);
-       while (!tny_iterator_is_done (iter))
-       {
-               TnyFolder *folder = TNY_FOLDER (tny_iterator_get_current (iter));
-               
-               if (folder) {
-                       gboolean add = TRUE;
-                       /* TODO: Do not add outboxes that are inside local-folders/, 
-                        * because these are just left-over from earlier Modest versions 
-                        * that put the outbox there: */
-                       if (is_actual_local_folders_account) {
-                               const TnyFolderType type = modest_tny_folder_get_local_folder_type (folder);
-                               if (type == TNY_FOLDER_TYPE_OUTBOX) {
-                                       add = FALSE;
-                               }
+       ModestTnyLocalFoldersAccountPrivate *priv;
+       gchar *down_name;
+       const gchar *type_name;
+       gboolean retval;
+       
+       /* Check that we're not trying to create/rename any folder
+          with the same name that our OUTBOX, DRAFT, SENT */
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
+       down_name = g_utf8_strdown (name, strlen (name));
+
+       type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_OUTBOX);
+       if (!strcmp (type_name, down_name)) {
+               retval = TRUE;
+       } else {
+               type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_DRAFTS);
+               if (!strcmp (type_name, down_name)) {
+                       retval = TRUE;
+               } else {
+                       type_name = modest_local_folder_info_get_type_name (TNY_FOLDER_TYPE_SENT);
+                       if (!strcmp (type_name, down_name)) {
+                               retval = TRUE;
+                       } else {
+                               retval = FALSE;
                        }
-                       
-                       if (add)
-                               tny_merge_folder_add_folder (merge_folder, folder);
-                               
-                       g_object_unref (folder);        
                }
-               
-               tny_iterator_next (iter);
        }
-       
-       g_object_unref (list_outbox_folders);
+       g_free (down_name);
+
+       return retval;
 }
 
-void modest_tny_local_folders_account_add_merged_outbox_folders (ModestTnyLocalFoldersAccount *self, 
-       GSList *accounts)
+void
+modest_tny_local_folders_account_add_folder_to_outbox (ModestTnyLocalFoldersAccount *self, 
+                                                      TnyFolder *per_account_outbox)
 {
-       modest_tny_local_folders_account_remove_all_extra_folders (self);
-       
-       /* All per-account outbox folders are merged into one folders
-        * so that they appear as one outbox to the user: */
-       TnyMergeFolder *merged_outbox = TNY_MERGE_FOLDER (tny_merge_folder_new());
-       
-       GSList *iter = accounts;
-       while (iter)
-       {
-               TnyAccount *account = TNY_ACCOUNT (iter->data);
-               if (account) {
-                       /* Add both outbox account and local-folders account folders
-                        * to our one combined account:
-                        */
-                       if (MODEST_IS_TNY_OUTBOX_ACCOUNT (account)) {
-                               /* Add the folder to the merged folder.
-                                * We will add it later to the virtual local-folders store: */
-                               add_account_folders_to_merged_folder (account, merged_outbox);
-                       }
-               }
-          
-               iter = g_slist_next (iter);
+       ModestTnyLocalFoldersAccountPrivate *priv;
+
+       g_return_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self));
+       g_return_if_fail (TNY_IS_FOLDER (per_account_outbox));
+
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
+
+       /* Create on-demand */
+       if (!priv->outbox_folder) {
+               priv->outbox_folder = (TnyMergeFolder *) 
+                       tny_merge_folder_new_with_ui_locker (_("mcen_me_folder_outbox"), 
+                                                            tny_gtk_lockable_new ());
+               /* Set type to outbox */
+               tny_merge_folder_set_folder_type (priv->outbox_folder, 
+                                                 TNY_FOLDER_TYPE_OUTBOX);
        }
-       
-       /* Add the merged outbox folder to the virtual local-folders store: */
-       modest_tny_local_folders_account_add_extra_folder (self, TNY_FOLDER(merged_outbox));
-       g_object_unref (merged_outbox);
-       merged_outbox = NULL;
+
+       /* Add outbox to the global OUTBOX folder */
+       tny_merge_folder_add_folder (priv->outbox_folder, per_account_outbox);
+}
+
+void 
+modest_tny_local_folders_account_remove_folder_from_outbox (ModestTnyLocalFoldersAccount *self, 
+                                                           TnyFolder *per_account_outbox)
+{
+       ModestTnyLocalFoldersAccountPrivate *priv;
+       TnyList *merged_folders = NULL;
+
+       g_return_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self));
+       g_return_if_fail (TNY_IS_FOLDER (per_account_outbox));
+
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
+
+       /* Remove outbox from the global OUTBOX folder */
+       tny_merge_folder_remove_folder (priv->outbox_folder, per_account_outbox);
+
+       /* If there is no folder in the outbox the delete it */
+       merged_folders = tny_simple_list_new ();
+       tny_merge_folder_get_folders (priv->outbox_folder, merged_folders);
+       if (tny_list_get_length (merged_folders) == 0) {
+               /* Emit signal */
+               g_signal_emit ((GObject *)self, signals[OUTBOX_DELETED_SIGNAL], 0);
+
+               /* Unref my own reference */
+               g_object_unref (priv->outbox_folder);
+               priv->outbox_folder = NULL;
+       }
+       g_object_unref (merged_folders);
 }
 
+TnyFolder *
+modest_tny_local_folders_account_get_merged_outbox (ModestTnyLocalFoldersAccount *self)
+{
+       ModestTnyLocalFoldersAccountPrivate *priv;
+       g_return_val_if_fail (MODEST_IS_TNY_LOCAL_FOLDERS_ACCOUNT (self), NULL);
 
+       priv = TNY_LOCAL_FOLDERS_ACCOUNT_GET_PRIVATE (self);
+       if (priv->outbox_folder)
+               return g_object_ref (priv->outbox_folder);
+       else
+               return NULL;
+}