1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
36 #include <modest-header-view.h>
37 #include <modest-header-view-priv.h>
38 #include <modest-dnd.h>
39 #include <modest-tny-folder.h>
41 #include <modest-main-window.h>
43 #include <modest-marshal.h>
44 #include <modest-text-utils.h>
45 #include <modest-icon-names.h>
46 #include <modest-runtime.h>
47 #include "modest-platform.h"
48 #include <modest-hbox-cell-renderer.h>
49 #include <modest-vbox-cell-renderer.h>
51 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
52 static void modest_header_view_init (ModestHeaderView *obj);
53 static void modest_header_view_finalize (GObject *obj);
54 static void modest_header_view_dispose (GObject *obj);
56 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
57 GtkTreeViewColumn *column, gpointer userdata);
59 static gint cmp_rows (GtkTreeModel *tree_model,
64 static gint cmp_subject_rows (GtkTreeModel *tree_model,
69 static gboolean filter_row (GtkTreeModel *model,
73 static void on_selection_changed (GtkTreeSelection *sel,
76 static void setup_drag_and_drop (GtkTreeView *self);
78 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
80 static gboolean on_focus_in (GtkWidget *sef,
84 static void folder_monitor_update (TnyFolderObserver *self,
85 TnyFolderChange *change);
87 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
89 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
91 static void _clear_hidding_filter (ModestHeaderView *header_view);
94 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
95 struct _ModestHeaderViewPrivate {
97 ModestHeaderViewStyle style;
99 TnyFolderMonitor *monitor;
100 GMutex *observers_lock;
102 /* not unref this object, its a singlenton */
103 ModestEmailClipboard *clipboard;
105 /* Filter tree model */
109 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
110 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
115 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
116 struct _HeadersCountChangedHelper {
117 ModestHeaderView *self;
118 TnyFolderChange *change;
122 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
123 MODEST_TYPE_HEADER_VIEW, \
124 ModestHeaderViewPrivate))
128 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
131 HEADER_SELECTED_SIGNAL,
132 HEADER_ACTIVATED_SIGNAL,
133 ITEM_NOT_FOUND_SIGNAL,
134 MSG_COUNT_CHANGED_SIGNAL,
139 static GObjectClass *parent_class = NULL;
141 /* uncomment the following if you have defined any signals */
142 static guint signals[LAST_SIGNAL] = {0};
145 modest_header_view_get_type (void)
147 static GType my_type = 0;
149 static const GTypeInfo my_info = {
150 sizeof(ModestHeaderViewClass),
151 NULL, /* base init */
152 NULL, /* base finalize */
153 (GClassInitFunc) modest_header_view_class_init,
154 NULL, /* class finalize */
155 NULL, /* class data */
156 sizeof(ModestHeaderView),
158 (GInstanceInitFunc) modest_header_view_init,
162 static const GInterfaceInfo tny_folder_observer_info =
164 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
165 NULL, /* interface_finalize */
166 NULL /* interface_data */
168 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
172 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
173 &tny_folder_observer_info);
181 modest_header_view_class_init (ModestHeaderViewClass *klass)
183 GObjectClass *gobject_class;
184 gobject_class = (GObjectClass*) klass;
186 parent_class = g_type_class_peek_parent (klass);
187 gobject_class->finalize = modest_header_view_finalize;
188 gobject_class->dispose = modest_header_view_dispose;
190 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
192 signals[HEADER_SELECTED_SIGNAL] =
193 g_signal_new ("header_selected",
194 G_TYPE_FROM_CLASS (gobject_class),
196 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
198 g_cclosure_marshal_VOID__POINTER,
199 G_TYPE_NONE, 1, G_TYPE_POINTER);
201 signals[HEADER_ACTIVATED_SIGNAL] =
202 g_signal_new ("header_activated",
203 G_TYPE_FROM_CLASS (gobject_class),
205 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
207 g_cclosure_marshal_VOID__POINTER,
208 G_TYPE_NONE, 1, G_TYPE_POINTER);
211 signals[ITEM_NOT_FOUND_SIGNAL] =
212 g_signal_new ("item_not_found",
213 G_TYPE_FROM_CLASS (gobject_class),
215 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
217 g_cclosure_marshal_VOID__INT,
218 G_TYPE_NONE, 1, G_TYPE_INT);
220 signals[MSG_COUNT_CHANGED_SIGNAL] =
221 g_signal_new ("msg_count_changed",
222 G_TYPE_FROM_CLASS (gobject_class),
224 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
226 modest_marshal_VOID__POINTER_POINTER,
227 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
231 tny_folder_observer_init (TnyFolderObserverIface *klass)
233 klass->update_func = folder_monitor_update;
236 static GtkTreeViewColumn*
237 get_new_column (const gchar *name, GtkCellRenderer *renderer,
238 gboolean resizable, gint sort_col_id, gboolean show_as_text,
239 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
241 GtkTreeViewColumn *column;
243 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
244 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
246 gtk_tree_view_column_set_resizable (column, resizable);
248 gtk_tree_view_column_set_expand (column, TRUE);
251 gtk_tree_view_column_add_attribute (column, renderer, "text",
253 if (sort_col_id >= 0)
254 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
256 gtk_tree_view_column_set_sort_indicator (column, FALSE);
257 gtk_tree_view_column_set_reorderable (column, TRUE);
260 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
267 remove_all_columns (ModestHeaderView *obj)
269 GList *columns, *cursor;
271 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
273 for (cursor = columns; cursor; cursor = cursor->next)
274 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
275 GTK_TREE_VIEW_COLUMN(cursor->data));
276 g_list_free (columns);
280 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
282 GtkTreeModel *tree_filter, *sortable;
283 GtkTreeViewColumn *column=NULL;
284 GtkTreeSelection *selection = NULL;
285 GtkCellRenderer *renderer_msgtype,*renderer_header,
286 *renderer_attach, *renderer_compact_date;
287 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
288 *renderer_subject, *renderer_subject_box, *renderer_recpt,
290 ModestHeaderViewPrivate *priv;
291 GtkTreeViewColumn *compact_column = NULL;
294 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
296 /* FIXME: check whether these renderers need to be freed */
297 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
298 renderer_attach = gtk_cell_renderer_pixbuf_new ();
299 renderer_priority = gtk_cell_renderer_pixbuf_new ();
300 renderer_header = gtk_cell_renderer_text_new ();
302 renderer_compact_header = modest_vbox_cell_renderer_new ();
303 renderer_recpt_box = modest_hbox_cell_renderer_new ();
304 renderer_subject_box = modest_hbox_cell_renderer_new ();
305 renderer_recpt = gtk_cell_renderer_text_new ();
306 renderer_subject = gtk_cell_renderer_text_new ();
307 renderer_compact_date = gtk_cell_renderer_text_new ();
309 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
310 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
311 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
312 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
313 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
314 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
315 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
316 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
317 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
318 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
319 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
320 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
321 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date, FALSE);
322 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date);
324 g_object_set(G_OBJECT(renderer_header),
325 "ellipsize", PANGO_ELLIPSIZE_END,
327 g_object_set (G_OBJECT (renderer_subject),
328 "ellipsize", PANGO_ELLIPSIZE_END,
330 g_object_set (G_OBJECT (renderer_recpt),
331 "ellipsize", PANGO_ELLIPSIZE_END,
333 g_object_set(G_OBJECT(renderer_compact_date),
337 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 32);
338 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 32);
340 remove_all_columns (self);
342 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
343 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
344 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
345 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
346 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
348 /* Add new columns */
349 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
350 ModestHeaderViewColumn col =
351 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
353 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
354 g_printerr ("modest: invalid column %d in column list\n", col);
360 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
361 column = get_new_column (_("M"), renderer_msgtype, FALSE,
362 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
364 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
366 gtk_tree_view_column_set_fixed_width (column, 45);
369 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
370 column = get_new_column (_("A"), renderer_attach, FALSE,
371 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
373 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
375 gtk_tree_view_column_set_fixed_width (column, 45);
379 case MODEST_HEADER_VIEW_COLUMN_FROM:
380 column = get_new_column (_("From"), renderer_header, TRUE,
381 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
383 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
384 GINT_TO_POINTER(TRUE));
387 case MODEST_HEADER_VIEW_COLUMN_TO:
388 column = get_new_column (_("To"), renderer_header, TRUE,
389 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
391 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
392 GINT_TO_POINTER(FALSE));
395 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
396 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
397 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
399 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
400 GINT_TO_POINTER(TRUE));
401 compact_column = column;
404 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
405 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
406 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
408 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
409 GINT_TO_POINTER(FALSE));
410 compact_column = column;
414 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
415 column = get_new_column (_("Subject"), renderer_header, TRUE,
416 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
418 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
422 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
423 column = get_new_column (_("Received"), renderer_header, TRUE,
424 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
426 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
427 GINT_TO_POINTER(TRUE));
430 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
431 column = get_new_column (_("Sent"), renderer_header, TRUE,
432 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
434 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
435 GINT_TO_POINTER(FALSE));
438 case MODEST_HEADER_VIEW_COLUMN_SIZE:
439 column = get_new_column (_("Size"), renderer_header, TRUE,
440 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
445 case MODEST_HEADER_VIEW_COLUMN_STATUS:
446 column = get_new_column (_("Status"), renderer_compact_date, TRUE,
447 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
449 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
454 g_return_val_if_reached(FALSE);
457 /* we keep the column id around */
458 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
459 GINT_TO_POINTER(col));
461 /* we need this ptr when sorting the rows */
462 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
464 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
468 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
469 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
470 (GtkTreeIterCompareFunc) cmp_rows,
471 compact_column, NULL);
472 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
473 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
474 (GtkTreeIterCompareFunc) cmp_subject_rows,
475 compact_column, NULL);
483 modest_header_view_init (ModestHeaderView *obj)
485 ModestHeaderViewPrivate *priv;
488 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
492 priv->monitor = NULL;
493 priv->observers_lock = g_mutex_new ();
495 priv->clipboard = modest_runtime_get_email_clipboard ();
496 priv->hidding_ids = NULL;
497 priv->n_selected = 0;
499 /* Sort parameters */
500 for (j=0; j < 2; j++) {
501 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
502 priv->sort_colid[j][i] = -1;
503 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
507 setup_drag_and_drop (GTK_TREE_VIEW (obj));
511 modest_header_view_dispose (GObject *obj)
513 ModestHeaderView *self;
514 ModestHeaderViewPrivate *priv;
516 self = MODEST_HEADER_VIEW(obj);
517 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
520 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
521 g_object_unref (G_OBJECT (priv->folder));
525 G_OBJECT_CLASS(parent_class)->dispose (obj);
529 modest_header_view_finalize (GObject *obj)
531 ModestHeaderView *self;
532 ModestHeaderViewPrivate *priv;
534 self = MODEST_HEADER_VIEW(obj);
535 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
537 g_mutex_lock (priv->observers_lock);
539 tny_folder_monitor_stop (priv->monitor);
540 g_object_unref (G_OBJECT (priv->monitor));
542 g_mutex_unlock (priv->observers_lock);
543 g_mutex_free (priv->observers_lock);
545 /* Clear hidding array created by cut operation */
546 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
548 G_OBJECT_CLASS(parent_class)->finalize (obj);
553 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
556 GtkTreeSelection *sel;
557 ModestHeaderView *self;
558 ModestHeaderViewPrivate *priv;
560 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
563 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
564 self = MODEST_HEADER_VIEW(obj);
565 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
567 modest_header_view_set_style (self, style);
568 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
570 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
571 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
572 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
574 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
575 TRUE); /* alternating row colors */
576 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
578 g_signal_connect (sel, "changed",
579 G_CALLBACK(on_selection_changed), self);
581 g_signal_connect (self, "row-activated",
582 G_CALLBACK (on_header_row_activated), NULL);
584 g_signal_connect (self, "focus-in-event",
585 G_CALLBACK(on_focus_in), NULL);
587 return GTK_WIDGET(self);
592 modest_header_view_count_selected_headers (ModestHeaderView *self)
594 GtkTreeSelection *sel;
597 g_return_val_if_fail (self, 0);
599 /* Get selection object and check selected rows count */
600 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
601 selected_rows = gtk_tree_selection_count_selected_rows (sel);
603 return selected_rows;
607 modest_header_view_has_selected_headers (ModestHeaderView *self)
609 GtkTreeSelection *sel;
612 g_return_val_if_fail (self, FALSE);
614 /* Get selection object and check selected rows count */
615 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
616 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
623 modest_header_view_get_selected_headers (ModestHeaderView *self)
625 GtkTreeSelection *sel;
626 ModestHeaderViewPrivate *priv;
627 TnyList *header_list = NULL;
629 GList *list, *tmp = NULL;
630 GtkTreeModel *tree_model = NULL;
633 g_return_val_if_fail (self, NULL);
635 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
637 /* Get selected rows */
638 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
639 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
642 header_list = tny_simple_list_new();
644 list = g_list_reverse (list);
647 /* get header from selection */
648 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
649 gtk_tree_model_get (tree_model, &iter,
650 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
652 /* Prepend to list */
653 tny_list_prepend (header_list, G_OBJECT (header));
654 g_object_unref (G_OBJECT (header));
656 tmp = g_list_next (tmp);
659 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
666 /* scroll our list view so the selected item is visible */
668 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
670 #ifdef MODEST_PLATFORM_GNOME
672 GtkTreePath *selected_path;
673 GtkTreePath *start, *end;
677 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
678 selected_path = gtk_tree_model_get_path (model, iter);
680 start = gtk_tree_path_new ();
681 end = gtk_tree_path_new ();
683 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
685 if (gtk_tree_path_compare (selected_path, start) < 0 ||
686 gtk_tree_path_compare (end, selected_path) < 0)
687 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
688 selected_path, NULL, TRUE,
691 gtk_tree_path_free (selected_path);
692 gtk_tree_path_free (start);
693 gtk_tree_path_free (end);
695 #endif /* MODEST_PLATFORM_GNOME */
700 modest_header_view_select_next (ModestHeaderView *self)
702 GtkTreeSelection *sel;
707 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
708 path = get_selected_row (GTK_TREE_VIEW(self), &model);
709 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
710 /* Unselect previous path */
711 gtk_tree_selection_unselect_path (sel, path);
713 /* Move path down and selects new one */
714 if (gtk_tree_model_iter_next (model, &iter)) {
715 gtk_tree_selection_select_iter (sel, &iter);
716 scroll_to_selected (self, &iter, FALSE);
718 gtk_tree_path_free(path);
724 modest_header_view_select_prev (ModestHeaderView *self)
726 GtkTreeSelection *sel;
731 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
732 path = get_selected_row (GTK_TREE_VIEW(self), &model);
733 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
734 /* Unselect previous path */
735 gtk_tree_selection_unselect_path (sel, path);
738 if (gtk_tree_path_prev (path)) {
739 gtk_tree_model_get_iter (model, &iter, path);
741 /* Select the new one */
742 gtk_tree_selection_select_iter (sel, &iter);
743 scroll_to_selected (self, &iter, TRUE);
746 gtk_tree_path_free (path);
751 modest_header_view_get_columns (ModestHeaderView *self)
753 g_return_val_if_fail (self, FALSE);
754 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
759 modest_header_view_is_empty (ModestHeaderView *self)
761 g_return_val_if_fail (self, FALSE);
762 return FALSE; /* FIXME */
767 modest_header_view_set_style (ModestHeaderView *self,
768 ModestHeaderViewStyle style)
770 ModestHeaderViewPrivate *priv;
771 gboolean show_col_headers = FALSE;
772 ModestHeaderViewStyle old_style;
774 g_return_val_if_fail (self, FALSE);
775 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
778 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
779 if (priv->style == style)
780 return TRUE; /* nothing to do */
783 case MODEST_HEADER_VIEW_STYLE_DETAILS:
784 show_col_headers = TRUE;
786 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
789 g_return_val_if_reached (FALSE);
791 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
792 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
794 old_style = priv->style;
801 ModestHeaderViewStyle
802 modest_header_view_get_style (ModestHeaderView *self)
804 g_return_val_if_fail (self, FALSE);
805 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
809 * This function sets a sortable model in the header view. It's just
810 * used for developing purposes, because it only does a
811 * gtk_tree_view_set_model
814 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
816 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
817 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
818 /* GtkTreeModel *old_model; */
819 /* ModestHeaderViewPrivate *priv; */
820 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
821 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
823 /* /\* Set new model *\/ */
824 /* gtk_tree_view_set_model (header_view, model); */
826 gtk_tree_view_set_model (header_view, model);
830 modest_header_view_get_folder (ModestHeaderView *self)
832 ModestHeaderViewPrivate *priv;
833 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
836 g_object_ref (priv->folder);
842 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
846 ModestHeaderViewPrivate *priv;
847 GList *cols, *cursor;
848 GtkTreeModel *filter_model, *sortable;
850 GtkSortType sort_type;
852 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
854 headers = TNY_LIST (tny_gtk_header_list_model_new ());
856 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
859 /* Add IDLE observer (monitor) and another folder observer for
860 new messages (self) */
861 g_mutex_lock (priv->observers_lock);
863 tny_folder_monitor_stop (priv->monitor);
864 g_object_unref (G_OBJECT (priv->monitor));
866 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
867 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
868 tny_folder_monitor_start (priv->monitor);
869 g_mutex_unlock (priv->observers_lock);
871 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
872 g_object_unref (G_OBJECT (headers));
874 /* Create a tree model filter to hide and show rows for cut operations */
875 filter_model = gtk_tree_model_filter_new (sortable, NULL);
876 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
880 g_object_unref (G_OBJECT (sortable));
882 /* install our special sorting functions */
883 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
885 /* Restore sort column id */
887 type = modest_tny_folder_guess_folder_type (folder);
888 sort_colid = modest_header_view_get_sort_column_id (self, type);
889 sort_type = modest_header_view_get_sort_type (self, type);
890 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
893 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
894 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
895 (GtkTreeIterCompareFunc) cmp_rows,
897 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
898 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
899 (GtkTreeIterCompareFunc) cmp_subject_rows,
904 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
905 g_object_unref (G_OBJECT (filter_model));
906 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
907 /* g_object_unref (G_OBJECT (sortable)); */
914 modest_header_view_sort_by_column_id (ModestHeaderView *self,
916 GtkSortType sort_type)
918 ModestHeaderViewPrivate *priv = NULL;
919 GtkTreeModel *tree_filter, *sortable = NULL;
922 /* Get model and private data */
923 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
924 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
925 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
926 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
928 /* Sort tree model */
929 type = modest_tny_folder_guess_folder_type (priv->folder);
930 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
933 /* Store new sort parameters */
934 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
936 /* Save GConf parameters */
937 /* modest_widget_memory_save (modest_runtime_get_conf(), */
938 /* G_OBJECT(self), "header-view"); */
943 modest_header_view_set_sort_params (ModestHeaderView *self,
945 GtkSortType sort_type,
948 ModestHeaderViewPrivate *priv;
949 ModestHeaderViewStyle style;
951 style = modest_header_view_get_style (self);
952 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
954 priv->sort_colid[style][type] = sort_colid;
955 priv->sort_type[style][type] = sort_type;
959 modest_header_view_get_sort_column_id (ModestHeaderView *self,
962 ModestHeaderViewPrivate *priv;
963 ModestHeaderViewStyle style;
965 style = modest_header_view_get_style (self);
966 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
968 return priv->sort_colid[style][type];
972 modest_header_view_get_sort_type (ModestHeaderView *self,
975 ModestHeaderViewPrivate *priv;
976 ModestHeaderViewStyle style;
978 style = modest_header_view_get_style (self);
979 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
981 return priv->sort_type[style][type];
985 ModestHeaderView *header_view;
986 RefreshAsyncUserCallback cb;
991 folder_refreshed_cb (const GObject *obj,
995 ModestHeaderViewPrivate *priv;
996 SetFolderHelper *info;
998 info = (SetFolderHelper*) user_data;
1000 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1004 info->cb (obj, folder, info->user_data);
1006 /* Start the folder count changes observer. We do not need it
1007 before the refresh. Note that the monitor could still be
1008 called for this refresh but now we know that the callback
1009 was previously called */
1010 g_mutex_lock (priv->observers_lock);
1011 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1012 g_mutex_unlock (priv->observers_lock);
1019 modest_header_view_set_folder (ModestHeaderView *self,
1021 RefreshAsyncUserCallback callback,
1024 ModestHeaderViewPrivate *priv;
1025 ModestWindowMgr *mgr = NULL;
1026 GObject *source = NULL;
1027 SetFolderHelper *info;
1029 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1032 g_mutex_lock (priv->observers_lock);
1033 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1034 g_object_unref (priv->folder);
1035 priv->folder = NULL;
1036 g_mutex_unlock (priv->observers_lock);
1040 ModestMailOperation *mail_op = NULL;
1042 /* Get main window to use it as source of mail operation */
1043 mgr = modest_runtime_get_window_mgr ();
1044 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1046 /* Set folder in the model */
1047 modest_header_view_set_folder_intern (self, folder);
1049 /* Pick my reference. Nothing to do with the mail operation */
1050 priv->folder = g_object_ref (folder);
1052 /* no message selected */
1053 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1055 info = g_malloc0 (sizeof(SetFolderHelper));
1056 info->header_view = self;
1057 info->cb = callback;
1058 info->user_data = user_data;
1060 /* Create the mail operation (source will be the parent widget) */
1061 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1062 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1065 /* Refresh the folder asynchronously */
1066 modest_mail_operation_refresh_folder (mail_op,
1068 folder_refreshed_cb,
1072 g_object_unref (mail_op);
1074 g_mutex_lock (priv->observers_lock);
1076 if (priv->monitor) {
1077 tny_folder_monitor_stop (priv->monitor);
1078 g_object_unref (G_OBJECT (priv->monitor));
1079 priv->monitor = NULL;
1081 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1083 g_mutex_unlock (priv->observers_lock);
1088 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1089 GtkTreeViewColumn *column, gpointer userdata)
1091 ModestHeaderView *self = NULL;
1092 ModestHeaderViewPrivate *priv = NULL;
1094 GtkTreeModel *model = NULL;
1095 TnyHeader *header = NULL;
1096 TnyHeaderFlags flags;
1098 self = MODEST_HEADER_VIEW (treeview);
1099 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1101 model = gtk_tree_view_get_model (treeview);
1102 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1105 /* get the first selected item */
1106 gtk_tree_model_get (model, &iter,
1107 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1108 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1111 /* Dont open DELETED messages */
1112 if (flags & TNY_HEADER_FLAG_DELETED) {
1113 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1118 g_signal_emit (G_OBJECT(self),
1119 signals[HEADER_ACTIVATED_SIGNAL],
1125 g_object_unref (G_OBJECT (header));
1130 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1132 GtkTreeModel *model;
1134 GtkTreePath *path = NULL;
1136 ModestHeaderView *self;
1137 ModestHeaderViewPrivate *priv;
1138 GList *selected = NULL;
1140 g_return_if_fail (sel);
1141 g_return_if_fail (user_data);
1143 self = MODEST_HEADER_VIEW (user_data);
1144 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1146 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1147 if (selected != NULL)
1148 path = (GtkTreePath *) selected->data;
1149 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1150 return; /* msg was _un_selected */
1152 gtk_tree_model_get (model, &iter,
1153 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1157 g_signal_emit (G_OBJECT(self),
1158 signals[HEADER_SELECTED_SIGNAL],
1161 g_object_unref (G_OBJECT (header));
1163 /* free all items in 'selected' */
1164 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1165 g_list_free (selected);
1169 /* PROTECTED method. It's useful when we want to force a given
1170 selection to reload a msg. For example if we have selected a header
1171 in offline mode, when Modest become online, we want to reload the
1172 message automatically without an user click over the header */
1174 _modest_header_view_change_selection (GtkTreeSelection *selection,
1177 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1178 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1180 on_selection_changed (selection, user_data);
1183 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1185 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1186 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1188 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1190 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1195 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1202 /* static int counter = 0; */
1204 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1205 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1206 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1210 case TNY_HEADER_FLAG_ATTACHMENTS:
1212 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1213 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1214 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1215 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1217 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1218 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1220 return cmp ? cmp : t1 - t2;
1222 case TNY_HEADER_FLAG_PRIORITY:
1223 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1224 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1225 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1226 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1228 /* This is for making priority values respect the intuitive sort relationship
1229 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1230 cmp = compare_priorities (val1, val2);
1232 return cmp ? cmp : t1 - t2;
1235 return &iter1 - &iter2; /* oughhhh */
1240 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1246 /* static int counter = 0; */
1248 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1250 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1251 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1252 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1253 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1255 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1256 val2 + modest_text_utils_get_subject_prefix_len(val2),
1263 /* Drag and drop stuff */
1265 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1266 GtkSelectionData *selection_data,
1267 guint info, guint time, gpointer data)
1269 GtkTreeModel *model = NULL;
1271 GtkTreePath *source_row = NULL;
1273 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1274 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1277 case MODEST_HEADER_ROW:
1278 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1282 gtk_tree_model_get (model, &iter,
1283 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1286 g_object_unref (G_OBJECT(hdr));
1291 g_message ("%s: default switch case.", __FUNCTION__);
1294 gtk_tree_path_free (source_row);
1297 /* Header view drag types */
1298 const GtkTargetEntry header_view_drag_types[] = {
1299 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1300 { "text/uri-list", 0, MODEST_MSG },
1304 setup_drag_and_drop (GtkTreeView *self)
1306 gtk_drag_source_set (GTK_WIDGET (self),
1308 header_view_drag_types,
1309 G_N_ELEMENTS (header_view_drag_types),
1310 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1312 g_signal_connect(G_OBJECT (self), "drag_data_get",
1313 G_CALLBACK(drag_data_get_cb), NULL);
1316 static GtkTreePath *
1317 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1319 GtkTreePath *path = NULL;
1320 GtkTreeSelection *sel = NULL;
1323 sel = gtk_tree_view_get_selection(self);
1324 rows = gtk_tree_selection_get_selected_rows (sel, model);
1326 if ((rows == NULL) || (g_list_length(rows) != 1))
1329 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1334 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1341 * This function moves the tree view scroll to the current selected
1342 * row when the widget grabs the focus
1345 on_focus_in (GtkWidget *self,
1346 GdkEventFocus *event,
1349 GtkTreeSelection *selection;
1350 GtkTreeModel *model;
1351 GList *selected = NULL;
1352 GtkTreePath *selected_path = NULL;
1354 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1358 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1359 /* If none selected yet, pick the first one */
1360 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1364 /* Return if the model is empty */
1365 if (!gtk_tree_model_get_iter_first (model, &iter))
1368 path = gtk_tree_model_get_path (model, &iter);
1369 gtk_tree_selection_select_path (selection, path);
1370 gtk_tree_path_free (path);
1373 /* Need to get the all the rows because is selection multiple */
1374 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1375 if (selected == NULL) return FALSE;
1376 selected_path = (GtkTreePath *) selected->data;
1378 /* Check if we need to scroll */
1379 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1380 GtkTreePath *start_path = NULL;
1381 GtkTreePath *end_path = NULL;
1382 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1386 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1387 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1389 /* Scroll to first path */
1390 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1398 #endif /* GTK_CHECK_VERSION */
1401 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1402 g_list_free (selected);
1408 idle_notify_headers_count_changed_destroy (gpointer data)
1410 HeadersCountChangedHelper *helper = NULL;
1412 g_return_if_fail (data != NULL);
1413 helper = (HeadersCountChangedHelper *) data;
1415 g_object_unref (helper->change);
1416 g_slice_free (HeadersCountChangedHelper, helper);
1420 idle_notify_headers_count_changed (gpointer data)
1422 TnyFolder *folder = NULL;
1423 ModestHeaderViewPrivate *priv = NULL;
1424 HeadersCountChangedHelper *helper = NULL;
1426 g_return_val_if_fail (data != NULL, FALSE);
1427 helper = (HeadersCountChangedHelper *) data;
1428 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1429 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1431 folder = tny_folder_change_get_folder (helper->change);
1433 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1435 g_mutex_lock (priv->observers_lock);
1437 /* Emit signal to evaluate how headers changes affects to the window view */
1438 gdk_threads_enter ();
1439 g_signal_emit (G_OBJECT(helper->self),
1440 signals[MSG_COUNT_CHANGED_SIGNAL],
1441 0, folder, helper->change);
1442 gdk_threads_leave ();
1444 /* Added or removed headers, so data stored on cliboard are invalid */
1445 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1446 modest_email_clipboard_clear (priv->clipboard);
1448 g_mutex_unlock (priv->observers_lock);
1454 folder_monitor_update (TnyFolderObserver *self,
1455 TnyFolderChange *change)
1457 ModestHeaderViewPrivate *priv;
1458 TnyFolderChangeChanged changed;
1459 HeadersCountChangedHelper *helper = NULL;
1461 changed = tny_folder_change_get_changed (change);
1463 /* Do not notify the observers if the folder of the header
1464 view has changed before this call to the observer
1466 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1467 if (tny_folder_change_get_folder (change) != priv->folder)
1470 /* Check folder count */
1471 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1472 (changed & TNY_FOLDER_CHANGE_CHANGED_REMOVED_HEADERS)) {
1473 helper = g_slice_new0 (HeadersCountChangedHelper);
1474 helper->self = MODEST_HEADER_VIEW(self);
1475 helper->change = g_object_ref(change);
1477 g_idle_add_full (G_PRIORITY_DEFAULT,
1478 idle_notify_headers_count_changed,
1480 idle_notify_headers_count_changed_destroy);
1485 modest_header_view_clear (ModestHeaderView *self)
1487 modest_header_view_set_folder (self, NULL, NULL, NULL);
1491 modest_header_view_copy_selection (ModestHeaderView *header_view)
1493 /* Copy selection */
1494 _clipboard_set_selected_data (header_view, FALSE);
1498 modest_header_view_cut_selection (ModestHeaderView *header_view)
1500 ModestHeaderViewPrivate *priv = NULL;
1501 GtkTreeModel *model = NULL;
1502 const gchar **hidding = NULL;
1503 guint i, n_selected;
1505 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1506 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1508 /* Copy selection */
1509 _clipboard_set_selected_data (header_view, TRUE);
1511 /* Get hidding ids */
1512 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1514 /* Clear hidding array created by previous cut operation */
1515 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1517 /* Copy hidding array */
1518 priv->n_selected = n_selected;
1519 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1520 for (i=0; i < n_selected; i++)
1521 priv->hidding_ids[i] = g_strdup(hidding[i]);
1523 /* Hide cut headers */
1524 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1525 if (GTK_IS_TREE_MODEL_FILTER (model))
1526 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1533 _clipboard_set_selected_data (ModestHeaderView *header_view,
1536 ModestHeaderViewPrivate *priv = NULL;
1537 TnyList *headers = NULL;
1539 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1540 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1542 /* Set selected data on clipboard */
1543 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1544 headers = modest_header_view_get_selected_headers (header_view);
1545 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1548 g_object_unref (headers);
1554 filter_row (GtkTreeModel *model,
1558 ModestHeaderViewPrivate *priv = NULL;
1559 TnyHeaderFlags flags;
1560 TnyHeader *header = NULL;
1563 gboolean visible = TRUE;
1564 gboolean found = FALSE;
1566 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1567 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1569 /* Get header from model */
1570 gtk_tree_model_get (model, iter,
1571 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1572 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1575 /* Hide mark as deleted heders */
1576 if (flags & TNY_HEADER_FLAG_DELETED) {
1581 /* If no data on clipboard, return always TRUE */
1582 if (modest_email_clipboard_cleared(priv->clipboard)) {
1587 /* Get message id from header (ensure is a valid id) */
1588 if (!header) return FALSE;
1589 id = g_strdup(tny_header_get_message_id (header));
1592 if (priv->hidding_ids != NULL) {
1593 for (i=0; i < priv->n_selected && !found; i++)
1594 if (priv->hidding_ids[i] != NULL && id != NULL)
1595 found = (!strcmp (priv->hidding_ids[i], id));
1603 g_object_unref (header);
1610 _clear_hidding_filter (ModestHeaderView *header_view)
1612 ModestHeaderViewPrivate *priv;
1615 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1616 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1618 if (priv->hidding_ids != NULL) {
1619 for (i=0; i < priv->n_selected; i++)
1620 g_free (priv->hidding_ids[i]);
1621 g_free(priv->hidding_ids);