Refilter header view in a way it doesn't lock ui.
[modest] / src / widgets / modest-header-view.c
index 2b521a9..c28cfb0 100644 (file)
@@ -128,6 +128,7 @@ static gboolean      modest_header_view_on_expose_event (GtkTreeView *header_vie
 
 static void         on_notify_style (GObject *obj, GParamSpec *spec, gpointer userdata);
 static void         update_style (ModestHeaderView *self);
+static void         modest_header_view_refilter_by_chunks (ModestHeaderView *self);
 
 typedef enum {
        HEADER_VIEW_NON_EMPTY,
@@ -189,6 +190,10 @@ struct _ModestHeaderViewPrivate {
        gboolean filter_date_range;
        time_t date_range_start;
        time_t date_range_end;
+
+       guint refilter_handler_id;
+       GtkTreeModel *filtered_model;
+       GtkTreeIter refilter_iter;
 };
 
 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
@@ -206,6 +211,12 @@ struct _HeadersCountChangedHelper {
 
 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
 
+#define _HEADER_VIEW_SUBJECT_FOLD "_subject_modest_header_view"
+#define _HEADER_VIEW_FROM_FOLD "_from_modest_header_view"
+#define _HEADER_VIEW_TO_FOLD "_to_modest_header_view"
+#define _HEADER_VIEW_CC_FOLD "_cc_modest_header_view"
+#define _HEADER_VIEW_BCC_FOLD "_bcc_modest_header_view"
+
 enum {
        HEADER_SELECTED_SIGNAL,
        HEADER_ACTIVATED_SIGNAL,
@@ -373,7 +384,7 @@ remove_all_columns (ModestHeaderView *obj)
 gboolean
 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
 {
-       GtkTreeModel *sortable;
+       GtkTreeModel *sortable, *filter_model;
        GtkTreeViewColumn *column=NULL;
        GtkTreeSelection *selection = NULL;
        GtkCellRenderer *renderer_header,
@@ -477,7 +488,11 @@ modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, Tn
 
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
        gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       sortable = NULL;
+       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
+       }
 
        /* Add new columns */
        for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
@@ -653,6 +668,9 @@ modest_header_view_init (ModestHeaderView *obj)
        priv->selection_changed_handler = 0;
        priv->acc_removed_handler = 0;
 
+       priv->filtered_model = NULL;
+       priv->refilter_handler_id = 0;
+
        /* Sort parameters */
        for (j=0; j < 2; j++) {
                for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
@@ -678,6 +696,12 @@ modest_header_view_dispose (GObject *obj)
        self = MODEST_HEADER_VIEW(obj);
        priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
 
+       if (priv->refilter_handler_id > 0) {
+               g_source_remove (priv->refilter_handler_id);
+               priv->refilter_handler_id = 0;
+               priv->filtered_model = NULL;
+       }
+
 #ifdef MODEST_TOOLKIT_HILDON2
        if (priv->live_search_timeout > 0) {
                g_source_remove (priv->live_search_timeout);
@@ -1173,6 +1197,7 @@ modest_header_view_set_folder_intern (ModestHeaderView *self,
        priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
 
        headers = TNY_LIST (tny_gtk_header_list_model_new ());
+       tny_gtk_header_list_model_set_update_in_batches (TNY_GTK_HEADER_LIST_MODEL (headers), 300);
        tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (headers), priv->show_latest);
 
        /* Start the monitor in the callback of the
@@ -1192,18 +1217,18 @@ modest_header_view_set_folder_intern (ModestHeaderView *self,
                                              set_folder_intern_get_headers_async_cb,
                                              NULL, self);
 
-       /* Create a tree model filter to hide and show rows for cut operations  */
-       filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (headers), NULL);
-       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
-                                               filter_row, self, NULL);
-       g_object_unref (headers);
-
        /* Init filter_row function to examine empty status */
        priv->status  = HEADER_VIEW_INIT;
 
        /* Create sortable model */
-       sortable = gtk_tree_model_sort_new_with_model (filter_model);
-       g_object_unref (filter_model);
+       sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (headers));
+       g_object_unref (headers);
+
+       /* Create a tree model filter to hide and show rows for cut operations  */
+       filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (sortable), NULL);
+       gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
+                                               filter_row, self, NULL);
+       g_object_unref (sortable);
 
        /* install our special sorting functions */
        cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
@@ -1230,9 +1255,9 @@ modest_header_view_set_folder_intern (ModestHeaderView *self,
        }
 
        /* Set new model */
-       gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
+       gtk_tree_view_set_model (GTK_TREE_VIEW (self), filter_model);
        modest_header_view_notify_observers (self, sortable, tny_folder_get_id (folder));
-       g_object_unref (sortable);
+       g_object_unref (filter_model);
 
        /* Free */
        g_list_free (cols);
@@ -1244,7 +1269,7 @@ modest_header_view_sort_by_column_id (ModestHeaderView *self,
                                      GtkSortType sort_type)
 {
        ModestHeaderViewPrivate *priv = NULL;
-       GtkTreeModel *sortable = NULL;
+       GtkTreeModel *sortable = NULL, *filter_model = NULL;
        TnyFolderType type;
 
        g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
@@ -1252,7 +1277,10 @@ modest_header_view_sort_by_column_id (ModestHeaderView *self,
 
        /* Get model and private data */
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
+       }
 
        /* Sort tree model */
        type  = modest_tny_folder_guess_folder_type (priv->folder);
@@ -2152,11 +2180,6 @@ current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
 static gboolean
 header_match_string (TnyHeader *header, gchar **words)
 {
-       gchar *subject;
-       gchar *cc;
-       gchar *bcc;
-       gchar *to;
-       gchar *from;
        gchar *subject_fold;
        gchar *cc_fold;
        gchar *bcc_fold;
@@ -2166,32 +2189,75 @@ header_match_string (TnyHeader *header, gchar **words)
        gchar **current_word;
        gboolean found;
 
-       subject = tny_header_dup_subject (header);
-       cc = tny_header_dup_cc (header);
-       bcc = tny_header_dup_bcc (header);
-       to = tny_header_dup_to (header);
-       from = tny_header_dup_from (header);
-
-       subject_fold = g_utf8_casefold (subject, -1);
-       g_free (subject);
-       bcc_fold = g_utf8_casefold (bcc, -1);
-       g_free (bcc);
-       cc_fold = g_utf8_casefold (cc, -1);
-       g_free (cc);
-       to_fold = g_utf8_casefold (to, -1);
-       g_free (to);
-       from_fold = g_utf8_casefold (from, -1);
-       g_free (from);
+       subject_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD);
+       if (subject_fold == NULL) {
+               gchar *subject;
+               subject = tny_header_dup_subject (header);
+               if (subject != NULL) {
+                       subject_fold = subject?g_utf8_casefold (subject, -1):NULL;
+                       g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_SUBJECT_FOLD,
+                                               subject_fold, (GDestroyNotify) g_free);
+               }
+               g_free (subject);
+       }
+
+       from_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD);
+       if (from_fold == NULL) {
+               gchar *from;
+               from = tny_header_dup_from (header);
+               if (from != NULL) {
+                       from_fold = from?g_utf8_casefold (from, -1):NULL;
+                       g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_FROM_FOLD,
+                                               from_fold, (GDestroyNotify) g_free);
+               }
+               g_free (from);
+       }
+
+       to_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_TO_FOLD);
+       if (to_fold == NULL) {
+               gchar *to;
+               to = tny_header_dup_to (header);
+               if (to != NULL) {
+                       to_fold = to?g_utf8_casefold (to, -1):NULL;
+                       g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_TO_FOLD,
+                                               to_fold, (GDestroyNotify) g_free);
+               }
+               g_free (to);
+       }
+
+       cc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_CC_FOLD);
+       if (cc_fold == NULL) {
+               gchar *cc;
+               cc = tny_header_dup_cc (header);
+               if (cc != NULL) {
+                       cc_fold = cc?g_utf8_casefold (cc, -1):NULL;
+                       g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_CC_FOLD,
+                                               cc_fold, (GDestroyNotify) g_free);
+               }
+               g_free (cc);
+       }
+
+       bcc_fold = g_object_get_data (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD);
+       if (bcc_fold == NULL) {
+               gchar *bcc;
+               bcc = tny_header_dup_bcc (header);
+               if (bcc != NULL) {
+                       bcc_fold = bcc?g_utf8_casefold (bcc, -1):NULL;
+                       g_object_set_data_full (G_OBJECT (header), _HEADER_VIEW_BCC_FOLD,
+                                               bcc_fold, (GDestroyNotify) g_free);
+               }
+               g_free (bcc);
+       }
 
        found = TRUE;
 
        for (current_word = words; *current_word != NULL; current_word++) {
 
-               if ((subject && g_strstr_len (subject_fold, -1, *current_word))
-                   || (cc && g_strstr_len (cc_fold, -1, *current_word))
-                   || (bcc && g_strstr_len (bcc_fold, -1, *current_word))
-                   || (to && g_strstr_len (to_fold, -1, *current_word))
-                   || (from && g_strstr_len (from_fold, -1, *current_word))) {
+               if ((subject_fold && g_strstr_len (subject_fold, -1, *current_word))
+                   || (cc_fold && g_strstr_len (cc_fold, -1, *current_word))
+                   || (bcc_fold && g_strstr_len (bcc_fold, -1, *current_word))
+                   || (to_fold && g_strstr_len (to_fold, -1, *current_word))
+                   || (from_fold && g_strstr_len (from_fold, -1, *current_word))) {
                        found = TRUE;
                } else {
                        found = FALSE;
@@ -2199,12 +2265,6 @@ header_match_string (TnyHeader *header, gchar **words)
                }
        }
 
-       g_free (subject_fold);
-       g_free (cc_fold);
-       g_free (bcc_fold);
-       g_free (to_fold);
-       g_free (from_fold);
-
        return found;
 }
 
@@ -2227,12 +2287,10 @@ filter_row (GtkTreeModel *model,
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
 
        /* Get header from model */
-       gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &value);
-       flags = (TnyHeaderFlags) g_value_get_int (&value);
-       g_value_unset (&value);
        gtk_tree_model_get_value (model, iter, TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &value);
        header = (TnyHeader *) g_value_get_object (&value);
        g_value_unset (&value);
+       flags = tny_header_get_flags (header);
 
        /* Get message id from header (ensure is a valid id) */
        if (!header) {
@@ -2337,20 +2395,17 @@ _clear_hidding_filter (ModestHeaderView *header_view)
 void
 modest_header_view_refilter (ModestHeaderView *header_view)
 {
-       GtkTreeModel *model, *sortable = NULL;
+       GtkTreeModel *filter_model = NULL;
        ModestHeaderViewPrivate *priv = NULL;
 
        g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
        priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
 
        /* Hide cut headers */
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
-       if (GTK_IS_TREE_MODEL_SORT (sortable)) {
-               model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
-               if (GTK_IS_TREE_MODEL_FILTER (model)) {
-                       priv->status = HEADER_VIEW_INIT;
-                       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
-               }
+       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
+       if (GTK_IS_TREE_MODEL_FILTER (filter_model)) {
+               priv->status = HEADER_VIEW_INIT;
+               modest_header_view_refilter_by_chunks (header_view);
        }
 }
 
@@ -2694,11 +2749,11 @@ modest_header_view_set_show_latest (ModestHeaderView *header_view,
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
        priv->show_latest = show_latest;
 
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
-       if (GTK_IS_TREE_MODEL_SORT (sortable)) {
-               filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
-               if (GTK_IS_TREE_MODEL_FILTER (filter)) {
-                       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+       filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
+       if (GTK_IS_TREE_MODEL_FILTER (filter)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+               if (GTK_IS_TREE_MODEL_SORT (sortable)) {
+                       model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
                        if (model) {
                                tny_gtk_header_list_model_set_show_latest (TNY_GTK_HEADER_LIST_MODEL (model), priv->show_latest);
                        }
@@ -2716,11 +2771,11 @@ modest_header_view_get_show_latest (ModestHeaderView *header_view)
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
 
        result = priv->show_latest;
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
-       if (GTK_IS_TREE_MODEL_SORT (sortable)) {
-               filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
-               if (GTK_IS_TREE_MODEL_FILTER (filter)) {
-                       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+       filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
+       if (GTK_IS_TREE_MODEL_FILTER (filter)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+               if (GTK_IS_TREE_MODEL_SORT (sortable)) {
+                       model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
                        if (model) {
                                result = tny_gtk_header_list_model_get_show_latest (TNY_GTK_HEADER_LIST_MODEL (model));
                        }
@@ -2742,11 +2797,11 @@ modest_header_view_get_not_latest (ModestHeaderView *header_view)
        if (priv->show_latest == 0)
                return 0;
 
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
-       if (GTK_IS_TREE_MODEL_SORT (sortable)) {
-               filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
-               if (GTK_IS_TREE_MODEL_FILTER (filter)) {
-                       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+       filter = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
+       if (GTK_IS_TREE_MODEL_FILTER (filter)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+               if (GTK_IS_TREE_MODEL_SORT (sortable)) {
+                       model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
                        if (model) {
                                not_latest = MAX (0, tny_list_get_length (TNY_LIST (model)) - priv->show_latest);
                        }
@@ -2845,11 +2900,11 @@ on_live_search_refilter (HildonLiveSearch *livesearch,
        }
 
        model = NULL;
-       sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
-       if (GTK_IS_TREE_MODEL_SORT (sortable)) {
-               filter = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
-               if (GTK_IS_TREE_MODEL_FILTER (filter)) {
-                       model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+       filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       if (GTK_IS_TREE_MODEL_FILTER (filter)) {
+               sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter));
+               if (GTK_IS_TREE_MODEL_SORT (sortable)) {
+                       model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (sortable));
                }
        }
 
@@ -2876,3 +2931,72 @@ modest_header_view_setup_live_search (ModestHeaderView *self)
        return priv->live_search;
 }
 #endif
+
+static gboolean
+refilter_idle_handler (gpointer userdata)
+{
+       ModestHeaderView *self = MODEST_HEADER_VIEW (userdata);
+       ModestHeaderViewPrivate *priv;
+       GtkTreeModel *filter_model;
+       GtkTreeModel *filtered_model;
+       gint i;
+       gboolean has_more;
+
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
+
+       if (filtered_model != priv->filtered_model) {
+               priv->refilter_handler_id = 0;
+               priv->filtered_model = NULL;
+               return FALSE;
+       }
+
+       if (!gtk_tree_model_sort_iter_is_valid (GTK_TREE_MODEL_SORT (filtered_model), &(priv->refilter_iter))) {
+               priv->refilter_handler_id = 0;
+               priv->filtered_model = NULL;
+               modest_header_view_refilter_by_chunks (self);
+               return FALSE;
+       }
+
+       i = 0;
+       do {
+               GtkTreePath *path;
+               path = gtk_tree_model_get_path (priv->filtered_model, &(priv->refilter_iter));
+               gtk_tree_model_row_changed (priv->filtered_model, path, &(priv->refilter_iter));
+               gtk_tree_path_free (path);
+               i++;
+
+               has_more = gtk_tree_model_iter_next (priv->filtered_model, &(priv->refilter_iter));
+       } while (i < 100 && has_more);
+
+       if (has_more) {
+               return TRUE;
+       } else {
+               priv->filtered_model = NULL;
+               priv->refilter_handler_id = 0;
+               return FALSE;
+       }
+}
+
+static void
+modest_header_view_refilter_by_chunks (ModestHeaderView *self)
+{
+       ModestHeaderViewPrivate *priv;
+       GtkTreeModel *filter_model;
+
+       g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+
+       if (priv->refilter_handler_id > 0) {
+               g_source_remove (priv->refilter_handler_id);
+               priv->refilter_handler_id = 0;
+       }
+
+       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+       priv->filtered_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
+
+       if (gtk_tree_model_get_iter_first (priv->filtered_model, &(priv->refilter_iter))) {
+               priv->refilter_handler_id = g_idle_add (refilter_idle_handler, self);
+       }
+}