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>
34 #include <tny-folder-change.h>
37 #include <modest-header-view.h>
38 #include <modest-header-view-priv.h>
39 #include <modest-dnd.h>
40 #include <modest-tny-folder.h>
42 #include <modest-main-window.h>
44 #include <modest-marshal.h>
45 #include <modest-text-utils.h>
46 #include <modest-icon-names.h>
47 #include <modest-runtime.h>
48 #include "modest-platform.h"
49 #include <modest-hbox-cell-renderer.h>
50 #include <modest-vbox-cell-renderer.h>
52 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
53 static void modest_header_view_init (ModestHeaderView *obj);
54 static void modest_header_view_finalize (GObject *obj);
55 static void modest_header_view_dispose (GObject *obj);
57 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
58 GtkTreeViewColumn *column, gpointer userdata);
60 static gint cmp_rows (GtkTreeModel *tree_model,
65 static gint cmp_subject_rows (GtkTreeModel *tree_model,
70 static gboolean filter_row (GtkTreeModel *model,
74 static void on_account_removed (TnyAccountStore *self,
78 static void on_selection_changed (GtkTreeSelection *sel,
81 static void setup_drag_and_drop (GtkTreeView *self);
83 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
85 static gboolean on_focus_in (GtkWidget *sef,
89 static void folder_monitor_update (TnyFolderObserver *self,
90 TnyFolderChange *change);
92 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
94 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
96 static void _clear_hidding_filter (ModestHeaderView *header_view);
99 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
100 struct _ModestHeaderViewPrivate {
102 ModestHeaderViewStyle style;
104 TnyFolderMonitor *monitor;
105 GMutex *observers_lock;
107 /* not unref this object, its a singlenton */
108 ModestEmailClipboard *clipboard;
110 /* Filter tree model */
114 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
115 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
117 gulong selection_changed_handler;
118 gulong acc_removed_handler;
123 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
124 struct _HeadersCountChangedHelper {
125 ModestHeaderView *self;
126 TnyFolderChange *change;
130 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
131 MODEST_TYPE_HEADER_VIEW, \
132 ModestHeaderViewPrivate))
136 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
139 HEADER_SELECTED_SIGNAL,
140 HEADER_ACTIVATED_SIGNAL,
141 ITEM_NOT_FOUND_SIGNAL,
142 MSG_COUNT_CHANGED_SIGNAL,
147 static GObjectClass *parent_class = NULL;
149 /* uncomment the following if you have defined any signals */
150 static guint signals[LAST_SIGNAL] = {0};
153 modest_header_view_get_type (void)
155 static GType my_type = 0;
157 static const GTypeInfo my_info = {
158 sizeof(ModestHeaderViewClass),
159 NULL, /* base init */
160 NULL, /* base finalize */
161 (GClassInitFunc) modest_header_view_class_init,
162 NULL, /* class finalize */
163 NULL, /* class data */
164 sizeof(ModestHeaderView),
166 (GInstanceInitFunc) modest_header_view_init,
170 static const GInterfaceInfo tny_folder_observer_info =
172 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
173 NULL, /* interface_finalize */
174 NULL /* interface_data */
176 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
180 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
181 &tny_folder_observer_info);
189 modest_header_view_class_init (ModestHeaderViewClass *klass)
191 GObjectClass *gobject_class;
192 gobject_class = (GObjectClass*) klass;
194 parent_class = g_type_class_peek_parent (klass);
195 gobject_class->finalize = modest_header_view_finalize;
196 gobject_class->dispose = modest_header_view_dispose;
198 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
200 signals[HEADER_SELECTED_SIGNAL] =
201 g_signal_new ("header_selected",
202 G_TYPE_FROM_CLASS (gobject_class),
204 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
206 g_cclosure_marshal_VOID__POINTER,
207 G_TYPE_NONE, 1, G_TYPE_POINTER);
209 signals[HEADER_ACTIVATED_SIGNAL] =
210 g_signal_new ("header_activated",
211 G_TYPE_FROM_CLASS (gobject_class),
213 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
215 g_cclosure_marshal_VOID__POINTER,
216 G_TYPE_NONE, 1, G_TYPE_POINTER);
219 signals[ITEM_NOT_FOUND_SIGNAL] =
220 g_signal_new ("item_not_found",
221 G_TYPE_FROM_CLASS (gobject_class),
223 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
225 g_cclosure_marshal_VOID__INT,
226 G_TYPE_NONE, 1, G_TYPE_INT);
228 signals[MSG_COUNT_CHANGED_SIGNAL] =
229 g_signal_new ("msg_count_changed",
230 G_TYPE_FROM_CLASS (gobject_class),
232 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
234 modest_marshal_VOID__POINTER_POINTER,
235 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
239 tny_folder_observer_init (TnyFolderObserverIface *klass)
241 klass->update_func = folder_monitor_update;
244 static GtkTreeViewColumn*
245 get_new_column (const gchar *name, GtkCellRenderer *renderer,
246 gboolean resizable, gint sort_col_id, gboolean show_as_text,
247 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
249 GtkTreeViewColumn *column;
251 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
252 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
254 gtk_tree_view_column_set_resizable (column, resizable);
256 gtk_tree_view_column_set_expand (column, TRUE);
259 gtk_tree_view_column_add_attribute (column, renderer, "text",
261 if (sort_col_id >= 0)
262 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
264 gtk_tree_view_column_set_sort_indicator (column, FALSE);
265 gtk_tree_view_column_set_reorderable (column, TRUE);
268 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
275 remove_all_columns (ModestHeaderView *obj)
277 GList *columns, *cursor;
279 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
281 for (cursor = columns; cursor; cursor = cursor->next)
282 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
283 GTK_TREE_VIEW_COLUMN(cursor->data));
284 g_list_free (columns);
288 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
290 GtkTreeModel *tree_filter, *sortable;
291 GtkTreeViewColumn *column=NULL;
292 GtkTreeSelection *selection = NULL;
293 GtkCellRenderer *renderer_msgtype,*renderer_header,
294 *renderer_attach, *renderer_compact_date_or_status;
295 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
296 *renderer_subject, *renderer_subject_box, *renderer_recpt,
298 ModestHeaderViewPrivate *priv;
299 GtkTreeViewColumn *compact_column = NULL;
302 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
304 /* FIXME: check whether these renderers need to be freed */
305 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
306 renderer_attach = gtk_cell_renderer_pixbuf_new ();
307 renderer_priority = gtk_cell_renderer_pixbuf_new ();
308 renderer_header = gtk_cell_renderer_text_new ();
310 renderer_compact_header = modest_vbox_cell_renderer_new ();
311 renderer_recpt_box = modest_hbox_cell_renderer_new ();
312 renderer_subject_box = modest_hbox_cell_renderer_new ();
313 renderer_recpt = gtk_cell_renderer_text_new ();
314 renderer_subject = gtk_cell_renderer_text_new ();
315 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
317 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
318 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
319 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
320 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
321 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
322 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
323 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
324 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
325 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
326 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
327 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
328 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
329 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
330 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
332 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
333 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
334 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
335 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
336 g_object_set(G_OBJECT(renderer_header),
337 "ellipsize", PANGO_ELLIPSIZE_END,
339 g_object_set (G_OBJECT (renderer_subject),
340 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
342 g_object_set (G_OBJECT (renderer_recpt),
343 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
345 g_object_set(G_OBJECT(renderer_compact_date_or_status),
346 "xalign", 1.0, "yalign", 0.0,
348 g_object_set (G_OBJECT (renderer_priority),
349 "yalign", 1.0, NULL);
350 g_object_set (G_OBJECT (renderer_attach),
351 "yalign", 0.0, NULL);
353 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
354 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
355 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
357 remove_all_columns (self);
359 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
360 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
361 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
362 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
364 /* Add new columns */
365 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
366 ModestHeaderViewColumn col =
367 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
369 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
370 g_printerr ("modest: invalid column %d in column list\n", col);
376 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
377 column = get_new_column (_("M"), renderer_msgtype, FALSE,
378 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
380 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
382 gtk_tree_view_column_set_fixed_width (column, 45);
385 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
386 column = get_new_column (_("A"), renderer_attach, FALSE,
387 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
389 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
391 gtk_tree_view_column_set_fixed_width (column, 45);
395 case MODEST_HEADER_VIEW_COLUMN_FROM:
396 column = get_new_column (_("From"), renderer_header, TRUE,
397 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
399 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
400 GINT_TO_POINTER(TRUE));
403 case MODEST_HEADER_VIEW_COLUMN_TO:
404 column = get_new_column (_("To"), renderer_header, TRUE,
405 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
407 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
408 GINT_TO_POINTER(FALSE));
411 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
412 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
413 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
415 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
416 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
417 compact_column = column;
420 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
421 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
422 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
424 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
425 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
426 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
427 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
428 compact_column = column;
432 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
433 column = get_new_column (_("Subject"), renderer_header, TRUE,
434 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
436 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
440 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
441 column = get_new_column (_("Received"), renderer_header, TRUE,
442 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
444 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
445 GINT_TO_POINTER(TRUE));
448 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
449 column = get_new_column (_("Sent"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
453 GINT_TO_POINTER(FALSE));
456 case MODEST_HEADER_VIEW_COLUMN_SIZE:
457 column = get_new_column (_("Size"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
463 case MODEST_HEADER_VIEW_COLUMN_STATUS:
464 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
465 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
467 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
472 g_return_val_if_reached(FALSE);
475 /* we keep the column id around */
476 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
477 GINT_TO_POINTER(col));
479 /* we need this ptr when sorting the rows */
480 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
482 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
486 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
487 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
488 (GtkTreeIterCompareFunc) cmp_rows,
489 compact_column, NULL);
490 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
491 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
492 (GtkTreeIterCompareFunc) cmp_subject_rows,
493 compact_column, NULL);
501 modest_header_view_init (ModestHeaderView *obj)
503 ModestHeaderViewPrivate *priv;
506 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
510 priv->monitor = NULL;
511 priv->observers_lock = g_mutex_new ();
515 priv->clipboard = modest_runtime_get_email_clipboard ();
516 priv->hidding_ids = NULL;
517 priv->n_selected = 0;
518 priv->selection_changed_handler = 0;
519 priv->acc_removed_handler = 0;
521 /* Sort parameters */
522 for (j=0; j < 2; j++) {
523 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
524 priv->sort_colid[j][i] = -1;
525 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
529 setup_drag_and_drop (GTK_TREE_VIEW (obj));
533 modest_header_view_dispose (GObject *obj)
535 ModestHeaderView *self;
536 ModestHeaderViewPrivate *priv;
537 GtkTreeSelection *sel;
539 self = MODEST_HEADER_VIEW(obj);
540 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
542 /* Free in the dispose to avoid unref cycles */
544 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
545 g_object_unref (G_OBJECT (priv->folder));
549 /* We need to do this here in the dispose because the
550 selection won't exist when finalizing */
551 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
552 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
553 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
554 priv->selection_changed_handler = 0;
557 G_OBJECT_CLASS(parent_class)->dispose (obj);
561 modest_header_view_finalize (GObject *obj)
563 ModestHeaderView *self;
564 ModestHeaderViewPrivate *priv;
566 self = MODEST_HEADER_VIEW(obj);
567 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
569 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
570 priv->acc_removed_handler)) {
571 g_signal_handler_disconnect (modest_runtime_get_account_store (),
572 priv->acc_removed_handler);
575 g_mutex_lock (priv->observers_lock);
577 tny_folder_monitor_stop (priv->monitor);
578 g_object_unref (G_OBJECT (priv->monitor));
580 g_mutex_unlock (priv->observers_lock);
581 g_mutex_free (priv->observers_lock);
583 /* Clear hidding array created by cut operation */
584 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
586 G_OBJECT_CLASS(parent_class)->finalize (obj);
591 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
594 GtkTreeSelection *sel;
595 ModestHeaderView *self;
596 ModestHeaderViewPrivate *priv;
598 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
601 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
602 self = MODEST_HEADER_VIEW(obj);
603 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
605 modest_header_view_set_style (self, style);
606 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
608 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
609 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
610 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
612 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
613 TRUE); /* alternating row colors */
615 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
616 priv->selection_changed_handler =
617 g_signal_connect_after (sel, "changed",
618 G_CALLBACK(on_selection_changed), self);
620 g_signal_connect (self, "row-activated",
621 G_CALLBACK (on_header_row_activated), NULL);
623 g_signal_connect (self, "focus-in-event",
624 G_CALLBACK(on_focus_in), NULL);
626 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
628 G_CALLBACK (on_account_removed),
631 return GTK_WIDGET(self);
636 modest_header_view_count_selected_headers (ModestHeaderView *self)
638 GtkTreeSelection *sel;
641 g_return_val_if_fail (self, 0);
643 /* Get selection object and check selected rows count */
644 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
645 selected_rows = gtk_tree_selection_count_selected_rows (sel);
647 return selected_rows;
651 modest_header_view_has_selected_headers (ModestHeaderView *self)
653 GtkTreeSelection *sel;
656 g_return_val_if_fail (self, FALSE);
658 /* Get selection object and check selected rows count */
659 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
660 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
667 modest_header_view_get_selected_headers (ModestHeaderView *self)
669 GtkTreeSelection *sel;
670 ModestHeaderViewPrivate *priv;
671 TnyList *header_list = NULL;
673 GList *list, *tmp = NULL;
674 GtkTreeModel *tree_model = NULL;
677 g_return_val_if_fail (self, NULL);
679 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
681 /* Get selected rows */
682 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
683 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
686 header_list = tny_simple_list_new();
688 list = g_list_reverse (list);
691 /* get header from selection */
692 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
693 gtk_tree_model_get (tree_model, &iter,
694 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
696 /* Prepend to list */
697 tny_list_prepend (header_list, G_OBJECT (header));
698 g_object_unref (G_OBJECT (header));
700 tmp = g_list_next (tmp);
703 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
710 /* scroll our list view so the selected item is visible */
712 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
714 #ifdef MODEST_PLATFORM_GNOME
716 GtkTreePath *selected_path;
717 GtkTreePath *start, *end;
721 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
722 selected_path = gtk_tree_model_get_path (model, iter);
724 start = gtk_tree_path_new ();
725 end = gtk_tree_path_new ();
727 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
729 if (gtk_tree_path_compare (selected_path, start) < 0 ||
730 gtk_tree_path_compare (end, selected_path) < 0)
731 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
732 selected_path, NULL, TRUE,
735 gtk_tree_path_free (selected_path);
736 gtk_tree_path_free (start);
737 gtk_tree_path_free (end);
739 #endif /* MODEST_PLATFORM_GNOME */
744 modest_header_view_select_next (ModestHeaderView *self)
746 GtkTreeSelection *sel;
751 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
752 path = get_selected_row (GTK_TREE_VIEW(self), &model);
753 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
754 /* Unselect previous path */
755 gtk_tree_selection_unselect_path (sel, path);
757 /* Move path down and selects new one */
758 if (gtk_tree_model_iter_next (model, &iter)) {
759 gtk_tree_selection_select_iter (sel, &iter);
760 scroll_to_selected (self, &iter, FALSE);
762 gtk_tree_path_free(path);
768 modest_header_view_select_prev (ModestHeaderView *self)
770 GtkTreeSelection *sel;
775 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
776 path = get_selected_row (GTK_TREE_VIEW(self), &model);
777 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
778 /* Unselect previous path */
779 gtk_tree_selection_unselect_path (sel, path);
782 if (gtk_tree_path_prev (path)) {
783 gtk_tree_model_get_iter (model, &iter, path);
785 /* Select the new one */
786 gtk_tree_selection_select_iter (sel, &iter);
787 scroll_to_selected (self, &iter, TRUE);
790 gtk_tree_path_free (path);
795 modest_header_view_get_columns (ModestHeaderView *self)
797 g_return_val_if_fail (self, FALSE);
798 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
803 /* modest_header_view_is_empty (ModestHeaderView *self) */
805 /* g_return_val_if_fail (self, FALSE); */
806 /* return FALSE; /\* FIXME *\/ */
811 modest_header_view_set_style (ModestHeaderView *self,
812 ModestHeaderViewStyle style)
814 ModestHeaderViewPrivate *priv;
815 gboolean show_col_headers = FALSE;
816 ModestHeaderViewStyle old_style;
818 g_return_val_if_fail (self, FALSE);
819 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
822 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
823 if (priv->style == style)
824 return TRUE; /* nothing to do */
827 case MODEST_HEADER_VIEW_STYLE_DETAILS:
828 show_col_headers = TRUE;
830 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
833 g_return_val_if_reached (FALSE);
835 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
836 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
838 old_style = priv->style;
845 ModestHeaderViewStyle
846 modest_header_view_get_style (ModestHeaderView *self)
848 g_return_val_if_fail (self, FALSE);
849 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
853 * This function sets a sortable model in the header view. It's just
854 * used for developing purposes, because it only does a
855 * gtk_tree_view_set_model
858 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
860 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
861 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
862 /* GtkTreeModel *old_model; */
863 /* ModestHeaderViewPrivate *priv; */
864 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
865 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
867 /* /\* Set new model *\/ */
868 /* gtk_tree_view_set_model (header_view, model); */
870 gtk_tree_view_set_model (header_view, model);
874 modest_header_view_get_folder (ModestHeaderView *self)
876 ModestHeaderViewPrivate *priv;
877 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
880 g_object_ref (priv->folder);
886 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
890 ModestHeaderViewPrivate *priv;
891 GList *cols, *cursor;
892 GtkTreeModel *filter_model, *sortable;
894 GtkSortType sort_type;
896 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
898 headers = TNY_LIST (tny_gtk_header_list_model_new ());
900 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
903 /* Add IDLE observer (monitor) and another folder observer for
904 new messages (self) */
905 g_mutex_lock (priv->observers_lock);
907 tny_folder_monitor_stop (priv->monitor);
908 g_object_unref (G_OBJECT (priv->monitor));
910 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
911 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
912 tny_folder_monitor_start (priv->monitor);
913 g_mutex_unlock (priv->observers_lock);
915 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
916 g_object_unref (G_OBJECT (headers));
918 /* Create a tree model filter to hide and show rows for cut operations */
919 filter_model = gtk_tree_model_filter_new (sortable, NULL);
920 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
924 g_object_unref (G_OBJECT (sortable));
926 /* install our special sorting functions */
927 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
929 /* Restore sort column id */
931 type = modest_tny_folder_guess_folder_type (folder);
932 sort_colid = modest_header_view_get_sort_column_id (self, type);
933 sort_type = modest_header_view_get_sort_type (self, type);
934 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
937 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
938 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
939 (GtkTreeIterCompareFunc) cmp_rows,
941 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
942 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
943 (GtkTreeIterCompareFunc) cmp_subject_rows,
948 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
949 g_object_unref (G_OBJECT (filter_model));
950 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
951 /* g_object_unref (G_OBJECT (sortable)); */
958 modest_header_view_sort_by_column_id (ModestHeaderView *self,
960 GtkSortType sort_type)
962 ModestHeaderViewPrivate *priv = NULL;
963 GtkTreeModel *tree_filter, *sortable = NULL;
966 /* Get model and private data */
967 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
968 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
969 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
970 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
972 /* Sort tree model */
973 type = modest_tny_folder_guess_folder_type (priv->folder);
974 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
977 /* Store new sort parameters */
978 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
980 /* Save GConf parameters */
981 /* modest_widget_memory_save (modest_runtime_get_conf(), */
982 /* G_OBJECT(self), "header-view"); */
987 modest_header_view_set_sort_params (ModestHeaderView *self,
989 GtkSortType sort_type,
992 ModestHeaderViewPrivate *priv;
993 ModestHeaderViewStyle style;
995 style = modest_header_view_get_style (self);
996 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
998 priv->sort_colid[style][type] = sort_colid;
999 priv->sort_type[style][type] = sort_type;
1003 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1006 ModestHeaderViewPrivate *priv;
1007 ModestHeaderViewStyle style;
1009 style = modest_header_view_get_style (self);
1010 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1012 return priv->sort_colid[style][type];
1016 modest_header_view_get_sort_type (ModestHeaderView *self,
1019 ModestHeaderViewPrivate *priv;
1020 ModestHeaderViewStyle style;
1022 style = modest_header_view_get_style (self);
1023 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1025 return priv->sort_type[style][type];
1029 ModestHeaderView *header_view;
1030 RefreshAsyncUserCallback cb;
1035 folder_refreshed_cb (ModestMailOperation *mail_op,
1039 ModestHeaderViewPrivate *priv;
1040 SetFolderHelper *info;
1042 info = (SetFolderHelper*) user_data;
1044 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1048 info->cb (mail_op, folder, info->user_data);
1050 /* Start the folder count changes observer. We do not need it
1051 before the refresh. Note that the monitor could still be
1052 called for this refresh but now we know that the callback
1053 was previously called */
1054 g_mutex_lock (priv->observers_lock);
1055 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1056 g_mutex_unlock (priv->observers_lock);
1063 modest_header_view_set_folder (ModestHeaderView *self,
1065 RefreshAsyncUserCallback callback,
1068 ModestHeaderViewPrivate *priv;
1069 ModestWindowMgr *mgr = NULL;
1070 GObject *source = NULL;
1071 SetFolderHelper *info;
1073 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1076 g_mutex_lock (priv->observers_lock);
1077 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1078 g_object_unref (priv->folder);
1079 priv->folder = NULL;
1080 g_mutex_unlock (priv->observers_lock);
1084 ModestMailOperation *mail_op = NULL;
1085 GtkTreeSelection *selection;
1087 /* Get main window to use it as source of mail operation */
1088 mgr = modest_runtime_get_window_mgr ();
1089 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1091 /* Set folder in the model */
1092 modest_header_view_set_folder_intern (self, folder);
1094 /* Pick my reference. Nothing to do with the mail operation */
1095 priv->folder = g_object_ref (folder);
1097 /* no message selected */
1098 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1100 info = g_malloc0 (sizeof(SetFolderHelper));
1101 info->header_view = self;
1102 info->cb = callback;
1103 info->user_data = user_data;
1105 /* bug 57631: Clear the selection if exists */
1106 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1107 gtk_tree_selection_unselect_all(selection);
1109 /* Create the mail operation (source will be the parent widget) */
1110 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1111 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1114 /* Refresh the folder asynchronously */
1115 modest_mail_operation_refresh_folder (mail_op,
1117 folder_refreshed_cb,
1121 g_object_unref (mail_op);
1123 g_mutex_lock (priv->observers_lock);
1125 if (priv->monitor) {
1126 tny_folder_monitor_stop (priv->monitor);
1127 g_object_unref (G_OBJECT (priv->monitor));
1128 priv->monitor = NULL;
1130 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1132 g_mutex_unlock (priv->observers_lock);
1137 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1138 GtkTreeViewColumn *column, gpointer userdata)
1140 ModestHeaderView *self = NULL;
1141 ModestHeaderViewPrivate *priv = NULL;
1143 GtkTreeModel *model = NULL;
1144 TnyHeader *header = NULL;
1145 TnyHeaderFlags flags;
1147 self = MODEST_HEADER_VIEW (treeview);
1148 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1150 model = gtk_tree_view_get_model (treeview);
1151 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1154 /* get the first selected item */
1155 gtk_tree_model_get (model, &iter,
1156 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1157 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1160 /* Dont open DELETED messages */
1161 if (flags & TNY_HEADER_FLAG_DELETED) {
1162 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1167 g_signal_emit (G_OBJECT(self),
1168 signals[HEADER_ACTIVATED_SIGNAL],
1174 g_object_unref (G_OBJECT (header));
1179 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1181 GtkTreeModel *model;
1182 TnyHeader *header = NULL;
1183 GtkTreePath *path = NULL;
1185 ModestHeaderView *self;
1186 ModestHeaderViewPrivate *priv;
1187 GList *selected = NULL;
1189 g_return_if_fail (sel);
1190 g_return_if_fail (user_data);
1192 self = MODEST_HEADER_VIEW (user_data);
1193 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1195 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1196 if (selected != NULL)
1197 path = (GtkTreePath *) selected->data;
1198 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1199 return; /* msg was _un_selected */
1201 gtk_tree_model_get (model, &iter,
1202 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1206 g_signal_emit (G_OBJECT(self),
1207 signals[HEADER_SELECTED_SIGNAL],
1210 g_object_unref (G_OBJECT (header));
1212 /* free all items in 'selected' */
1213 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1214 g_list_free (selected);
1218 /* PROTECTED method. It's useful when we want to force a given
1219 selection to reload a msg. For example if we have selected a header
1220 in offline mode, when Modest become online, we want to reload the
1221 message automatically without an user click over the header */
1223 _modest_header_view_change_selection (GtkTreeSelection *selection,
1226 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1227 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1229 on_selection_changed (selection, user_data);
1232 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1234 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1235 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1237 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1239 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1244 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1251 /* static int counter = 0; */
1253 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1254 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1255 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1259 case TNY_HEADER_FLAG_ATTACHMENTS:
1261 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1262 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1263 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1264 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1266 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1267 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1269 return cmp ? cmp : t1 - t2;
1271 case TNY_HEADER_FLAG_PRIORITY:
1272 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1273 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1274 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1275 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1277 /* This is for making priority values respect the intuitive sort relationship
1278 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1279 cmp = compare_priorities (val1, val2);
1281 return cmp ? cmp : t1 - t2;
1284 return &iter1 - &iter2; /* oughhhh */
1289 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1295 /* static int counter = 0; */
1297 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1299 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1300 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1301 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1302 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1304 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1305 val2 + modest_text_utils_get_subject_prefix_len(val2),
1312 /* Drag and drop stuff */
1314 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1315 GtkSelectionData *selection_data,
1316 guint info, guint time, gpointer data)
1318 GtkTreeModel *model = NULL;
1320 GtkTreePath *source_row = NULL;
1321 GtkTreeSelection *sel = NULL;
1323 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1324 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1327 case MODEST_HEADER_ROW:
1328 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1331 TnyHeader *hdr = NULL;
1332 gtk_tree_model_get (model, &iter,
1333 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1336 g_object_unref (G_OBJECT(hdr));
1341 g_message ("%s: default switch case.", __FUNCTION__);
1344 /* Set focus on next header */
1345 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1346 gtk_tree_path_next (source_row);
1347 gtk_tree_selection_select_path (sel, source_row);
1349 gtk_tree_path_free (source_row);
1352 /* Header view drag types */
1353 const GtkTargetEntry header_view_drag_types[] = {
1354 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1355 { "text/uri-list", 0, MODEST_MSG },
1359 setup_drag_and_drop (GtkTreeView *self)
1361 gtk_drag_source_set (GTK_WIDGET (self),
1363 header_view_drag_types,
1364 G_N_ELEMENTS (header_view_drag_types),
1365 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1367 g_signal_connect(G_OBJECT (self), "drag_data_get",
1368 G_CALLBACK(drag_data_get_cb), NULL);
1371 static GtkTreePath *
1372 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1374 GtkTreePath *path = NULL;
1375 GtkTreeSelection *sel = NULL;
1378 sel = gtk_tree_view_get_selection(self);
1379 rows = gtk_tree_selection_get_selected_rows (sel, model);
1381 if ((rows == NULL) || (g_list_length(rows) != 1))
1384 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1389 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1396 * This function moves the tree view scroll to the current selected
1397 * row when the widget grabs the focus
1400 on_focus_in (GtkWidget *self,
1401 GdkEventFocus *event,
1404 GtkTreeSelection *selection;
1405 GtkTreeModel *model;
1406 GList *selected = NULL;
1407 GtkTreePath *selected_path = NULL;
1409 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1413 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1414 /* If none selected yet, pick the first one */
1415 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1419 /* Return if the model is empty */
1420 if (!gtk_tree_model_get_iter_first (model, &iter))
1423 path = gtk_tree_model_get_path (model, &iter);
1424 gtk_tree_selection_select_path (selection, path);
1425 gtk_tree_path_free (path);
1428 /* Need to get the all the rows because is selection multiple */
1429 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1430 if (selected == NULL) return FALSE;
1431 selected_path = (GtkTreePath *) selected->data;
1433 /* Check if we need to scroll */
1434 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1435 GtkTreePath *start_path = NULL;
1436 GtkTreePath *end_path = NULL;
1437 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1441 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1442 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1444 /* Scroll to first path */
1445 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1454 gtk_tree_path_free (start_path);
1456 gtk_tree_path_free (end_path);
1458 #endif /* GTK_CHECK_VERSION */
1461 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1462 g_list_free (selected);
1468 idle_notify_headers_count_changed_destroy (gpointer data)
1470 HeadersCountChangedHelper *helper = NULL;
1472 g_return_if_fail (data != NULL);
1473 helper = (HeadersCountChangedHelper *) data;
1475 g_object_unref (helper->change);
1476 g_slice_free (HeadersCountChangedHelper, helper);
1480 idle_notify_headers_count_changed (gpointer data)
1482 TnyFolder *folder = NULL;
1483 ModestHeaderViewPrivate *priv = NULL;
1484 HeadersCountChangedHelper *helper = NULL;
1486 g_return_val_if_fail (data != NULL, FALSE);
1487 helper = (HeadersCountChangedHelper *) data;
1488 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1489 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1491 folder = tny_folder_change_get_folder (helper->change);
1493 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1495 g_mutex_lock (priv->observers_lock);
1497 /* Emit signal to evaluate how headers changes affects to the window view */
1498 g_signal_emit (G_OBJECT(helper->self),
1499 signals[MSG_COUNT_CHANGED_SIGNAL],
1500 0, folder, helper->change);
1502 /* Added or removed headers, so data stored on cliboard are invalid */
1503 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1504 modest_email_clipboard_clear (priv->clipboard);
1506 g_mutex_unlock (priv->observers_lock);
1512 folder_monitor_update (TnyFolderObserver *self,
1513 TnyFolderChange *change)
1515 ModestHeaderViewPrivate *priv = NULL;
1516 TnyFolderChangeChanged changed;
1517 HeadersCountChangedHelper *helper = NULL;
1519 changed = tny_folder_change_get_changed (change);
1521 /* Do not notify the observers if the folder of the header
1522 view has changed before this call to the observer
1524 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1525 if (tny_folder_change_get_folder (change) != priv->folder)
1528 /* Check folder count */
1529 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1530 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1531 helper = g_slice_new0 (HeadersCountChangedHelper);
1532 helper->self = MODEST_HEADER_VIEW(self);
1533 helper->change = g_object_ref(change);
1535 g_idle_add_full (G_PRIORITY_DEFAULT,
1536 idle_notify_headers_count_changed,
1538 idle_notify_headers_count_changed_destroy);
1543 modest_header_view_is_empty (ModestHeaderView *self)
1545 ModestHeaderViewPrivate *priv = NULL;
1547 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1553 modest_header_view_clear (ModestHeaderView *self)
1555 modest_header_view_set_folder (self, NULL, NULL, NULL);
1559 modest_header_view_copy_selection (ModestHeaderView *header_view)
1561 /* Copy selection */
1562 _clipboard_set_selected_data (header_view, FALSE);
1566 modest_header_view_cut_selection (ModestHeaderView *header_view)
1568 ModestHeaderViewPrivate *priv = NULL;
1569 const gchar **hidding = NULL;
1570 guint i, n_selected;
1572 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1573 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1575 /* Copy selection */
1576 _clipboard_set_selected_data (header_view, TRUE);
1578 /* Get hidding ids */
1579 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1581 /* Clear hidding array created by previous cut operation */
1582 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1584 /* Copy hidding array */
1585 priv->n_selected = n_selected;
1586 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1587 for (i=0; i < n_selected; i++)
1588 priv->hidding_ids[i] = g_strdup(hidding[i]);
1590 /* Hide cut headers */
1591 modest_header_view_refilter (header_view);
1598 _clipboard_set_selected_data (ModestHeaderView *header_view,
1601 ModestHeaderViewPrivate *priv = NULL;
1602 TnyList *headers = NULL;
1604 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1605 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1607 /* Set selected data on clipboard */
1608 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1609 headers = modest_header_view_get_selected_headers (header_view);
1610 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1613 g_object_unref (headers);
1619 filter_row (GtkTreeModel *model,
1623 ModestHeaderViewPrivate *priv = NULL;
1624 TnyHeaderFlags flags;
1625 TnyHeader *header = NULL;
1628 gboolean visible = TRUE;
1629 gboolean found = FALSE;
1631 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1632 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1634 /* Get header from model */
1635 gtk_tree_model_get (model, iter,
1636 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1637 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1640 /* Hide mark as deleted heders */
1641 if (flags & TNY_HEADER_FLAG_DELETED) {
1646 /* If no data on clipboard, return always TRUE */
1647 if (modest_email_clipboard_cleared(priv->clipboard)) {
1652 /* Get message id from header (ensure is a valid id) */
1653 if (!header) return FALSE;
1654 id = g_strdup(tny_header_get_message_id (header));
1657 if (priv->hidding_ids != NULL) {
1658 for (i=0; i < priv->n_selected && !found; i++)
1659 if (priv->hidding_ids[i] != NULL && id != NULL)
1660 found = (!strcmp (priv->hidding_ids[i], id));
1666 priv->empty = priv->empty && !visible;
1670 g_object_unref (header);
1677 _clear_hidding_filter (ModestHeaderView *header_view)
1679 ModestHeaderViewPrivate *priv = NULL;
1682 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1683 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1685 if (priv->hidding_ids != NULL) {
1686 for (i=0; i < priv->n_selected; i++)
1687 g_free (priv->hidding_ids[i]);
1688 g_free(priv->hidding_ids);
1693 modest_header_view_refilter (ModestHeaderView *header_view)
1695 GtkTreeModel *model = NULL;
1696 ModestHeaderViewPrivate *priv = NULL;
1698 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1699 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1703 /* Hide cut headers */
1704 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1705 if (GTK_IS_TREE_MODEL_FILTER (model))
1706 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1710 * Called when an account is removed. If I'm showing a folder of the
1711 * account that has been removed then clear the view
1714 on_account_removed (TnyAccountStore *self,
1715 TnyAccount *account,
1718 ModestHeaderViewPrivate *priv = NULL;
1720 /* Ignore changes in transport accounts */
1721 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1724 g_print ("--------------------- HEADER ---------------\n");
1726 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1729 TnyAccount *my_account;
1731 my_account = tny_folder_get_account (priv->folder);
1732 if (my_account == account)
1733 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1734 g_object_unref (account);