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 /* Init filter_row function to examine empty status */
936 priv->status = HEADER_VIEW_INIT;
938 /* Create a tree model filter to hide and show rows for cut operations */
939 filter_model = gtk_tree_model_filter_new (sortable, NULL);
940 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
944 g_object_unref (G_OBJECT (sortable));
946 /* install our special sorting functions */
947 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
949 /* Restore sort column id */
951 type = modest_tny_folder_guess_folder_type (folder);
952 sort_colid = modest_header_view_get_sort_column_id (self, type);
953 sort_type = modest_header_view_get_sort_type (self, type);
954 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
957 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
958 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
959 (GtkTreeIterCompareFunc) cmp_rows,
961 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
962 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
963 (GtkTreeIterCompareFunc) cmp_subject_rows,
968 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
969 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
970 tny_folder_get_id(folder));
971 g_object_unref (G_OBJECT (filter_model));
972 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
973 /* g_object_unref (G_OBJECT (sortable)); */
980 modest_header_view_sort_by_column_id (ModestHeaderView *self,
982 GtkSortType sort_type)
984 ModestHeaderViewPrivate *priv = NULL;
985 GtkTreeModel *tree_filter, *sortable = NULL;
988 /* Get model and private data */
989 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
990 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
991 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
992 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
994 /* Sort tree model */
995 type = modest_tny_folder_guess_folder_type (priv->folder);
996 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
999 /* Store new sort parameters */
1000 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1002 /* Save GConf parameters */
1003 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1004 /* G_OBJECT(self), "header-view"); */
1009 modest_header_view_set_sort_params (ModestHeaderView *self,
1011 GtkSortType sort_type,
1014 ModestHeaderViewPrivate *priv;
1015 ModestHeaderViewStyle style;
1017 style = modest_header_view_get_style (self);
1018 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1020 priv->sort_colid[style][type] = sort_colid;
1021 priv->sort_type[style][type] = sort_type;
1025 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1028 ModestHeaderViewPrivate *priv;
1029 ModestHeaderViewStyle style;
1031 style = modest_header_view_get_style (self);
1032 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1034 return priv->sort_colid[style][type];
1038 modest_header_view_get_sort_type (ModestHeaderView *self,
1041 ModestHeaderViewPrivate *priv;
1042 ModestHeaderViewStyle style;
1044 style = modest_header_view_get_style (self);
1045 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1047 return priv->sort_type[style][type];
1051 ModestHeaderView *header_view;
1052 RefreshAsyncUserCallback cb;
1057 folder_refreshed_cb (ModestMailOperation *mail_op,
1061 ModestHeaderViewPrivate *priv;
1062 SetFolderHelper *info;
1064 info = (SetFolderHelper*) user_data;
1066 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1070 info->cb (mail_op, folder, info->user_data);
1072 /* Start the folder count changes observer. We do not need it
1073 before the refresh. Note that the monitor could still be
1074 called for this refresh but now we know that the callback
1075 was previously called */
1076 g_mutex_lock (priv->observers_lock);
1077 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1078 g_mutex_unlock (priv->observers_lock);
1085 modest_header_view_set_folder (ModestHeaderView *self,
1087 RefreshAsyncUserCallback callback,
1090 ModestHeaderViewPrivate *priv;
1091 ModestWindowMgr *mgr = NULL;
1092 GObject *source = NULL;
1093 SetFolderHelper *info;
1095 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1098 g_mutex_lock (priv->observers_lock);
1099 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1100 g_object_unref (priv->folder);
1101 priv->folder = NULL;
1102 g_mutex_unlock (priv->observers_lock);
1106 ModestMailOperation *mail_op = NULL;
1107 GtkTreeSelection *selection;
1109 /* Get main window to use it as source of mail operation */
1110 mgr = modest_runtime_get_window_mgr ();
1111 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1113 /* Set folder in the model */
1114 modest_header_view_set_folder_intern (self, folder);
1116 /* Pick my reference. Nothing to do with the mail operation */
1117 priv->folder = g_object_ref (folder);
1119 /* no message selected */
1120 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1122 info = g_malloc0 (sizeof(SetFolderHelper));
1123 info->header_view = self;
1124 info->cb = callback;
1125 info->user_data = user_data;
1127 /* bug 57631: Clear the selection if exists */
1128 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1129 gtk_tree_selection_unselect_all(selection);
1131 /* Create the mail operation (source will be the parent widget) */
1132 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1133 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1136 /* Refresh the folder asynchronously */
1137 modest_mail_operation_refresh_folder (mail_op,
1139 folder_refreshed_cb,
1143 g_object_unref (mail_op);
1145 g_mutex_lock (priv->observers_lock);
1147 if (priv->monitor) {
1148 tny_folder_monitor_stop (priv->monitor);
1149 g_object_unref (G_OBJECT (priv->monitor));
1150 priv->monitor = NULL;
1152 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1154 modest_header_view_notify_observers(self, NULL, NULL);
1156 g_mutex_unlock (priv->observers_lock);
1161 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1162 GtkTreeViewColumn *column, gpointer userdata)
1164 ModestHeaderView *self = NULL;
1165 ModestHeaderViewPrivate *priv = NULL;
1167 GtkTreeModel *model = NULL;
1168 TnyHeader *header = NULL;
1169 TnyHeaderFlags flags;
1171 self = MODEST_HEADER_VIEW (treeview);
1172 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1174 model = gtk_tree_view_get_model (treeview);
1175 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1178 /* get the first selected item */
1179 gtk_tree_model_get (model, &iter,
1180 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1181 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1184 /* Dont open DELETED messages */
1185 if (flags & TNY_HEADER_FLAG_DELETED) {
1186 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1191 g_signal_emit (G_OBJECT(self),
1192 signals[HEADER_ACTIVATED_SIGNAL],
1198 g_object_unref (G_OBJECT (header));
1203 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1205 GtkTreeModel *model;
1206 TnyHeader *header = NULL;
1207 GtkTreePath *path = NULL;
1209 ModestHeaderView *self;
1210 ModestHeaderViewPrivate *priv;
1211 GList *selected = NULL;
1213 g_return_if_fail (sel);
1214 g_return_if_fail (user_data);
1216 self = MODEST_HEADER_VIEW (user_data);
1217 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1219 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1220 if (selected != NULL)
1221 path = (GtkTreePath *) selected->data;
1222 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1223 return; /* msg was _un_selected */
1225 gtk_tree_model_get (model, &iter,
1226 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1230 g_signal_emit (G_OBJECT(self),
1231 signals[HEADER_SELECTED_SIGNAL],
1234 g_object_unref (G_OBJECT (header));
1236 /* free all items in 'selected' */
1237 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1238 g_list_free (selected);
1242 /* PROTECTED method. It's useful when we want to force a given
1243 selection to reload a msg. For example if we have selected a header
1244 in offline mode, when Modest become online, we want to reload the
1245 message automatically without an user click over the header */
1247 _modest_header_view_change_selection (GtkTreeSelection *selection,
1250 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1251 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1253 on_selection_changed (selection, user_data);
1256 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1258 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1259 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1261 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1263 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1268 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1275 /* static int counter = 0; */
1277 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1278 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1279 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1283 case TNY_HEADER_FLAG_ATTACHMENTS:
1285 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1286 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1287 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1288 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1290 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1291 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1293 return cmp ? cmp : t1 - t2;
1295 case TNY_HEADER_FLAG_PRIORITY:
1296 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1297 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1298 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1299 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1301 /* This is for making priority values respect the intuitive sort relationship
1302 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1303 cmp = compare_priorities (val1, val2);
1305 return cmp ? cmp : t1 - t2;
1308 return &iter1 - &iter2; /* oughhhh */
1313 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1319 /* static int counter = 0; */
1321 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1323 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1324 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1325 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1326 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1328 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1329 val2 + modest_text_utils_get_subject_prefix_len(val2),
1336 /* Drag and drop stuff */
1338 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1339 GtkSelectionData *selection_data,
1340 guint info, guint time, gpointer data)
1342 GtkTreeModel *model = NULL;
1344 GtkTreePath *source_row = NULL;
1345 /* GtkTreeSelection *sel = NULL;*/
1347 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1349 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1352 case MODEST_HEADER_ROW:
1353 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1356 TnyHeader *hdr = NULL;
1357 gtk_tree_model_get (model, &iter,
1358 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1361 g_object_unref (G_OBJECT(hdr));
1366 g_message ("%s: default switch case.", __FUNCTION__);
1369 /* commenting out the next, fixes NB#62963 */
1371 /* Set focus on next header */
1372 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1373 gtk_tree_path_next (source_row);
1374 gtk_tree_selection_select_path (sel, source_row);
1376 gtk_tree_path_free (source_row);
1380 /* Header view drag types */
1381 const GtkTargetEntry header_view_drag_types[] = {
1382 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1383 { "text/uri-list", 0, MODEST_MSG },
1387 setup_drag_and_drop (GtkTreeView *self)
1389 gtk_drag_source_set (GTK_WIDGET (self),
1391 header_view_drag_types,
1392 G_N_ELEMENTS (header_view_drag_types),
1393 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1395 g_signal_connect(G_OBJECT (self), "drag_data_get",
1396 G_CALLBACK(drag_data_get_cb), NULL);
1399 static GtkTreePath *
1400 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1402 GtkTreePath *path = NULL;
1403 GtkTreeSelection *sel = NULL;
1406 sel = gtk_tree_view_get_selection(self);
1407 rows = gtk_tree_selection_get_selected_rows (sel, model);
1409 if ((rows == NULL) || (g_list_length(rows) != 1))
1412 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1417 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1424 * This function moves the tree view scroll to the current selected
1425 * row when the widget grabs the focus
1428 on_focus_in (GtkWidget *self,
1429 GdkEventFocus *event,
1432 GtkTreeSelection *selection;
1433 GtkTreeModel *model;
1434 GList *selected = NULL;
1435 GtkTreePath *selected_path = NULL;
1437 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1441 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1442 /* If none selected yet, pick the first one */
1443 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1447 /* Return if the model is empty */
1448 if (!gtk_tree_model_get_iter_first (model, &iter))
1451 path = gtk_tree_model_get_path (model, &iter);
1452 gtk_tree_selection_select_path (selection, path);
1453 gtk_tree_path_free (path);
1456 /* Need to get the all the rows because is selection multiple */
1457 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1458 if (selected == NULL) return FALSE;
1459 selected_path = (GtkTreePath *) selected->data;
1461 /* Check if we need to scroll */
1462 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1463 GtkTreePath *start_path = NULL;
1464 GtkTreePath *end_path = NULL;
1465 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1469 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1470 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1472 /* Scroll to first path */
1473 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1482 gtk_tree_path_free (start_path);
1484 gtk_tree_path_free (end_path);
1486 #endif /* GTK_CHECK_VERSION */
1489 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1490 g_list_free (selected);
1496 idle_notify_headers_count_changed_destroy (gpointer data)
1498 HeadersCountChangedHelper *helper = NULL;
1500 g_return_if_fail (data != NULL);
1501 helper = (HeadersCountChangedHelper *) data;
1503 g_object_unref (helper->change);
1504 g_slice_free (HeadersCountChangedHelper, helper);
1508 idle_notify_headers_count_changed (gpointer data)
1510 TnyFolder *folder = NULL;
1511 ModestHeaderViewPrivate *priv = NULL;
1512 HeadersCountChangedHelper *helper = NULL;
1514 g_return_val_if_fail (data != NULL, FALSE);
1515 helper = (HeadersCountChangedHelper *) data;
1516 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1517 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1519 folder = tny_folder_change_get_folder (helper->change);
1521 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1523 g_mutex_lock (priv->observers_lock);
1525 /* Emit signal to evaluate how headers changes affects to the window view */
1526 g_signal_emit (G_OBJECT(helper->self),
1527 signals[MSG_COUNT_CHANGED_SIGNAL],
1528 0, folder, helper->change);
1530 /* Added or removed headers, so data stored on cliboard are invalid */
1531 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1532 modest_email_clipboard_clear (priv->clipboard);
1534 g_mutex_unlock (priv->observers_lock);
1540 folder_monitor_update (TnyFolderObserver *self,
1541 TnyFolderChange *change)
1543 ModestHeaderViewPrivate *priv = NULL;
1544 TnyFolderChangeChanged changed;
1545 HeadersCountChangedHelper *helper = NULL;
1547 changed = tny_folder_change_get_changed (change);
1549 /* Do not notify the observers if the folder of the header
1550 view has changed before this call to the observer
1552 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1553 if (tny_folder_change_get_folder (change) != priv->folder)
1556 /* Check folder count */
1557 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1558 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1559 helper = g_slice_new0 (HeadersCountChangedHelper);
1560 helper->self = MODEST_HEADER_VIEW(self);
1561 helper->change = g_object_ref(change);
1563 g_idle_add_full (G_PRIORITY_DEFAULT,
1564 idle_notify_headers_count_changed,
1566 idle_notify_headers_count_changed_destroy);
1571 modest_header_view_is_empty (ModestHeaderView *self)
1573 ModestHeaderViewPrivate *priv = NULL;
1575 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1577 return priv->status == HEADER_VIEW_EMPTY;
1581 modest_header_view_clear (ModestHeaderView *self)
1583 modest_header_view_set_folder (self, NULL, NULL, NULL);
1587 modest_header_view_copy_selection (ModestHeaderView *header_view)
1589 /* Copy selection */
1590 _clipboard_set_selected_data (header_view, FALSE);
1594 modest_header_view_cut_selection (ModestHeaderView *header_view)
1596 ModestHeaderViewPrivate *priv = NULL;
1597 const gchar **hidding = NULL;
1598 guint i, n_selected;
1600 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1601 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1603 /* Copy selection */
1604 _clipboard_set_selected_data (header_view, TRUE);
1606 /* Get hidding ids */
1607 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1609 /* Clear hidding array created by previous cut operation */
1610 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1612 /* Copy hidding array */
1613 priv->n_selected = n_selected;
1614 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1615 for (i=0; i < n_selected; i++)
1616 priv->hidding_ids[i] = g_strdup(hidding[i]);
1618 /* Hide cut headers */
1619 modest_header_view_refilter (header_view);
1626 _clipboard_set_selected_data (ModestHeaderView *header_view,
1629 ModestHeaderViewPrivate *priv = NULL;
1630 TnyList *headers = NULL;
1632 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1633 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1635 /* Set selected data on clipboard */
1636 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1637 headers = modest_header_view_get_selected_headers (header_view);
1638 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1641 g_object_unref (headers);
1647 filter_row (GtkTreeModel *model,
1651 ModestHeaderViewPrivate *priv = NULL;
1652 TnyHeaderFlags flags;
1653 TnyHeader *header = NULL;
1656 gboolean visible = TRUE;
1657 gboolean found = FALSE;
1659 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1660 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1662 /* Get header from model */
1663 gtk_tree_model_get (model, iter,
1664 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1665 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1668 /* Hide mark as deleted heders */
1669 if (flags & TNY_HEADER_FLAG_DELETED) {
1674 /* If no data on clipboard, return always TRUE */
1675 if (modest_email_clipboard_cleared(priv->clipboard)) {
1680 /* Get message id from header (ensure is a valid id) */
1681 if (!header) return FALSE;
1682 id = g_strdup(tny_header_get_message_id (header));
1685 if (priv->hidding_ids != NULL) {
1686 for (i=0; i < priv->n_selected && !found; i++)
1687 if (priv->hidding_ids[i] != NULL && id != NULL)
1688 found = (!strcmp (priv->hidding_ids[i], id));
1694 priv->status = ((gboolean) priv->status) && !visible;
1698 g_object_unref (header);
1705 _clear_hidding_filter (ModestHeaderView *header_view)
1707 ModestHeaderViewPrivate *priv = NULL;
1710 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1711 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1713 if (priv->hidding_ids != NULL) {
1714 for (i=0; i < priv->n_selected; i++)
1715 g_free (priv->hidding_ids[i]);
1716 g_free(priv->hidding_ids);
1721 modest_header_view_refilter (ModestHeaderView *header_view)
1723 GtkTreeModel *model = NULL;
1724 ModestHeaderViewPrivate *priv = NULL;
1726 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1727 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1729 priv->status = HEADER_VIEW_EMPTY;
1731 /* Hide cut headers */
1732 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1733 if (GTK_IS_TREE_MODEL_FILTER (model))
1734 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1738 * Called when an account is removed. If I'm showing a folder of the
1739 * account that has been removed then clear the view
1742 on_account_removed (TnyAccountStore *self,
1743 TnyAccount *account,
1746 ModestHeaderViewPrivate *priv = NULL;
1748 /* Ignore changes in transport accounts */
1749 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1752 g_print ("--------------------- HEADER ---------------\n");
1754 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1757 TnyAccount *my_account;
1759 my_account = tny_folder_get_account (priv->folder);
1760 if (my_account == account)
1761 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1762 g_object_unref (account);
1766 void modest_header_view_add_observer(
1767 ModestHeaderView *header_view,
1768 ModestHeaderViewObserver *observer)
1770 ModestHeaderViewPrivate *priv = NULL;
1772 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1773 g_assert(observer != NULL);
1774 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1776 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1778 g_mutex_lock(priv->observer_list_lock);
1779 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1780 g_mutex_unlock(priv->observer_list_lock);
1783 void modest_header_view_remove_observer(
1784 ModestHeaderView *header_view,
1785 ModestHeaderViewObserver *observer)
1787 ModestHeaderViewPrivate *priv = NULL;
1789 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1790 g_assert(observer != NULL);
1791 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1793 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1795 g_mutex_lock(priv->observer_list_lock);
1796 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1797 g_mutex_unlock(priv->observer_list_lock);
1800 static void modest_header_view_notify_observers(
1801 ModestHeaderView *header_view,
1802 GtkTreeModel *model,
1803 const gchar *tny_folder_id)
1805 ModestHeaderViewPrivate *priv = NULL;
1807 ModestHeaderViewObserver *observer;
1809 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1811 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1813 g_mutex_lock(priv->observer_list_lock);
1814 iter = priv->observer_list;
1815 while(iter != NULL){
1816 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1817 modest_header_view_observer_update(observer, model,
1819 iter = g_slist_next(iter);
1821 g_mutex_unlock(priv->observer_list_lock);