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;
1097 self = MODEST_HEADER_VIEW (treeview);
1098 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1100 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_INSTANCE_COLUMN,
1110 g_signal_emit (G_OBJECT(self),
1111 signals[HEADER_ACTIVATED_SIGNAL],
1115 g_object_unref (G_OBJECT (header));
1120 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1122 GtkTreeModel *model;
1124 GtkTreePath *path = NULL;
1126 ModestHeaderView *self;
1127 ModestHeaderViewPrivate *priv;
1128 GList *selected = NULL;
1130 g_return_if_fail (sel);
1131 g_return_if_fail (user_data);
1133 self = MODEST_HEADER_VIEW (user_data);
1134 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1136 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1137 if (selected != NULL)
1138 path = (GtkTreePath *) selected->data;
1139 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1140 return; /* msg was _un_selected */
1142 gtk_tree_model_get (model, &iter,
1143 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1147 g_signal_emit (G_OBJECT(self),
1148 signals[HEADER_SELECTED_SIGNAL],
1151 g_object_unref (G_OBJECT (header));
1153 /* free all items in 'selected' */
1154 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1155 g_list_free (selected);
1159 /* PROTECTED method. It's useful when we want to force a given
1160 selection to reload a msg. For example if we have selected a header
1161 in offline mode, when Modest become online, we want to reload the
1162 message automatically without an user click over the header */
1164 _modest_header_view_change_selection (GtkTreeSelection *selection,
1167 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1168 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1170 on_selection_changed (selection, user_data);
1173 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1175 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1176 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1178 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1180 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1185 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1192 /* static int counter = 0; */
1194 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1195 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1196 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1200 case TNY_HEADER_FLAG_ATTACHMENTS:
1202 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1203 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1204 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1205 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1207 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1208 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1210 return cmp ? cmp : t1 - t2;
1212 case TNY_HEADER_FLAG_PRIORITY:
1213 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1214 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1215 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1216 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1218 /* This is for making priority values respect the intuitive sort relationship
1219 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1220 cmp = compare_priorities (val1, val2);
1222 return cmp ? cmp : t1 - t2;
1225 return &iter1 - &iter2; /* oughhhh */
1230 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1236 /* static int counter = 0; */
1238 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1240 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1241 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1242 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1243 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1245 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1246 val2 + modest_text_utils_get_subject_prefix_len(val2),
1253 /* Drag and drop stuff */
1255 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1256 GtkSelectionData *selection_data,
1257 guint info, guint time, gpointer data)
1259 GtkTreeModel *model = NULL;
1261 GtkTreePath *source_row = NULL;
1263 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1264 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1267 case MODEST_HEADER_ROW:
1268 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1272 gtk_tree_model_get (model, &iter,
1273 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1276 g_object_unref (G_OBJECT(hdr));
1281 g_message ("%s: default switch case.", __FUNCTION__);
1284 gtk_tree_path_free (source_row);
1287 /* Header view drag types */
1288 const GtkTargetEntry header_view_drag_types[] = {
1289 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1290 { "text/uri-list", 0, MODEST_MSG },
1294 setup_drag_and_drop (GtkTreeView *self)
1296 gtk_drag_source_set (GTK_WIDGET (self),
1298 header_view_drag_types,
1299 G_N_ELEMENTS (header_view_drag_types),
1300 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1302 g_signal_connect(G_OBJECT (self), "drag_data_get",
1303 G_CALLBACK(drag_data_get_cb), NULL);
1306 static GtkTreePath *
1307 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1309 GtkTreePath *path = NULL;
1310 GtkTreeSelection *sel = NULL;
1313 sel = gtk_tree_view_get_selection(self);
1314 rows = gtk_tree_selection_get_selected_rows (sel, model);
1316 if ((rows == NULL) || (g_list_length(rows) != 1))
1319 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1324 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1331 * This function moves the tree view scroll to the current selected
1332 * row when the widget grabs the focus
1335 on_focus_in (GtkWidget *self,
1336 GdkEventFocus *event,
1339 GtkTreeSelection *selection;
1340 GtkTreeModel *model;
1341 GList *selected = NULL;
1342 GtkTreePath *selected_path = NULL;
1344 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1348 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1349 /* If none selected yet, pick the first one */
1350 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1354 /* Return if the model is empty */
1355 if (!gtk_tree_model_get_iter_first (model, &iter))
1358 path = gtk_tree_model_get_path (model, &iter);
1359 gtk_tree_selection_select_path (selection, path);
1360 gtk_tree_path_free (path);
1363 /* Need to get the all the rows because is selection multiple */
1364 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1365 if (selected == NULL) return FALSE;
1366 selected_path = (GtkTreePath *) selected->data;
1368 /* Check if we need to scroll */
1369 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1370 GtkTreePath *start_path = NULL;
1371 GtkTreePath *end_path = NULL;
1372 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1376 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1377 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1379 /* Scroll to first path */
1380 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1388 #endif /* GTK_CHECK_VERSION */
1391 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1392 g_list_free (selected);
1398 idle_notify_headers_count_changed_destroy (gpointer data)
1400 gdk_threads_enter ();
1402 HeadersCountChangedHelper *helper = NULL;
1404 g_return_if_fail (data != NULL);
1405 helper = (HeadersCountChangedHelper *) data;
1407 g_object_unref (helper->change);
1408 g_slice_free (HeadersCountChangedHelper, helper);
1410 gdk_threads_leave ();
1414 idle_notify_headers_count_changed (gpointer data)
1416 gdk_threads_enter ();
1418 TnyFolder *folder = NULL;
1419 ModestHeaderViewPrivate *priv = NULL;
1420 HeadersCountChangedHelper *helper = NULL;
1422 g_return_val_if_fail (data != NULL, FALSE);
1423 helper = (HeadersCountChangedHelper *) data;
1424 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1425 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1427 folder = tny_folder_change_get_folder (helper->change);
1429 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1430 g_mutex_lock (priv->observers_lock);
1432 /* Emmit signal to evaluate how headers changes affects to the window view */
1433 g_signal_emit (G_OBJECT(helper->self), signals[MSG_COUNT_CHANGED_SIGNAL], 0, folder, helper->change);
1435 /* Added or removed headers, so data stored on cliboard are invalid */
1436 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1437 modest_email_clipboard_clear (priv->clipboard);
1439 g_mutex_unlock (priv->observers_lock);
1441 gdk_threads_leave ();
1447 folder_monitor_update (TnyFolderObserver *self,
1448 TnyFolderChange *change)
1450 ModestHeaderViewPrivate *priv;
1451 TnyFolderChangeChanged changed;
1452 HeadersCountChangedHelper *helper = NULL;
1454 changed = tny_folder_change_get_changed (change);
1456 /* Do not notify the observers if the folder of the header
1457 view has changed before this call to the observer
1459 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1460 if (tny_folder_change_get_folder (change) != priv->folder)
1463 /* Check folder count */
1464 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1465 (changed & TNY_FOLDER_CHANGE_CHANGED_REMOVED_HEADERS)) {
1466 helper = g_slice_new0 (HeadersCountChangedHelper);
1467 helper->self = MODEST_HEADER_VIEW(self);
1468 helper->change = g_object_ref(change);
1470 g_idle_add_full (G_PRIORITY_DEFAULT,
1471 idle_notify_headers_count_changed,
1473 idle_notify_headers_count_changed_destroy);
1478 modest_header_view_clear (ModestHeaderView *self)
1480 modest_header_view_set_folder (self, NULL, NULL, NULL);
1484 modest_header_view_copy_selection (ModestHeaderView *header_view)
1486 /* Copy selection */
1487 _clipboard_set_selected_data (header_view, FALSE);
1491 modest_header_view_cut_selection (ModestHeaderView *header_view)
1493 ModestHeaderViewPrivate *priv = NULL;
1494 GtkTreeModel *model = NULL;
1495 const gchar **hidding = NULL;
1496 guint i, n_selected;
1498 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1499 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1501 /* Copy selection */
1502 _clipboard_set_selected_data (header_view, TRUE);
1504 /* Get hidding ids */
1505 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1507 /* Clear hidding array created by previous cut operation */
1508 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1510 /* Copy hidding array */
1511 priv->n_selected = n_selected;
1512 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1513 for (i=0; i < n_selected; i++)
1514 priv->hidding_ids[i] = g_strdup(hidding[i]);
1516 /* Hide cut headers */
1517 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1518 if (GTK_IS_TREE_MODEL_FILTER (model))
1519 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1526 _clipboard_set_selected_data (ModestHeaderView *header_view,
1529 ModestHeaderViewPrivate *priv = NULL;
1530 TnyList *headers = NULL;
1532 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1533 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1535 /* Set selected data on clipboard */
1536 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1537 headers = modest_header_view_get_selected_headers (header_view);
1538 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1541 g_object_unref (headers);
1547 filter_row (GtkTreeModel *model,
1551 ModestHeaderViewPrivate *priv = NULL;
1552 TnyHeader *header = NULL;
1555 gboolean found = FALSE;
1557 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1558 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1560 /* If no data on clipboard, return always TRUE */
1561 if (modest_email_clipboard_cleared(priv->clipboard)) return TRUE;
1563 /* Get header from model */
1564 gtk_tree_model_get (model, iter,
1565 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1568 /* Get message id from header (ensure is a valid id) */
1569 if (!header) return FALSE;
1570 id = g_strdup(tny_header_get_message_id (header));
1573 if (priv->hidding_ids != NULL)
1574 for (i=0; i < priv->n_selected && !found; i++)
1575 if (priv->hidding_ids[i] != NULL && id != NULL)
1576 found = (!strcmp (priv->hidding_ids[i], id));
1579 g_object_unref (header);
1586 _clear_hidding_filter (ModestHeaderView *header_view)
1588 ModestHeaderViewPrivate *priv;
1591 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1592 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1594 if (priv->hidding_ids != NULL) {
1595 for (i=0; i < priv->n_selected; i++)
1596 g_free (priv->hidding_ids[i]);
1597 g_free(priv->hidding_ids);