+
+static gboolean
+on_focus_out (GtkWidget *self,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+
+ if (!gtk_widget_is_focus (self)) {
+ GtkTreeSelection *selection = NULL;
+ GList *selected_rows = NULL;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
+ if (gtk_tree_selection_count_selected_rows (selection) > 1) {
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
+ g_signal_handlers_block_by_func (selection, on_selection_changed, self);
+ gtk_tree_selection_unselect_all (selection);
+ gtk_tree_selection_select_path (selection, (GtkTreePath *) selected_rows->data);
+ g_signal_handlers_unblock_by_func (selection, on_selection_changed, self);
+ g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+on_button_release_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
+{
+ enable_drag_and_drop(self);
+ return FALSE;
+}
+
+static gboolean
+on_button_press_event(GtkWidget * self, GdkEventButton * event, gpointer userdata)
+{
+ GtkTreeSelection *selection = NULL;
+ GtkTreePath *path = NULL;
+ gboolean already_selected = FALSE;
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(self), event->x, event->y, &path, NULL, NULL, NULL)) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
+ already_selected = gtk_tree_selection_path_is_selected (selection, path);
+ }
+
+ /* Enable drag and drop onlly if the user clicks on a row that
+ it's already selected. If not, let him select items using
+ the pointer */
+ if (!already_selected) {
+ disable_drag_and_drop(self);
+ }
+
+ if (path != NULL) {
+ gtk_tree_path_free(path);
+ }
+
+ return FALSE;
+}
+
+static void
+folder_monitor_update (TnyFolderObserver *self,
+ TnyFolderChange *change)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ TnyFolderChangeChanged changed;
+ TnyFolder *folder = NULL;
+
+ changed = tny_folder_change_get_changed (change);
+
+ /* Do not notify the observers if the folder of the header
+ view has changed before this call to the observer
+ happens */
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
+ folder = tny_folder_change_get_folder (change);
+ if (folder != priv->folder)
+ goto frees;
+
+ MODEST_DEBUG_BLOCK (
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS)
+ g_print ("ADDED %d/%d (r/t) \n",
+ tny_folder_change_get_new_unread_count (change),
+ tny_folder_change_get_new_all_count (change));
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_ALL_COUNT)
+ g_print ("ALL COUNT %d\n",
+ tny_folder_change_get_new_all_count (change));
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_UNREAD_COUNT)
+ g_print ("UNREAD COUNT %d\n",
+ tny_folder_change_get_new_unread_count (change));
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)
+ g_print ("EXPUNGED %d/%d (r/t) \n",
+ tny_folder_change_get_new_unread_count (change),
+ tny_folder_change_get_new_all_count (change));
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_FOLDER_RENAME)
+ g_print ("FOLDER RENAME\n");
+ if (changed & TNY_FOLDER_CHANGE_CHANGED_MSG_RECEIVED)
+ g_print ("MSG RECEIVED %d/%d (r/t) \n",
+ tny_folder_change_get_new_unread_count (change),
+ tny_folder_change_get_new_all_count (change));
+ g_print ("---------------------------------------------------\n");
+ );
+
+ /* Check folder count */
+ if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
+ (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
+
+ g_mutex_lock (priv->observers_lock);
+
+ /* Emit signal to evaluate how headers changes affects
+ to the window view */
+ g_signal_emit (G_OBJECT(self),
+ signals[MSG_COUNT_CHANGED_SIGNAL],
+ 0, folder, change);
+
+ /* Added or removed headers, so data stored on cliboard are invalid */
+ if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
+ modest_email_clipboard_clear (priv->clipboard);
+
+ g_mutex_unlock (priv->observers_lock);
+ }
+
+ /* Free */
+ frees:
+ if (folder != NULL)
+ g_object_unref (folder);
+}
+
+gboolean
+modest_header_view_is_empty (ModestHeaderView *self)
+{
+ ModestHeaderViewPrivate *priv;
+
+ g_return_val_if_fail (self && MODEST_IS_HEADER_VIEW(self), TRUE);
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
+
+ return priv->status == HEADER_VIEW_EMPTY;
+}
+
+void
+modest_header_view_clear (ModestHeaderView *self)
+{
+ g_return_if_fail (self && MODEST_IS_HEADER_VIEW(self));
+
+ modest_header_view_set_folder (self, NULL, NULL, NULL);
+}
+
+void
+modest_header_view_copy_selection (ModestHeaderView *header_view)
+{
+ g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
+
+ /* Copy selection */
+ _clipboard_set_selected_data (header_view, FALSE);
+}
+
+void
+modest_header_view_cut_selection (ModestHeaderView *header_view)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ const gchar **hidding = NULL;
+ guint i, n_selected;
+
+ g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW (header_view));
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
+
+ /* Copy selection */
+ _clipboard_set_selected_data (header_view, TRUE);
+
+ /* Get hidding ids */
+ hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
+
+ /* Clear hidding array created by previous cut operation */
+ _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
+
+ /* Copy hidding array */
+ priv->n_selected = n_selected;
+ priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
+ for (i=0; i < n_selected; i++)
+ priv->hidding_ids[i] = g_strdup(hidding[i]);
+
+ /* Hide cut headers */
+ modest_header_view_refilter (header_view);
+}
+
+
+
+
+static void
+_clipboard_set_selected_data (ModestHeaderView *header_view,
+ gboolean delete)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ TnyList *headers = NULL;
+
+ g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
+
+ /* Set selected data on clipboard */
+ g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
+ headers = modest_header_view_get_selected_headers (header_view);
+ modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
+
+ /* Free */
+ g_object_unref (headers);
+}
+
+typedef struct {
+ ModestHeaderView *self;
+ TnyFolder *folder;
+} NotifyFilterInfo;
+
+static gboolean
+notify_filter_change (gpointer data)
+{
+ NotifyFilterInfo *info = (NotifyFilterInfo *) data;
+
+ g_signal_emit (info->self,
+ signals[MSG_COUNT_CHANGED_SIGNAL],
+ 0, info->folder, NULL);
+
+ return FALSE;
+}
+
+static void
+notify_filter_change_destroy (gpointer data)
+{
+ NotifyFilterInfo *info = (NotifyFilterInfo *) data;
+
+ g_object_unref (info->self);
+ g_object_unref (info->folder);
+ g_slice_free (NotifyFilterInfo, info);
+}
+
+static gboolean
+filter_row (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ TnyHeaderFlags flags;
+ TnyHeader *header = NULL;
+ guint i;
+ gchar *id = NULL;
+ gboolean visible = TRUE;
+ gboolean found = FALSE;
+ GValue value = {0,};
+ HeaderViewStatus old_status;
+
+ g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
+ 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);
+
+ /* Hide deleted and mark as deleted heders */
+ if (flags & TNY_HEADER_FLAG_DELETED ||
+ flags & TNY_HEADER_FLAG_EXPUNGED) {
+ visible = FALSE;
+ goto frees;
+ }
+
+ /* If no data on clipboard, return always TRUE */
+ if (modest_email_clipboard_cleared(priv->clipboard)) {
+ visible = TRUE;
+ goto frees;
+ }
+
+ /* Get message id from header (ensure is a valid id) */
+ if (!header) {
+ visible = FALSE;
+ goto frees;
+ }
+
+ /* Check hiding */
+ if (priv->hidding_ids != NULL) {
+ id = g_strdup(tny_header_get_message_id (header));
+ for (i=0; i < priv->n_selected && !found; i++)
+ if (priv->hidding_ids[i] != NULL && id != NULL)
+ found = (!strcmp (priv->hidding_ids[i], id));
+
+ visible = !found;
+ g_free(id);
+ }
+
+ frees:
+ old_status = priv->status;
+ priv->status = ((gboolean) priv->status) && !visible;
+ if (priv->status != old_status) {
+ NotifyFilterInfo *info;
+
+ if (priv->status_timeout)
+ g_source_remove (priv->status_timeout);
+
+ info = g_slice_new0 (NotifyFilterInfo);
+ info->self = g_object_ref (G_OBJECT (user_data));
+ info->folder = tny_header_get_folder (header);
+ priv->status_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, 1000,
+ notify_filter_change,
+ info,
+ notify_filter_change_destroy);
+ }
+
+ return visible;
+}
+
+static void
+_clear_hidding_filter (ModestHeaderView *header_view)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ guint i;
+
+ g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
+
+ if (priv->hidding_ids != NULL) {
+ for (i=0; i < priv->n_selected; i++)
+ g_free (priv->hidding_ids[i]);
+ g_free(priv->hidding_ids);
+ }
+}
+
+void
+modest_header_view_refilter (ModestHeaderView *header_view)
+{
+ GtkTreeModel *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)) {
+ priv->status = HEADER_VIEW_INIT;
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
+ }
+}
+
+/*
+ * Called when an account is removed. If I'm showing a folder of the
+ * account that has been removed then clear the view
+ */
+static void
+on_account_removed (TnyAccountStore *self,
+ TnyAccount *account,
+ gpointer user_data)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+
+ /* Ignore changes in transport accounts */
+ if (TNY_IS_TRANSPORT_ACCOUNT (account))
+ return;
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
+
+ if (priv->folder) {
+ TnyAccount *my_account;
+
+ my_account = tny_folder_get_account (priv->folder);
+ if (my_account == account)
+ modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
+ g_object_unref (account);
+ }
+}
+
+void
+modest_header_view_add_observer(ModestHeaderView *header_view,
+ ModestHeaderViewObserver *observer)
+{
+ ModestHeaderViewPrivate *priv;
+
+ g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
+ g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
+
+ g_mutex_lock(priv->observer_list_lock);
+ priv->observer_list = g_slist_prepend(priv->observer_list, observer);
+ g_mutex_unlock(priv->observer_list_lock);
+}
+
+void
+modest_header_view_remove_observer(ModestHeaderView *header_view,
+ ModestHeaderViewObserver *observer)
+{
+ ModestHeaderViewPrivate *priv;
+
+ g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
+ g_return_if_fail (observer && MODEST_IS_HEADER_VIEW_OBSERVER(observer));
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
+
+ g_mutex_lock(priv->observer_list_lock);
+ priv->observer_list = g_slist_remove(priv->observer_list, observer);
+ g_mutex_unlock(priv->observer_list_lock);
+}
+
+static void
+modest_header_view_notify_observers(ModestHeaderView *header_view,
+ GtkTreeModel *model,
+ const gchar *tny_folder_id)
+{
+ ModestHeaderViewPrivate *priv = NULL;
+ GSList *iter;
+ ModestHeaderViewObserver *observer;
+
+
+ g_return_if_fail (header_view && MODEST_IS_HEADER_VIEW(header_view));
+
+ priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
+
+ g_mutex_lock(priv->observer_list_lock);
+ iter = priv->observer_list;
+ while(iter != NULL){
+ observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
+ modest_header_view_observer_update(observer, model,
+ tny_folder_id);
+ iter = g_slist_next(iter);
+ }
+ g_mutex_unlock(priv->observer_list_lock);
+}
+