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);
98 static void modest_header_view_notify_observers(
99 ModestHeaderView *header_view,
101 const gchar *tny_folder_id);
106 HEADER_VIEW_NON_EMPTY,
111 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
112 struct _ModestHeaderViewPrivate {
114 ModestHeaderViewStyle style;
116 TnyFolderMonitor *monitor;
117 GMutex *observers_lock;
119 /*header-view-observer observer*/
120 GMutex *observer_list_lock;
121 GSList *observer_list;
123 /* not unref this object, its a singlenton */
124 ModestEmailClipboard *clipboard;
126 /* Filter tree model */
130 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
131 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
133 gulong selection_changed_handler;
134 gulong acc_removed_handler;
136 HeaderViewStatus status;
139 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
140 struct _HeadersCountChangedHelper {
141 ModestHeaderView *self;
142 TnyFolderChange *change;
146 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
147 MODEST_TYPE_HEADER_VIEW, \
148 ModestHeaderViewPrivate))
152 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
155 HEADER_SELECTED_SIGNAL,
156 HEADER_ACTIVATED_SIGNAL,
157 ITEM_NOT_FOUND_SIGNAL,
158 MSG_COUNT_CHANGED_SIGNAL,
163 static GObjectClass *parent_class = NULL;
165 /* uncomment the following if you have defined any signals */
166 static guint signals[LAST_SIGNAL] = {0};
169 modest_header_view_get_type (void)
171 static GType my_type = 0;
173 static const GTypeInfo my_info = {
174 sizeof(ModestHeaderViewClass),
175 NULL, /* base init */
176 NULL, /* base finalize */
177 (GClassInitFunc) modest_header_view_class_init,
178 NULL, /* class finalize */
179 NULL, /* class data */
180 sizeof(ModestHeaderView),
182 (GInstanceInitFunc) modest_header_view_init,
186 static const GInterfaceInfo tny_folder_observer_info =
188 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
189 NULL, /* interface_finalize */
190 NULL /* interface_data */
192 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
196 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
197 &tny_folder_observer_info);
205 modest_header_view_class_init (ModestHeaderViewClass *klass)
207 GObjectClass *gobject_class;
208 gobject_class = (GObjectClass*) klass;
210 parent_class = g_type_class_peek_parent (klass);
211 gobject_class->finalize = modest_header_view_finalize;
212 gobject_class->dispose = modest_header_view_dispose;
214 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
216 signals[HEADER_SELECTED_SIGNAL] =
217 g_signal_new ("header_selected",
218 G_TYPE_FROM_CLASS (gobject_class),
220 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
222 g_cclosure_marshal_VOID__POINTER,
223 G_TYPE_NONE, 1, G_TYPE_POINTER);
225 signals[HEADER_ACTIVATED_SIGNAL] =
226 g_signal_new ("header_activated",
227 G_TYPE_FROM_CLASS (gobject_class),
229 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
231 g_cclosure_marshal_VOID__POINTER,
232 G_TYPE_NONE, 1, G_TYPE_POINTER);
235 signals[ITEM_NOT_FOUND_SIGNAL] =
236 g_signal_new ("item_not_found",
237 G_TYPE_FROM_CLASS (gobject_class),
239 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
241 g_cclosure_marshal_VOID__INT,
242 G_TYPE_NONE, 1, G_TYPE_INT);
244 signals[MSG_COUNT_CHANGED_SIGNAL] =
245 g_signal_new ("msg_count_changed",
246 G_TYPE_FROM_CLASS (gobject_class),
248 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
250 modest_marshal_VOID__POINTER_POINTER,
251 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
255 tny_folder_observer_init (TnyFolderObserverIface *klass)
257 klass->update_func = folder_monitor_update;
260 static GtkTreeViewColumn*
261 get_new_column (const gchar *name, GtkCellRenderer *renderer,
262 gboolean resizable, gint sort_col_id, gboolean show_as_text,
263 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
265 GtkTreeViewColumn *column;
267 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
268 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
270 gtk_tree_view_column_set_resizable (column, resizable);
272 gtk_tree_view_column_set_expand (column, TRUE);
275 gtk_tree_view_column_add_attribute (column, renderer, "text",
277 if (sort_col_id >= 0)
278 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
280 gtk_tree_view_column_set_sort_indicator (column, FALSE);
281 gtk_tree_view_column_set_reorderable (column, TRUE);
284 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
291 remove_all_columns (ModestHeaderView *obj)
293 GList *columns, *cursor;
295 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
297 for (cursor = columns; cursor; cursor = cursor->next)
298 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
299 GTK_TREE_VIEW_COLUMN(cursor->data));
300 g_list_free (columns);
304 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
306 GtkTreeModel *tree_filter, *sortable;
307 GtkTreeViewColumn *column=NULL;
308 GtkTreeSelection *selection = NULL;
309 GtkCellRenderer *renderer_msgtype,*renderer_header,
310 *renderer_attach, *renderer_compact_date_or_status;
311 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
312 *renderer_subject, *renderer_subject_box, *renderer_recpt,
314 ModestHeaderViewPrivate *priv;
315 GtkTreeViewColumn *compact_column = NULL;
318 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
320 /* FIXME: check whether these renderers need to be freed */
321 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
322 renderer_attach = gtk_cell_renderer_pixbuf_new ();
323 renderer_priority = gtk_cell_renderer_pixbuf_new ();
324 renderer_header = gtk_cell_renderer_text_new ();
326 renderer_compact_header = modest_vbox_cell_renderer_new ();
327 renderer_recpt_box = modest_hbox_cell_renderer_new ();
328 renderer_subject_box = modest_hbox_cell_renderer_new ();
329 renderer_recpt = gtk_cell_renderer_text_new ();
330 renderer_subject = gtk_cell_renderer_text_new ();
331 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
333 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
334 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
335 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
336 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
337 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
338 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
339 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
340 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
341 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
342 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
343 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
344 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
345 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
346 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
348 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
349 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
350 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
351 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
352 g_object_set(G_OBJECT(renderer_header),
353 "ellipsize", PANGO_ELLIPSIZE_END,
355 g_object_set (G_OBJECT (renderer_subject),
356 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
358 g_object_set (G_OBJECT (renderer_recpt),
359 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
361 g_object_set(G_OBJECT(renderer_compact_date_or_status),
362 "xalign", 1.0, "yalign", 0.0,
364 g_object_set (G_OBJECT (renderer_priority),
365 "yalign", 1.0, NULL);
366 g_object_set (G_OBJECT (renderer_attach),
367 "yalign", 0.0, NULL);
369 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
370 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
371 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
373 remove_all_columns (self);
375 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
376 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
377 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
378 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
380 /* Add new columns */
381 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
382 ModestHeaderViewColumn col =
383 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
385 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
386 g_printerr ("modest: invalid column %d in column list\n", col);
392 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
393 column = get_new_column (_("M"), renderer_msgtype, FALSE,
394 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
396 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
398 gtk_tree_view_column_set_fixed_width (column, 45);
401 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
402 column = get_new_column (_("A"), renderer_attach, FALSE,
403 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
405 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
407 gtk_tree_view_column_set_fixed_width (column, 45);
411 case MODEST_HEADER_VIEW_COLUMN_FROM:
412 column = get_new_column (_("From"), renderer_header, TRUE,
413 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
415 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
416 GINT_TO_POINTER(TRUE));
419 case MODEST_HEADER_VIEW_COLUMN_TO:
420 column = get_new_column (_("To"), renderer_header, TRUE,
421 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
423 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
424 GINT_TO_POINTER(FALSE));
427 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
428 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
429 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
431 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
432 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
433 compact_column = column;
436 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
437 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
438 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
440 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
441 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
442 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
443 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
444 compact_column = column;
448 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
449 column = get_new_column (_("Subject"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
456 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
457 column = get_new_column (_("Received"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
461 GINT_TO_POINTER(TRUE));
464 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
465 column = get_new_column (_("Sent"), renderer_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
469 GINT_TO_POINTER(FALSE));
472 case MODEST_HEADER_VIEW_COLUMN_SIZE:
473 column = get_new_column (_("Size"), renderer_header, TRUE,
474 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
476 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
479 case MODEST_HEADER_VIEW_COLUMN_STATUS:
480 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
481 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
483 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
488 g_return_val_if_reached(FALSE);
491 /* we keep the column id around */
492 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
493 GINT_TO_POINTER(col));
495 /* we need this ptr when sorting the rows */
496 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
498 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
502 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
503 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
504 (GtkTreeIterCompareFunc) cmp_rows,
505 compact_column, NULL);
506 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
507 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
508 (GtkTreeIterCompareFunc) cmp_subject_rows,
509 compact_column, NULL);
517 modest_header_view_init (ModestHeaderView *obj)
519 ModestHeaderViewPrivate *priv;
522 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
526 priv->monitor = NULL;
527 priv->observers_lock = g_mutex_new ();
529 priv->status = HEADER_VIEW_INIT;
531 priv->observer_list_lock = g_mutex_new();
532 priv->observer_list = NULL;
534 priv->clipboard = modest_runtime_get_email_clipboard ();
535 priv->hidding_ids = NULL;
536 priv->n_selected = 0;
537 priv->selection_changed_handler = 0;
538 priv->acc_removed_handler = 0;
540 /* Sort parameters */
541 for (j=0; j < 2; j++) {
542 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
543 priv->sort_colid[j][i] = -1;
544 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
548 setup_drag_and_drop (GTK_TREE_VIEW (obj));
552 modest_header_view_dispose (GObject *obj)
554 ModestHeaderView *self;
555 ModestHeaderViewPrivate *priv;
556 GtkTreeSelection *sel;
558 self = MODEST_HEADER_VIEW(obj);
559 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
561 /* Free in the dispose to avoid unref cycles */
563 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
564 g_object_unref (G_OBJECT (priv->folder));
568 /* We need to do this here in the dispose because the
569 selection won't exist when finalizing */
570 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
571 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
572 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
573 priv->selection_changed_handler = 0;
576 G_OBJECT_CLASS(parent_class)->dispose (obj);
580 modest_header_view_finalize (GObject *obj)
582 ModestHeaderView *self;
583 ModestHeaderViewPrivate *priv;
585 self = MODEST_HEADER_VIEW(obj);
586 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
588 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
589 priv->acc_removed_handler)) {
590 g_signal_handler_disconnect (modest_runtime_get_account_store (),
591 priv->acc_removed_handler);
594 /* There is no need to lock because there should not be any
595 * reference to self now. */
596 g_mutex_free(priv->observer_list_lock);
597 g_slist_free(priv->observer_list);
599 g_mutex_lock (priv->observers_lock);
601 tny_folder_monitor_stop (priv->monitor);
602 g_object_unref (G_OBJECT (priv->monitor));
604 g_mutex_unlock (priv->observers_lock);
605 g_mutex_free (priv->observers_lock);
607 /* Clear hidding array created by cut operation */
608 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
610 G_OBJECT_CLASS(parent_class)->finalize (obj);
615 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
618 GtkTreeSelection *sel;
619 ModestHeaderView *self;
620 ModestHeaderViewPrivate *priv;
622 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
625 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
626 self = MODEST_HEADER_VIEW(obj);
627 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
629 modest_header_view_set_style (self, style);
630 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
632 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
633 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
634 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
636 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
637 TRUE); /* alternating row colors */
639 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
640 priv->selection_changed_handler =
641 g_signal_connect_after (sel, "changed",
642 G_CALLBACK(on_selection_changed), self);
644 g_signal_connect (self, "row-activated",
645 G_CALLBACK (on_header_row_activated), NULL);
647 g_signal_connect (self, "focus-in-event",
648 G_CALLBACK(on_focus_in), NULL);
650 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
652 G_CALLBACK (on_account_removed),
655 return GTK_WIDGET(self);
660 modest_header_view_count_selected_headers (ModestHeaderView *self)
662 GtkTreeSelection *sel;
665 g_return_val_if_fail (self, 0);
667 /* Get selection object and check selected rows count */
668 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
669 selected_rows = gtk_tree_selection_count_selected_rows (sel);
671 return selected_rows;
675 modest_header_view_has_selected_headers (ModestHeaderView *self)
677 GtkTreeSelection *sel;
680 g_return_val_if_fail (self, FALSE);
682 /* Get selection object and check selected rows count */
683 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
684 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
691 modest_header_view_get_selected_headers (ModestHeaderView *self)
693 GtkTreeSelection *sel;
694 ModestHeaderViewPrivate *priv;
695 TnyList *header_list = NULL;
697 GList *list, *tmp = NULL;
698 GtkTreeModel *tree_model = NULL;
701 g_return_val_if_fail (self, NULL);
703 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
705 /* Get selected rows */
706 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
707 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
710 header_list = tny_simple_list_new();
712 list = g_list_reverse (list);
715 /* get header from selection */
716 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
717 gtk_tree_model_get (tree_model, &iter,
718 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
720 /* Prepend to list */
721 tny_list_prepend (header_list, G_OBJECT (header));
722 g_object_unref (G_OBJECT (header));
724 tmp = g_list_next (tmp);
727 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
734 /* scroll our list view so the selected item is visible */
736 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
738 #ifdef MODEST_PLATFORM_GNOME
740 GtkTreePath *selected_path;
741 GtkTreePath *start, *end;
745 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
746 selected_path = gtk_tree_model_get_path (model, iter);
748 start = gtk_tree_path_new ();
749 end = gtk_tree_path_new ();
751 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
753 if (gtk_tree_path_compare (selected_path, start) < 0 ||
754 gtk_tree_path_compare (end, selected_path) < 0)
755 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
756 selected_path, NULL, TRUE,
759 gtk_tree_path_free (selected_path);
760 gtk_tree_path_free (start);
761 gtk_tree_path_free (end);
763 #endif /* MODEST_PLATFORM_GNOME */
768 modest_header_view_select_next (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);
781 /* Move path down and selects new one */
782 if (gtk_tree_model_iter_next (model, &iter)) {
783 gtk_tree_selection_select_iter (sel, &iter);
784 scroll_to_selected (self, &iter, FALSE);
786 gtk_tree_path_free(path);
792 modest_header_view_select_prev (ModestHeaderView *self)
794 GtkTreeSelection *sel;
799 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
800 path = get_selected_row (GTK_TREE_VIEW(self), &model);
801 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
802 /* Unselect previous path */
803 gtk_tree_selection_unselect_path (sel, path);
806 if (gtk_tree_path_prev (path)) {
807 gtk_tree_model_get_iter (model, &iter, path);
809 /* Select the new one */
810 gtk_tree_selection_select_iter (sel, &iter);
811 scroll_to_selected (self, &iter, TRUE);
814 gtk_tree_path_free (path);
819 modest_header_view_get_columns (ModestHeaderView *self)
821 g_return_val_if_fail (self, FALSE);
822 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
828 modest_header_view_set_style (ModestHeaderView *self,
829 ModestHeaderViewStyle style)
831 ModestHeaderViewPrivate *priv;
832 gboolean show_col_headers = FALSE;
833 ModestHeaderViewStyle old_style;
835 g_return_val_if_fail (self, FALSE);
836 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
839 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
840 if (priv->style == style)
841 return TRUE; /* nothing to do */
844 case MODEST_HEADER_VIEW_STYLE_DETAILS:
845 show_col_headers = TRUE;
847 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
850 g_return_val_if_reached (FALSE);
852 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
853 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
855 old_style = priv->style;
862 ModestHeaderViewStyle
863 modest_header_view_get_style (ModestHeaderView *self)
865 g_return_val_if_fail (self, FALSE);
866 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
870 * This function sets a sortable model in the header view. It's just
871 * used for developing purposes, because it only does a
872 * gtk_tree_view_set_model
875 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
877 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
878 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
879 /* GtkTreeModel *old_model; */
880 /* ModestHeaderViewPrivate *priv; */
881 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
882 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
884 /* /\* Set new model *\/ */
885 /* gtk_tree_view_set_model (header_view, model); */
887 gtk_tree_view_set_model (header_view, model);
891 modest_header_view_get_folder (ModestHeaderView *self)
893 ModestHeaderViewPrivate *priv;
894 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
897 g_object_ref (priv->folder);
903 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
907 ModestHeaderViewPrivate *priv;
908 GList *cols, *cursor;
909 GtkTreeModel *filter_model, *sortable;
911 GtkSortType sort_type;
913 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
915 headers = TNY_LIST (tny_gtk_header_list_model_new ());
917 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
920 /* Add IDLE observer (monitor) and another folder observer for
921 new messages (self) */
922 g_mutex_lock (priv->observers_lock);
924 tny_folder_monitor_stop (priv->monitor);
925 g_object_unref (G_OBJECT (priv->monitor));
927 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
928 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
929 tny_folder_monitor_start (priv->monitor);
930 g_mutex_unlock (priv->observers_lock);
932 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
933 g_object_unref (G_OBJECT (headers));
935 /* Create a tree model filter to hide and show rows for cut operations */
936 filter_model = gtk_tree_model_filter_new (sortable, NULL);
937 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
941 g_object_unref (G_OBJECT (sortable));
943 /* install our special sorting functions */
944 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
946 /* Restore sort column id */
948 type = modest_tny_folder_guess_folder_type (folder);
949 sort_colid = modest_header_view_get_sort_column_id (self, type);
950 sort_type = modest_header_view_get_sort_type (self, type);
951 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
954 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
955 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
956 (GtkTreeIterCompareFunc) cmp_rows,
958 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
959 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
960 (GtkTreeIterCompareFunc) cmp_subject_rows,
965 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
966 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
967 tny_folder_get_id(folder));
968 g_object_unref (G_OBJECT (filter_model));
969 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
970 /* g_object_unref (G_OBJECT (sortable)); */
977 modest_header_view_sort_by_column_id (ModestHeaderView *self,
979 GtkSortType sort_type)
981 ModestHeaderViewPrivate *priv = NULL;
982 GtkTreeModel *tree_filter, *sortable = NULL;
985 /* Get model and private data */
986 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
987 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
988 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
989 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
991 /* Sort tree model */
992 type = modest_tny_folder_guess_folder_type (priv->folder);
993 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
996 /* Store new sort parameters */
997 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
999 /* Save GConf parameters */
1000 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1001 /* G_OBJECT(self), "header-view"); */
1006 modest_header_view_set_sort_params (ModestHeaderView *self,
1008 GtkSortType sort_type,
1011 ModestHeaderViewPrivate *priv;
1012 ModestHeaderViewStyle style;
1014 style = modest_header_view_get_style (self);
1015 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1017 priv->sort_colid[style][type] = sort_colid;
1018 priv->sort_type[style][type] = sort_type;
1022 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1025 ModestHeaderViewPrivate *priv;
1026 ModestHeaderViewStyle style;
1028 style = modest_header_view_get_style (self);
1029 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1031 return priv->sort_colid[style][type];
1035 modest_header_view_get_sort_type (ModestHeaderView *self,
1038 ModestHeaderViewPrivate *priv;
1039 ModestHeaderViewStyle style;
1041 style = modest_header_view_get_style (self);
1042 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1044 return priv->sort_type[style][type];
1048 ModestHeaderView *header_view;
1049 RefreshAsyncUserCallback cb;
1054 folder_refreshed_cb (ModestMailOperation *mail_op,
1058 ModestHeaderViewPrivate *priv;
1059 SetFolderHelper *info;
1061 info = (SetFolderHelper*) user_data;
1063 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1067 info->cb (mail_op, folder, info->user_data);
1069 /* Start the folder count changes observer. We do not need it
1070 before the refresh. Note that the monitor could still be
1071 called for this refresh but now we know that the callback
1072 was previously called */
1073 g_mutex_lock (priv->observers_lock);
1074 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1075 g_mutex_unlock (priv->observers_lock);
1082 modest_header_view_set_folder (ModestHeaderView *self,
1084 RefreshAsyncUserCallback callback,
1087 ModestHeaderViewPrivate *priv;
1088 ModestWindowMgr *mgr = NULL;
1089 GObject *source = NULL;
1090 SetFolderHelper *info;
1092 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1095 g_mutex_lock (priv->observers_lock);
1096 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1097 g_object_unref (priv->folder);
1098 priv->folder = NULL;
1099 g_mutex_unlock (priv->observers_lock);
1103 ModestMailOperation *mail_op = NULL;
1104 GtkTreeSelection *selection;
1106 /* Get main window to use it as source of mail operation */
1107 mgr = modest_runtime_get_window_mgr ();
1108 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1110 /* Set folder in the model */
1111 modest_header_view_set_folder_intern (self, folder);
1113 /* Pick my reference. Nothing to do with the mail operation */
1114 priv->folder = g_object_ref (folder);
1116 /* no message selected */
1117 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1119 info = g_malloc0 (sizeof(SetFolderHelper));
1120 info->header_view = self;
1121 info->cb = callback;
1122 info->user_data = user_data;
1124 /* bug 57631: Clear the selection if exists */
1125 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1126 gtk_tree_selection_unselect_all(selection);
1128 /* Create the mail operation (source will be the parent widget) */
1129 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1130 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1133 /* Refresh the folder asynchronously */
1134 modest_mail_operation_refresh_folder (mail_op,
1136 folder_refreshed_cb,
1140 g_object_unref (mail_op);
1142 g_mutex_lock (priv->observers_lock);
1144 if (priv->monitor) {
1145 tny_folder_monitor_stop (priv->monitor);
1146 g_object_unref (G_OBJECT (priv->monitor));
1147 priv->monitor = NULL;
1149 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1151 modest_header_view_notify_observers(self, NULL, NULL);
1153 g_mutex_unlock (priv->observers_lock);
1158 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1159 GtkTreeViewColumn *column, gpointer userdata)
1161 ModestHeaderView *self = NULL;
1162 ModestHeaderViewPrivate *priv = NULL;
1164 GtkTreeModel *model = NULL;
1165 TnyHeader *header = NULL;
1166 TnyHeaderFlags flags;
1168 self = MODEST_HEADER_VIEW (treeview);
1169 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1171 model = gtk_tree_view_get_model (treeview);
1172 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1175 /* get the first selected item */
1176 gtk_tree_model_get (model, &iter,
1177 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1178 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1181 /* Dont open DELETED messages */
1182 if (flags & TNY_HEADER_FLAG_DELETED) {
1183 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1188 g_signal_emit (G_OBJECT(self),
1189 signals[HEADER_ACTIVATED_SIGNAL],
1195 g_object_unref (G_OBJECT (header));
1200 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1202 GtkTreeModel *model;
1203 TnyHeader *header = NULL;
1204 GtkTreePath *path = NULL;
1206 ModestHeaderView *self;
1207 ModestHeaderViewPrivate *priv;
1208 GList *selected = NULL;
1210 g_return_if_fail (sel);
1211 g_return_if_fail (user_data);
1213 self = MODEST_HEADER_VIEW (user_data);
1214 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1216 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1217 if (selected != NULL)
1218 path = (GtkTreePath *) selected->data;
1219 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1220 return; /* msg was _un_selected */
1222 gtk_tree_model_get (model, &iter,
1223 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1227 g_signal_emit (G_OBJECT(self),
1228 signals[HEADER_SELECTED_SIGNAL],
1231 g_object_unref (G_OBJECT (header));
1233 /* free all items in 'selected' */
1234 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1235 g_list_free (selected);
1239 /* PROTECTED method. It's useful when we want to force a given
1240 selection to reload a msg. For example if we have selected a header
1241 in offline mode, when Modest become online, we want to reload the
1242 message automatically without an user click over the header */
1244 _modest_header_view_change_selection (GtkTreeSelection *selection,
1247 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1248 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1250 on_selection_changed (selection, user_data);
1253 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1255 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1256 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1258 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1260 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1265 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1272 /* static int counter = 0; */
1274 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1275 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1276 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1280 case TNY_HEADER_FLAG_ATTACHMENTS:
1282 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1283 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1284 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1285 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1287 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1288 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1290 return cmp ? cmp : t1 - t2;
1292 case TNY_HEADER_FLAG_PRIORITY:
1293 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1294 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1295 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1296 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1298 /* This is for making priority values respect the intuitive sort relationship
1299 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1300 cmp = compare_priorities (val1, val2);
1302 return cmp ? cmp : t1 - t2;
1305 return &iter1 - &iter2; /* oughhhh */
1310 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1316 /* static int counter = 0; */
1318 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1320 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1321 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1322 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1323 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1325 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1326 val2 + modest_text_utils_get_subject_prefix_len(val2),
1333 /* Drag and drop stuff */
1335 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1336 GtkSelectionData *selection_data,
1337 guint info, guint time, gpointer data)
1339 GtkTreeModel *model = NULL;
1341 GtkTreePath *source_row = NULL;
1342 /* GtkTreeSelection *sel = NULL;*/
1344 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1346 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1349 case MODEST_HEADER_ROW:
1350 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1353 TnyHeader *hdr = NULL;
1354 gtk_tree_model_get (model, &iter,
1355 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1358 g_object_unref (G_OBJECT(hdr));
1363 g_message ("%s: default switch case.", __FUNCTION__);
1366 /* commenting out the next, fixes NB#62963 */
1368 /* Set focus on next header */
1369 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1370 gtk_tree_path_next (source_row);
1371 gtk_tree_selection_select_path (sel, source_row);
1373 gtk_tree_path_free (source_row);
1377 /* Header view drag types */
1378 const GtkTargetEntry header_view_drag_types[] = {
1379 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1380 { "text/uri-list", 0, MODEST_MSG },
1384 setup_drag_and_drop (GtkTreeView *self)
1386 gtk_drag_source_set (GTK_WIDGET (self),
1388 header_view_drag_types,
1389 G_N_ELEMENTS (header_view_drag_types),
1390 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1392 g_signal_connect(G_OBJECT (self), "drag_data_get",
1393 G_CALLBACK(drag_data_get_cb), NULL);
1396 static GtkTreePath *
1397 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1399 GtkTreePath *path = NULL;
1400 GtkTreeSelection *sel = NULL;
1403 sel = gtk_tree_view_get_selection(self);
1404 rows = gtk_tree_selection_get_selected_rows (sel, model);
1406 if ((rows == NULL) || (g_list_length(rows) != 1))
1409 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1414 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1421 * This function moves the tree view scroll to the current selected
1422 * row when the widget grabs the focus
1425 on_focus_in (GtkWidget *self,
1426 GdkEventFocus *event,
1429 GtkTreeSelection *selection;
1430 GtkTreeModel *model;
1431 GList *selected = NULL;
1432 GtkTreePath *selected_path = NULL;
1434 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1438 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1439 /* If none selected yet, pick the first one */
1440 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1444 /* Return if the model is empty */
1445 if (!gtk_tree_model_get_iter_first (model, &iter))
1448 path = gtk_tree_model_get_path (model, &iter);
1449 gtk_tree_selection_select_path (selection, path);
1450 gtk_tree_path_free (path);
1453 /* Need to get the all the rows because is selection multiple */
1454 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1455 if (selected == NULL) return FALSE;
1456 selected_path = (GtkTreePath *) selected->data;
1458 /* Check if we need to scroll */
1459 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1460 GtkTreePath *start_path = NULL;
1461 GtkTreePath *end_path = NULL;
1462 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1466 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1467 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1469 /* Scroll to first path */
1470 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1479 gtk_tree_path_free (start_path);
1481 gtk_tree_path_free (end_path);
1483 #endif /* GTK_CHECK_VERSION */
1486 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1487 g_list_free (selected);
1493 idle_notify_headers_count_changed_destroy (gpointer data)
1495 HeadersCountChangedHelper *helper = NULL;
1497 g_return_if_fail (data != NULL);
1498 helper = (HeadersCountChangedHelper *) data;
1500 g_object_unref (helper->change);
1501 g_slice_free (HeadersCountChangedHelper, helper);
1505 idle_notify_headers_count_changed (gpointer data)
1507 TnyFolder *folder = NULL;
1508 ModestHeaderViewPrivate *priv = NULL;
1509 HeadersCountChangedHelper *helper = NULL;
1511 g_return_val_if_fail (data != NULL, FALSE);
1512 helper = (HeadersCountChangedHelper *) data;
1513 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1514 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1516 folder = tny_folder_change_get_folder (helper->change);
1518 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1520 g_mutex_lock (priv->observers_lock);
1522 /* Emit signal to evaluate how headers changes affects to the window view */
1523 g_signal_emit (G_OBJECT(helper->self),
1524 signals[MSG_COUNT_CHANGED_SIGNAL],
1525 0, folder, helper->change);
1527 /* Added or removed headers, so data stored on cliboard are invalid */
1528 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1529 modest_email_clipboard_clear (priv->clipboard);
1531 g_mutex_unlock (priv->observers_lock);
1537 folder_monitor_update (TnyFolderObserver *self,
1538 TnyFolderChange *change)
1540 ModestHeaderViewPrivate *priv = NULL;
1541 TnyFolderChangeChanged changed;
1542 HeadersCountChangedHelper *helper = NULL;
1544 changed = tny_folder_change_get_changed (change);
1546 /* Do not notify the observers if the folder of the header
1547 view has changed before this call to the observer
1549 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1550 if (tny_folder_change_get_folder (change) != priv->folder)
1553 /* Check folder count */
1554 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1555 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1556 helper = g_slice_new0 (HeadersCountChangedHelper);
1557 helper->self = MODEST_HEADER_VIEW(self);
1558 helper->change = g_object_ref(change);
1560 g_idle_add_full (G_PRIORITY_DEFAULT,
1561 idle_notify_headers_count_changed,
1563 idle_notify_headers_count_changed_destroy);
1568 modest_header_view_is_empty (ModestHeaderView *self)
1570 ModestHeaderViewPrivate *priv = NULL;
1572 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1574 return priv->status == HEADER_VIEW_EMPTY;
1578 modest_header_view_clear (ModestHeaderView *self)
1580 modest_header_view_set_folder (self, NULL, NULL, NULL);
1584 modest_header_view_copy_selection (ModestHeaderView *header_view)
1586 /* Copy selection */
1587 _clipboard_set_selected_data (header_view, FALSE);
1591 modest_header_view_cut_selection (ModestHeaderView *header_view)
1593 ModestHeaderViewPrivate *priv = NULL;
1594 const gchar **hidding = NULL;
1595 guint i, n_selected;
1597 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1598 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1600 /* Copy selection */
1601 _clipboard_set_selected_data (header_view, TRUE);
1603 /* Get hidding ids */
1604 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1606 /* Clear hidding array created by previous cut operation */
1607 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1609 /* Copy hidding array */
1610 priv->n_selected = n_selected;
1611 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1612 for (i=0; i < n_selected; i++)
1613 priv->hidding_ids[i] = g_strdup(hidding[i]);
1615 /* Hide cut headers */
1616 modest_header_view_refilter (header_view);
1623 _clipboard_set_selected_data (ModestHeaderView *header_view,
1626 ModestHeaderViewPrivate *priv = NULL;
1627 TnyList *headers = NULL;
1629 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1630 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1632 /* Set selected data on clipboard */
1633 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1634 headers = modest_header_view_get_selected_headers (header_view);
1635 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1638 g_object_unref (headers);
1644 filter_row (GtkTreeModel *model,
1648 ModestHeaderViewPrivate *priv = NULL;
1649 TnyHeaderFlags flags;
1650 TnyHeader *header = NULL;
1653 gboolean visible = TRUE;
1654 gboolean found = FALSE;
1656 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1657 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1659 /* Get header from model */
1660 gtk_tree_model_get (model, iter,
1661 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1662 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1665 /* Hide mark as deleted heders */
1666 if (flags & TNY_HEADER_FLAG_DELETED) {
1671 /* If no data on clipboard, return always TRUE */
1672 if (modest_email_clipboard_cleared(priv->clipboard)) {
1677 /* Get message id from header (ensure is a valid id) */
1678 if (!header) return FALSE;
1679 id = g_strdup(tny_header_get_message_id (header));
1682 if (priv->hidding_ids != NULL) {
1683 for (i=0; i < priv->n_selected && !found; i++)
1684 if (priv->hidding_ids[i] != NULL && id != NULL)
1685 found = (!strcmp (priv->hidding_ids[i], id));
1691 priv->status = priv->status && !visible;
1695 g_object_unref (header);
1702 _clear_hidding_filter (ModestHeaderView *header_view)
1704 ModestHeaderViewPrivate *priv = NULL;
1707 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1708 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1710 if (priv->hidding_ids != NULL) {
1711 for (i=0; i < priv->n_selected; i++)
1712 g_free (priv->hidding_ids[i]);
1713 g_free(priv->hidding_ids);
1718 modest_header_view_refilter (ModestHeaderView *header_view)
1720 GtkTreeModel *model = NULL;
1721 ModestHeaderViewPrivate *priv = NULL;
1723 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1724 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1726 priv->status = HEADER_VIEW_EMPTY;
1728 /* Hide cut headers */
1729 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1730 if (GTK_IS_TREE_MODEL_FILTER (model))
1731 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1735 * Called when an account is removed. If I'm showing a folder of the
1736 * account that has been removed then clear the view
1739 on_account_removed (TnyAccountStore *self,
1740 TnyAccount *account,
1743 ModestHeaderViewPrivate *priv = NULL;
1745 /* Ignore changes in transport accounts */
1746 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1749 g_print ("--------------------- HEADER ---------------\n");
1751 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1754 TnyAccount *my_account;
1756 my_account = tny_folder_get_account (priv->folder);
1757 if (my_account == account)
1758 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1759 g_object_unref (account);
1763 void modest_header_view_add_observer(
1764 ModestHeaderView *header_view,
1765 ModestHeaderViewObserver *observer)
1767 ModestHeaderViewPrivate *priv = NULL;
1769 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1770 g_assert(observer != NULL);
1771 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1773 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1775 g_mutex_lock(priv->observer_list_lock);
1776 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1777 g_mutex_unlock(priv->observer_list_lock);
1780 void modest_header_view_remove_observer(
1781 ModestHeaderView *header_view,
1782 ModestHeaderViewObserver *observer)
1784 ModestHeaderViewPrivate *priv = NULL;
1786 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1787 g_assert(observer != NULL);
1788 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1790 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1792 g_mutex_lock(priv->observer_list_lock);
1793 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1794 g_mutex_unlock(priv->observer_list_lock);
1797 static void modest_header_view_notify_observers(
1798 ModestHeaderView *header_view,
1799 GtkTreeModel *model,
1800 const gchar *tny_folder_id)
1802 ModestHeaderViewPrivate *priv = NULL;
1804 ModestHeaderViewObserver *observer;
1806 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1808 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1810 g_mutex_lock(priv->observer_list_lock);
1811 iter = priv->observer_list;
1812 while(iter != NULL){
1813 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1814 modest_header_view_observer_update(observer, model,
1816 iter = g_slist_next(iter);
1818 g_mutex_unlock(priv->observer_list_lock);