Modified webpage: now tinymail repository is in gitorious.
[modest] / src / widgets / modest-header-view.c
index 6a2c7ae..996cbae 100644 (file)
@@ -41,7 +41,6 @@
 #include <modest-dnd.h>
 #include <modest-tny-folder.h>
 #include <modest-debug.h>
-#include <modest-main-window.h>
 #include <modest-ui-actions.h>
 #include <modest-marshal.h>
 #include <modest-text-utils.h>
@@ -52,6 +51,9 @@
 #include <modest-vbox-cell-renderer.h>
 #include <modest-datetime-formatter.h>
 #include <modest-ui-constants.h>
+#ifdef MODEST_TOOLKIT_HILDON2
+#include <hildon/hildon.h>
+#endif
 
 static void modest_header_view_class_init  (ModestHeaderViewClass *klass);
 static void modest_header_view_init        (ModestHeaderView *obj);
@@ -125,6 +127,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,
@@ -153,6 +156,10 @@ struct _ModestHeaderViewPrivate {
        guint   n_selected;
        GtkTreeRowReference *autoselect_reference;
        ModestHeaderViewFilter filter;
+#ifdef MODEST_TOOLKIT_HILDON2
+       GtkWidget *live_search;
+       guint live_search_timeout;
+#endif
 
        gint    sort_colid[2][TNY_FOLDER_TYPE_NUM];
        gint    sort_type[2][TNY_FOLDER_TYPE_NUM];
@@ -174,6 +181,16 @@ struct _ModestHeaderViewPrivate {
 
        GdkColor active_color;
        GdkColor secondary_color;
+
+       gchar *filter_string;
+       gchar **filter_string_splitted;
+       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;
@@ -191,6 +208,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,
@@ -358,7 +381,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,
@@ -462,7 +485,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)) {
@@ -626,9 +653,18 @@ modest_header_view_init (ModestHeaderView *obj)
        priv->hidding_ids = NULL;
        priv->n_selected = 0;
        priv->filter = MODEST_HEADER_VIEW_FILTER_NONE;
+#ifdef MODEST_TOOLKIT_HILDON2
+       priv->live_search = NULL;
+#endif
+       priv->filter_string = NULL;
+       priv->filter_string_splitted = NULL;
+       priv->filter_date_range = FALSE;
        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++) {
@@ -654,6 +690,19 @@ 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);
+               priv->live_search_timeout = 0;
+       }
+#endif
+
        if (priv->datetime_formatter) {
                g_object_unref (priv->datetime_formatter);
                priv->datetime_formatter = NULL;
@@ -713,6 +762,14 @@ modest_header_view_finalize (GObject *obj)
                priv->autoselect_reference = NULL;
        }
 
+       if (priv->filter_string) {
+               g_free (priv->filter_string);
+       }
+
+       if (priv->filter_string_splitted) {
+               g_strfreev (priv->filter_string_splitted);
+       }
+
        G_OBJECT_CLASS(parent_class)->finalize (obj);
 }
 
@@ -1028,7 +1085,7 @@ modest_header_view_on_expose_event(GtkTreeView *header_view,
                        gtk_tree_path_free (tree_iter_path);
                }
        } else {
-               if (priv->autoselect_reference != NULL) {
+               if (priv->autoselect_reference != NULL && gtk_tree_row_reference_valid (priv->autoselect_reference)) {
                        gboolean moved_selection = FALSE;
                        GtkTreePath * last_path;
                        if (gtk_tree_selection_count_selected_rows (sel) != 1) {
@@ -1119,7 +1176,9 @@ set_folder_intern_get_headers_async_cb (TnyFolder *folder,
 }
 
 static void
-modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
+modest_header_view_set_folder_intern (ModestHeaderView *self,
+                                     TnyFolder *folder,
+                                     gboolean refresh)
 {
        TnyFolderType type;
        TnyList *headers;
@@ -1132,6 +1191,7 @@ modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
        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);
 
        /* Start the monitor in the callback of the
           tny_gtk_header_list_model_set_folder call. It's crucial to
@@ -1146,22 +1206,22 @@ modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
           be added again by tny_gtk_header_list_model_set_folder, so
           we'd end up with duplicate headers. sergio */
        tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
-                                             folder, FALSE,
+                                             folder, refresh,
                                              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));
@@ -1188,9 +1248,9 @@ modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
        }
 
        /* 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);
@@ -1202,7 +1262,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));
@@ -1210,7 +1270,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);
@@ -1375,7 +1438,7 @@ modest_header_view_set_folder (ModestHeaderView *self,
                ModestMailOperation *mail_op = NULL;
 
                /* Set folder in the model */
-               modest_header_view_set_folder_intern (self, folder);
+               modest_header_view_set_folder_intern (self, folder, refresh);
 
                /* Pick my reference. Nothing to do with the mail operation */
                priv->folder = g_object_ref (folder);
@@ -1624,7 +1687,6 @@ cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *ite
        gint t1, t2;
        gchar *val1, *val2;
        gint cmp;
-/*     static int counter = 0; */
 
        g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
 
@@ -1633,9 +1695,22 @@ cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *ite
        gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
                            TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
 
-       cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
-                                            val2 + modest_text_utils_get_subject_prefix_len(val2),
+       /* Do not use the prefixes for sorting. Consume all the blank
+          spaces for sorting */
+       cmp = modest_text_utils_utf8_strcmp (g_strchug (val1 + modest_text_utils_get_subject_prefix_len(val1)),
+                                            g_strchug (val2 + modest_text_utils_get_subject_prefix_len(val2)),
                                             TRUE);
+
+       /* If they're equal based on subject without prefix then just
+          sort them by length. This will show messages like this.
+          * Fw:
+          * Fw:Fw:
+          * Fw:Fw:
+          * Fw:Fw:Fw:
+          * */
+       if (cmp == 0)
+               cmp = (g_utf8_strlen (val1, -1) >= g_utf8_strlen (val2, -1)) ? 1 : -1;
+
        g_free (val1);
        g_free (val2);
        return cmp;
@@ -2089,6 +2164,104 @@ notify_filter_change_destroy (gpointer data)
 }
 
 static gboolean
+current_folder_needs_filtering (ModestHeaderViewPrivate *priv)
+{
+       /* For the moment we only need to filter outbox */
+       return priv->is_outbox;
+}
+
+static gboolean
+header_match_string (TnyHeader *header, gchar **words)
+{
+       gchar *subject_fold;
+       gchar *cc_fold;
+       gchar *bcc_fold;
+       gchar *to_fold;
+       gchar *from_fold;
+
+       gchar **current_word;
+       gboolean found;
+
+       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_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;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+static gboolean
 filter_row (GtkTreeModel *model,
            GtkTreeIter *iter,
            gpointer user_data)
@@ -2107,12 +2280,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) {
@@ -2128,7 +2299,7 @@ filter_row (GtkTreeModel *model,
        }
 
        if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_DELETABLE)) {
-               if (priv->is_outbox &&
+               if (current_folder_needs_filtering (priv) &&
                    modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
                        visible = FALSE;
                        goto frees;
@@ -2136,13 +2307,27 @@ filter_row (GtkTreeModel *model,
        }
 
        if (visible && (priv->filter & MODEST_HEADER_VIEW_FILTER_MOVEABLE)) {
-               if (priv->is_outbox &&
+               if (current_folder_needs_filtering (priv) &&
                    modest_tny_all_send_queues_get_msg_status (header) == MODEST_TNY_SEND_QUEUE_SENDING) {
                        visible = FALSE;
                        goto frees;
                }
        }
 
+       if (visible && priv->filter_string) {
+               if (!header_match_string (header, priv->filter_string_splitted)) {
+                       visible = FALSE;
+                       goto frees;
+               }
+               if (priv->filter_date_range) {
+                       if ((tny_header_get_date_sent (TNY_HEADER (header)) < priv->date_range_start) ||
+                           ((priv->date_range_end != -1) && (tny_header_get_date_sent (TNY_HEADER (header)) > priv->date_range_end))) {
+                               visible = FALSE;
+                               goto frees;
+                       }
+               }
+       }
+
        /* If no data on clipboard, return always TRUE */
        if (modest_email_clipboard_cleared(priv->clipboard)) {
                visible = TRUE;
@@ -2180,7 +2365,7 @@ filter_row (GtkTreeModel *model,
                                                                   notify_filter_change_destroy);
                }
        }
-
+       
        return visible;
 }
 
@@ -2203,17 +2388,17 @@ _clear_hidding_filter (ModestHeaderView *header_view)
 void
 modest_header_view_refilter (ModestHeaderView *header_view)
 {
-       GtkTreeModel *model = 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 */
-       model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
-       if (GTK_IS_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;
-               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
+               modest_header_view_refilter_by_chunks (header_view);
        }
 }
 
@@ -2324,17 +2509,14 @@ modest_header_view_set_filter (ModestHeaderView *self,
                               ModestHeaderViewFilter filter)
 {
        ModestHeaderViewPrivate *priv;
-       GtkTreeModel *filter_model;
 
        g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
 
        priv->filter |= filter;
 
-       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
-       if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
-               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
-       }
+       if (current_folder_needs_filtering (priv))
+               modest_header_view_refilter (self);
 }
 
 void
@@ -2342,17 +2524,14 @@ modest_header_view_unset_filter (ModestHeaderView *self,
                                 ModestHeaderViewFilter filter)
 {
        ModestHeaderViewPrivate *priv;
-       GtkTreeModel *filter_model;
 
        g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
        priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
 
        priv->filter &= ~filter;
 
-       filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
-       if (GTK_IS_TREE_MODEL_FILTER(filter_model)) {
-               gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
-       }
+       if (current_folder_needs_filtering (priv))
+               modest_header_view_refilter (self);
 }
 
 static void
@@ -2380,8 +2559,8 @@ update_style (ModestHeaderView *self)
        /* Set color */
 
        attr_list = pango_attr_list_new ();
-       if (!gtk_style_lookup_color (GTK_WIDGET (self)->style, "SecondaryTextColor", &style_color)) {
-               gdk_color_parse ("grey", &style_color);
+       if (!gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "SecondaryTextColor", &style_color)) {
+               gdk_color_parse (MODEST_SECONDARY_COLOR, &style_color);
        }
        priv->secondary_color = style_color;
        attr = pango_attr_foreground_new (style_color.red, style_color.green, style_color.blue);
@@ -2398,36 +2577,36 @@ update_style (ModestHeaderView *self)
                pango_attr_list_insert (attr_list, attr);
 
                g_object_set (G_OBJECT (priv->renderer_address),
-                             "foreground-gdk", priv->secondary_color,
+                             "foreground-gdk", &(priv->secondary_color),
                              "foreground-set", TRUE,
                              "attributes", attr_list,
                              NULL);
                g_object_set (G_OBJECT (priv->renderer_date_status),
-                             "foreground-gdk", priv->secondary_color,
+                             "foreground-gdk", &(priv->secondary_color),
                              "foreground-set", TRUE,
                              "attributes", attr_list,
                              NULL);
                pango_attr_list_unref (attr_list);
        } else {
                g_object_set (G_OBJECT (priv->renderer_address),
-                             "foreground-gdk", priv->secondary_color,
+                             "foreground-gdk", &(priv->secondary_color),
                              "foreground-set", TRUE,
                              "scale", PANGO_SCALE_SMALL,
                              "scale-set", TRUE,
                              NULL);
                g_object_set (G_OBJECT (priv->renderer_date_status),
-                             "foreground-gdk", priv->secondary_color,
+                             "foreground-gdk", &(priv->secondary_color),
                              "foreground-set", TRUE,
                              "scale", PANGO_SCALE_SMALL,
                              "scale-set", TRUE,
                              NULL);
        }
 
-       if (gtk_style_lookup_color (GTK_WIDGET (self)->style, "ActiveTextColor", &style_active_color)) {
+       if (gtk_style_lookup_color (gtk_widget_get_style (GTK_WIDGET (self)), "ActiveTextColor", &style_active_color)) {
                priv->active_color = style_active_color;
 #ifdef MODEST_TOOLKIT_HILDON2
                g_object_set_data (G_OBJECT (priv->renderer_subject), BOLD_IS_ACTIVE_COLOR, GINT_TO_POINTER (TRUE));
-               g_object_set_data_full (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, new_color, (GDestroyNotify) gdk_color_free);
+               g_object_set_data (G_OBJECT (priv->renderer_subject), ACTIVE_COLOR, &(priv->active_color));
 #endif
        } else {
 #ifdef MODEST_TOOLKIT_HILDON2
@@ -2454,8 +2633,6 @@ modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
                                                NULL))
                return NULL;
 
-       g_debug ("located path: %s", gtk_tree_path_to_string (path));
-
        /* Get model */
        tree_model = gtk_tree_view_get_model ((GtkTreeView *) header_view);
        if (!gtk_tree_model_get_iter (tree_model, &iter, path))
@@ -2468,3 +2645,276 @@ modest_header_view_get_header_at_pos (ModestHeaderView *header_view,
 
        return header;
 }
+
+static gboolean
+parse_date_side (const gchar *string, time_t *date_side)
+{
+       gchar *today;
+       gchar *yesterday;
+       gchar *casefold;
+       GDate *date;
+       gboolean result = FALSE;
+
+       if (string && string[0] == '\0') {
+               *date_side = 0;
+               return TRUE;
+       }
+
+       casefold = g_utf8_casefold (string, -1);
+       today = g_utf8_casefold (dgettext ("gtk20", "Today"), -1);
+       yesterday = g_utf8_casefold (dgettext ("gtk20", "Yesterday"), -1);
+       date = g_date_new ();
+
+       if (g_utf8_collate (casefold, today) == 0) {
+               *date_side = time (NULL);
+               result = TRUE;
+               goto frees;
+       }
+
+       if (g_utf8_collate (casefold, yesterday) == 0) {
+               *date_side = time (NULL) - 24*60*60;
+               result = TRUE;
+               goto frees;
+       }
+
+       g_date_set_parse (date, string);
+       if (g_date_valid (date)) {
+               struct tm tm = {0};
+               g_date_to_struct_tm (date, &tm);
+               *date_side = mktime (&tm);
+               
+               result = TRUE;
+               goto frees;
+       }
+frees:
+       g_free (today);
+       g_free (yesterday);
+       g_free (casefold);
+       g_date_free (date);
+
+       return result;
+}
+
+static gboolean
+parse_date_range (const gchar *string, time_t *date_range_start, time_t *date_range_end)
+{
+       gchar ** parts;
+       gboolean valid;
+
+       parts = g_strsplit (string, "..", 2);
+       valid = TRUE;
+
+       if (g_strv_length (parts) != 2) {
+               valid = FALSE;
+               goto frees;
+               g_strfreev (parts);
+               return FALSE;
+       }
+
+       if (!parse_date_side (parts[0], date_range_start)) {
+               valid = FALSE;
+               goto frees;
+       }
+
+       if (parse_date_side (parts[1], date_range_end)) {
+               if (*date_range_end == 0) {
+                       *date_range_end = (time_t) -1;
+               } else {
+                       *date_range_end += (24*60*60 - 1);
+               }
+       } else {
+               valid = FALSE;
+               goto frees;
+       }
+               
+frees:
+       g_strfreev (parts);
+       return valid;
+}
+
+void
+modest_header_view_set_filter_string (ModestHeaderView *self,
+                                     const gchar *filter_string)
+{
+       ModestHeaderViewPrivate *priv;
+
+       g_return_if_fail (MODEST_IS_HEADER_VIEW (self));
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+
+       if (priv->filter_string)
+               g_free (priv->filter_string);
+
+       priv->filter_string = g_strdup (filter_string);
+       priv->filter_date_range = FALSE;
+
+       if (priv->filter_string_splitted) {
+               g_strfreev (priv->filter_string_splitted);
+               priv->filter_string_splitted = NULL;
+       }
+
+       if (priv->filter_string) {
+               gchar **split, **current, **current_target;
+
+               split = g_strsplit (priv->filter_string, " ", 0);
+
+               priv->filter_string_splitted = g_malloc0 (sizeof (gchar *)*(g_strv_length (split) + 1));
+               current_target = priv->filter_string_splitted;
+               for (current = split; *current != 0; current ++) {
+                       gboolean has_date_range = FALSE;;
+                       if (g_strstr_len (*current, -1, "..") && strcmp(*current, "..")) {
+                               time_t range_start, range_end;
+                               /* It contains .. but it's not ".." so it may be a date range */
+                               if (parse_date_range (*current, &range_start, &range_end)) {
+                                       priv->filter_date_range = TRUE;
+                                       has_date_range = TRUE;
+                                       priv->date_range_start = range_start;
+                                       priv->date_range_end = range_end;
+                               }
+                       }
+                       if (!has_date_range) {
+                               *current_target = g_utf8_casefold (*current, -1);
+                               current_target++;
+                       }
+               }
+               *current_target = '\0';
+               g_strfreev (split);
+       }
+       modest_header_view_refilter (MODEST_HEADER_VIEW (self));
+}
+
+#ifdef MODEST_TOOLKIT_HILDON2
+static gboolean
+on_live_search_timeout (ModestHeaderView *self)
+{
+       const gchar *needle;
+       ModestHeaderViewPrivate *priv;
+
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+
+       needle = hildon_live_search_get_text ((HildonLiveSearch *) priv->live_search);
+       if (needle && needle[0] != '\0') {
+               modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), needle);
+       } else {
+               modest_header_view_set_filter_string (MODEST_HEADER_VIEW (self), NULL);
+       }
+
+       priv->live_search_timeout = 0;
+
+       return FALSE;
+}
+
+static gboolean
+on_live_search_refilter (HildonLiveSearch *livesearch,
+                        ModestHeaderView *self)
+{
+       ModestHeaderViewPrivate *priv;
+       GtkTreeModel *model, *sortable, *filter;
+
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+
+       if (priv->live_search_timeout > 0) {
+               g_source_remove (priv->live_search_timeout);
+               priv->live_search_timeout = 0;
+       }
+
+       model = NULL;
+       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));
+               }
+       }
+
+       if (model && tny_list_get_length (TNY_LIST (model)) > 250) {
+               priv->live_search_timeout = g_timeout_add (1000, (GSourceFunc) on_live_search_timeout, self);
+       } else {
+               on_live_search_timeout (self);
+       }
+
+       return TRUE;
+}
+
+GtkWidget *
+modest_header_view_setup_live_search (ModestHeaderView *self)
+{
+       ModestHeaderViewPrivate *priv;
+
+       g_return_val_if_fail (MODEST_IS_HEADER_VIEW (self), NULL);
+       priv = MODEST_HEADER_VIEW_GET_PRIVATE (self);
+       priv->live_search = hildon_live_search_new ();
+
+       g_signal_connect (G_OBJECT (priv->live_search), "refilter", G_CALLBACK (on_live_search_refilter), 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);
+       }
+}