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);
103 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
104 struct _ModestHeaderViewPrivate {
106 ModestHeaderViewStyle style;
108 TnyFolderMonitor *monitor;
109 GMutex *observers_lock;
111 /*header-view-observer observer*/
112 GMutex *observer_list_lock;
113 GSList *observer_list;
115 /* not unref this object, its a singlenton */
116 ModestEmailClipboard *clipboard;
118 /* Filter tree model */
122 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
123 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
125 gulong selection_changed_handler;
126 gulong acc_removed_handler;
131 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
132 struct _HeadersCountChangedHelper {
133 ModestHeaderView *self;
134 TnyFolderChange *change;
138 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
139 MODEST_TYPE_HEADER_VIEW, \
140 ModestHeaderViewPrivate))
144 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
147 HEADER_SELECTED_SIGNAL,
148 HEADER_ACTIVATED_SIGNAL,
149 ITEM_NOT_FOUND_SIGNAL,
150 MSG_COUNT_CHANGED_SIGNAL,
155 static GObjectClass *parent_class = NULL;
157 /* uncomment the following if you have defined any signals */
158 static guint signals[LAST_SIGNAL] = {0};
161 modest_header_view_get_type (void)
163 static GType my_type = 0;
165 static const GTypeInfo my_info = {
166 sizeof(ModestHeaderViewClass),
167 NULL, /* base init */
168 NULL, /* base finalize */
169 (GClassInitFunc) modest_header_view_class_init,
170 NULL, /* class finalize */
171 NULL, /* class data */
172 sizeof(ModestHeaderView),
174 (GInstanceInitFunc) modest_header_view_init,
178 static const GInterfaceInfo tny_folder_observer_info =
180 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
181 NULL, /* interface_finalize */
182 NULL /* interface_data */
184 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
188 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
189 &tny_folder_observer_info);
197 modest_header_view_class_init (ModestHeaderViewClass *klass)
199 GObjectClass *gobject_class;
200 gobject_class = (GObjectClass*) klass;
202 parent_class = g_type_class_peek_parent (klass);
203 gobject_class->finalize = modest_header_view_finalize;
204 gobject_class->dispose = modest_header_view_dispose;
206 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
208 signals[HEADER_SELECTED_SIGNAL] =
209 g_signal_new ("header_selected",
210 G_TYPE_FROM_CLASS (gobject_class),
212 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
214 g_cclosure_marshal_VOID__POINTER,
215 G_TYPE_NONE, 1, G_TYPE_POINTER);
217 signals[HEADER_ACTIVATED_SIGNAL] =
218 g_signal_new ("header_activated",
219 G_TYPE_FROM_CLASS (gobject_class),
221 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
223 g_cclosure_marshal_VOID__POINTER,
224 G_TYPE_NONE, 1, G_TYPE_POINTER);
227 signals[ITEM_NOT_FOUND_SIGNAL] =
228 g_signal_new ("item_not_found",
229 G_TYPE_FROM_CLASS (gobject_class),
231 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
233 g_cclosure_marshal_VOID__INT,
234 G_TYPE_NONE, 1, G_TYPE_INT);
236 signals[MSG_COUNT_CHANGED_SIGNAL] =
237 g_signal_new ("msg_count_changed",
238 G_TYPE_FROM_CLASS (gobject_class),
240 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
242 modest_marshal_VOID__POINTER_POINTER,
243 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
247 tny_folder_observer_init (TnyFolderObserverIface *klass)
249 klass->update_func = folder_monitor_update;
252 static GtkTreeViewColumn*
253 get_new_column (const gchar *name, GtkCellRenderer *renderer,
254 gboolean resizable, gint sort_col_id, gboolean show_as_text,
255 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
257 GtkTreeViewColumn *column;
259 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
260 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
262 gtk_tree_view_column_set_resizable (column, resizable);
264 gtk_tree_view_column_set_expand (column, TRUE);
267 gtk_tree_view_column_add_attribute (column, renderer, "text",
269 if (sort_col_id >= 0)
270 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
272 gtk_tree_view_column_set_sort_indicator (column, FALSE);
273 gtk_tree_view_column_set_reorderable (column, TRUE);
276 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
283 remove_all_columns (ModestHeaderView *obj)
285 GList *columns, *cursor;
287 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
289 for (cursor = columns; cursor; cursor = cursor->next)
290 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
291 GTK_TREE_VIEW_COLUMN(cursor->data));
292 g_list_free (columns);
296 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
298 GtkTreeModel *tree_filter, *sortable;
299 GtkTreeViewColumn *column=NULL;
300 GtkTreeSelection *selection = NULL;
301 GtkCellRenderer *renderer_msgtype,*renderer_header,
302 *renderer_attach, *renderer_compact_date_or_status;
303 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
304 *renderer_subject, *renderer_subject_box, *renderer_recpt,
306 ModestHeaderViewPrivate *priv;
307 GtkTreeViewColumn *compact_column = NULL;
310 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
312 /* FIXME: check whether these renderers need to be freed */
313 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
314 renderer_attach = gtk_cell_renderer_pixbuf_new ();
315 renderer_priority = gtk_cell_renderer_pixbuf_new ();
316 renderer_header = gtk_cell_renderer_text_new ();
318 renderer_compact_header = modest_vbox_cell_renderer_new ();
319 renderer_recpt_box = modest_hbox_cell_renderer_new ();
320 renderer_subject_box = modest_hbox_cell_renderer_new ();
321 renderer_recpt = gtk_cell_renderer_text_new ();
322 renderer_subject = gtk_cell_renderer_text_new ();
323 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
325 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
326 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
327 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
328 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
329 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
330 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
331 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
332 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
333 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
334 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
335 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
336 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
337 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
338 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
340 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
341 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
342 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
343 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
344 g_object_set(G_OBJECT(renderer_header),
345 "ellipsize", PANGO_ELLIPSIZE_END,
347 g_object_set (G_OBJECT (renderer_subject),
348 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
350 g_object_set (G_OBJECT (renderer_recpt),
351 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
353 g_object_set(G_OBJECT(renderer_compact_date_or_status),
354 "xalign", 1.0, "yalign", 0.0,
356 g_object_set (G_OBJECT (renderer_priority),
357 "yalign", 1.0, NULL);
358 g_object_set (G_OBJECT (renderer_attach),
359 "yalign", 0.0, NULL);
361 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
362 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
363 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
365 remove_all_columns (self);
367 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
368 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
369 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
370 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
372 /* Add new columns */
373 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
374 ModestHeaderViewColumn col =
375 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
377 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
378 g_printerr ("modest: invalid column %d in column list\n", col);
384 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
385 column = get_new_column (_("M"), renderer_msgtype, FALSE,
386 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
388 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
390 gtk_tree_view_column_set_fixed_width (column, 45);
393 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
394 column = get_new_column (_("A"), renderer_attach, FALSE,
395 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
397 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
399 gtk_tree_view_column_set_fixed_width (column, 45);
403 case MODEST_HEADER_VIEW_COLUMN_FROM:
404 column = get_new_column (_("From"), renderer_header, TRUE,
405 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
407 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
408 GINT_TO_POINTER(TRUE));
411 case MODEST_HEADER_VIEW_COLUMN_TO:
412 column = get_new_column (_("To"), renderer_header, TRUE,
413 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
415 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
416 GINT_TO_POINTER(FALSE));
419 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
420 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
421 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
423 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
424 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
425 compact_column = column;
428 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
429 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
430 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
432 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
433 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
434 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
435 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
436 compact_column = column;
440 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
441 column = get_new_column (_("Subject"), renderer_header, TRUE,
442 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
444 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
448 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
449 column = get_new_column (_("Received"), renderer_header, TRUE,
450 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
452 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
453 GINT_TO_POINTER(TRUE));
456 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
457 column = get_new_column (_("Sent"), renderer_header, TRUE,
458 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
460 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
461 GINT_TO_POINTER(FALSE));
464 case MODEST_HEADER_VIEW_COLUMN_SIZE:
465 column = get_new_column (_("Size"), renderer_header, TRUE,
466 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
468 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
471 case MODEST_HEADER_VIEW_COLUMN_STATUS:
472 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
473 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
475 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
480 g_return_val_if_reached(FALSE);
483 /* we keep the column id around */
484 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
485 GINT_TO_POINTER(col));
487 /* we need this ptr when sorting the rows */
488 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
490 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
494 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
495 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
496 (GtkTreeIterCompareFunc) cmp_rows,
497 compact_column, NULL);
498 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
499 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
500 (GtkTreeIterCompareFunc) cmp_subject_rows,
501 compact_column, NULL);
509 modest_header_view_init (ModestHeaderView *obj)
511 ModestHeaderViewPrivate *priv;
514 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
518 priv->monitor = NULL;
519 priv->observers_lock = g_mutex_new ();
523 priv->observer_list_lock = g_mutex_new();
524 priv->observer_list = NULL;
526 priv->clipboard = modest_runtime_get_email_clipboard ();
527 priv->hidding_ids = NULL;
528 priv->n_selected = 0;
529 priv->selection_changed_handler = 0;
530 priv->acc_removed_handler = 0;
532 /* Sort parameters */
533 for (j=0; j < 2; j++) {
534 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
535 priv->sort_colid[j][i] = -1;
536 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
540 setup_drag_and_drop (GTK_TREE_VIEW (obj));
544 modest_header_view_dispose (GObject *obj)
546 ModestHeaderView *self;
547 ModestHeaderViewPrivate *priv;
548 GtkTreeSelection *sel;
550 self = MODEST_HEADER_VIEW(obj);
551 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
553 /* Free in the dispose to avoid unref cycles */
555 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
556 g_object_unref (G_OBJECT (priv->folder));
560 /* We need to do this here in the dispose because the
561 selection won't exist when finalizing */
562 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
563 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
564 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
565 priv->selection_changed_handler = 0;
568 G_OBJECT_CLASS(parent_class)->dispose (obj);
572 modest_header_view_finalize (GObject *obj)
574 ModestHeaderView *self;
575 ModestHeaderViewPrivate *priv;
577 self = MODEST_HEADER_VIEW(obj);
578 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
580 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
581 priv->acc_removed_handler)) {
582 g_signal_handler_disconnect (modest_runtime_get_account_store (),
583 priv->acc_removed_handler);
586 /* There is no need to lock because there should not be any
587 * reference to self now. */
588 g_mutex_free(priv->observer_list_lock);
589 g_slist_free(priv->observer_list);
591 g_mutex_lock (priv->observers_lock);
593 tny_folder_monitor_stop (priv->monitor);
594 g_object_unref (G_OBJECT (priv->monitor));
596 g_mutex_unlock (priv->observers_lock);
597 g_mutex_free (priv->observers_lock);
599 /* Clear hidding array created by cut operation */
600 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
602 G_OBJECT_CLASS(parent_class)->finalize (obj);
607 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
610 GtkTreeSelection *sel;
611 ModestHeaderView *self;
612 ModestHeaderViewPrivate *priv;
614 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
617 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
618 self = MODEST_HEADER_VIEW(obj);
619 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
621 modest_header_view_set_style (self, style);
622 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
624 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
625 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
626 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
628 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
629 TRUE); /* alternating row colors */
631 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
632 priv->selection_changed_handler =
633 g_signal_connect_after (sel, "changed",
634 G_CALLBACK(on_selection_changed), self);
636 g_signal_connect (self, "row-activated",
637 G_CALLBACK (on_header_row_activated), NULL);
639 g_signal_connect (self, "focus-in-event",
640 G_CALLBACK(on_focus_in), NULL);
642 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
644 G_CALLBACK (on_account_removed),
647 return GTK_WIDGET(self);
652 modest_header_view_count_selected_headers (ModestHeaderView *self)
654 GtkTreeSelection *sel;
657 g_return_val_if_fail (self, 0);
659 /* Get selection object and check selected rows count */
660 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
661 selected_rows = gtk_tree_selection_count_selected_rows (sel);
663 return selected_rows;
667 modest_header_view_has_selected_headers (ModestHeaderView *self)
669 GtkTreeSelection *sel;
672 g_return_val_if_fail (self, FALSE);
674 /* Get selection object and check selected rows count */
675 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
676 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
683 modest_header_view_get_selected_headers (ModestHeaderView *self)
685 GtkTreeSelection *sel;
686 ModestHeaderViewPrivate *priv;
687 TnyList *header_list = NULL;
689 GList *list, *tmp = NULL;
690 GtkTreeModel *tree_model = NULL;
693 g_return_val_if_fail (self, NULL);
695 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
697 /* Get selected rows */
698 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
699 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
702 header_list = tny_simple_list_new();
704 list = g_list_reverse (list);
707 /* get header from selection */
708 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
709 gtk_tree_model_get (tree_model, &iter,
710 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
712 /* Prepend to list */
713 tny_list_prepend (header_list, G_OBJECT (header));
714 g_object_unref (G_OBJECT (header));
716 tmp = g_list_next (tmp);
719 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
726 /* scroll our list view so the selected item is visible */
728 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
730 #ifdef MODEST_PLATFORM_GNOME
732 GtkTreePath *selected_path;
733 GtkTreePath *start, *end;
737 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
738 selected_path = gtk_tree_model_get_path (model, iter);
740 start = gtk_tree_path_new ();
741 end = gtk_tree_path_new ();
743 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
745 if (gtk_tree_path_compare (selected_path, start) < 0 ||
746 gtk_tree_path_compare (end, selected_path) < 0)
747 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
748 selected_path, NULL, TRUE,
751 gtk_tree_path_free (selected_path);
752 gtk_tree_path_free (start);
753 gtk_tree_path_free (end);
755 #endif /* MODEST_PLATFORM_GNOME */
760 modest_header_view_select_next (ModestHeaderView *self)
762 GtkTreeSelection *sel;
767 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
768 path = get_selected_row (GTK_TREE_VIEW(self), &model);
769 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
770 /* Unselect previous path */
771 gtk_tree_selection_unselect_path (sel, path);
773 /* Move path down and selects new one */
774 if (gtk_tree_model_iter_next (model, &iter)) {
775 gtk_tree_selection_select_iter (sel, &iter);
776 scroll_to_selected (self, &iter, FALSE);
778 gtk_tree_path_free(path);
784 modest_header_view_select_prev (ModestHeaderView *self)
786 GtkTreeSelection *sel;
791 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
792 path = get_selected_row (GTK_TREE_VIEW(self), &model);
793 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
794 /* Unselect previous path */
795 gtk_tree_selection_unselect_path (sel, path);
798 if (gtk_tree_path_prev (path)) {
799 gtk_tree_model_get_iter (model, &iter, path);
801 /* Select the new one */
802 gtk_tree_selection_select_iter (sel, &iter);
803 scroll_to_selected (self, &iter, TRUE);
806 gtk_tree_path_free (path);
811 modest_header_view_get_columns (ModestHeaderView *self)
813 g_return_val_if_fail (self, FALSE);
814 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
819 /* modest_header_view_is_empty (ModestHeaderView *self) */
821 /* g_return_val_if_fail (self, FALSE); */
822 /* return FALSE; /\* FIXME *\/ */
827 modest_header_view_set_style (ModestHeaderView *self,
828 ModestHeaderViewStyle style)
830 ModestHeaderViewPrivate *priv;
831 gboolean show_col_headers = FALSE;
832 ModestHeaderViewStyle old_style;
834 g_return_val_if_fail (self, FALSE);
835 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
838 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
839 if (priv->style == style)
840 return TRUE; /* nothing to do */
843 case MODEST_HEADER_VIEW_STYLE_DETAILS:
844 show_col_headers = TRUE;
846 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
849 g_return_val_if_reached (FALSE);
851 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
852 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
854 old_style = priv->style;
861 ModestHeaderViewStyle
862 modest_header_view_get_style (ModestHeaderView *self)
864 g_return_val_if_fail (self, FALSE);
865 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
869 * This function sets a sortable model in the header view. It's just
870 * used for developing purposes, because it only does a
871 * gtk_tree_view_set_model
874 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
876 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
877 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
878 /* GtkTreeModel *old_model; */
879 /* ModestHeaderViewPrivate *priv; */
880 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
881 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
883 /* /\* Set new model *\/ */
884 /* gtk_tree_view_set_model (header_view, model); */
886 gtk_tree_view_set_model (header_view, model);
890 modest_header_view_get_folder (ModestHeaderView *self)
892 ModestHeaderViewPrivate *priv;
893 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
896 g_object_ref (priv->folder);
902 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
906 ModestHeaderViewPrivate *priv;
907 GList *cols, *cursor;
908 GtkTreeModel *filter_model, *sortable;
910 GtkSortType sort_type;
912 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
914 headers = TNY_LIST (tny_gtk_header_list_model_new ());
916 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
919 /* Add IDLE observer (monitor) and another folder observer for
920 new messages (self) */
921 g_mutex_lock (priv->observers_lock);
923 tny_folder_monitor_stop (priv->monitor);
924 g_object_unref (G_OBJECT (priv->monitor));
926 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
927 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
928 tny_folder_monitor_start (priv->monitor);
929 g_mutex_unlock (priv->observers_lock);
931 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
932 g_object_unref (G_OBJECT (headers));
934 /* Create a tree model filter to hide and show rows for cut operations */
935 filter_model = gtk_tree_model_filter_new (sortable, NULL);
936 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
940 g_object_unref (G_OBJECT (sortable));
942 /* install our special sorting functions */
943 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
945 /* Restore sort column id */
947 type = modest_tny_folder_guess_folder_type (folder);
948 sort_colid = modest_header_view_get_sort_column_id (self, type);
949 sort_type = modest_header_view_get_sort_type (self, type);
950 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
953 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
954 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
955 (GtkTreeIterCompareFunc) cmp_rows,
957 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
958 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
959 (GtkTreeIterCompareFunc) cmp_subject_rows,
964 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
965 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
966 tny_folder_get_id(folder));
967 g_object_unref (G_OBJECT (filter_model));
968 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
969 /* g_object_unref (G_OBJECT (sortable)); */
976 modest_header_view_sort_by_column_id (ModestHeaderView *self,
978 GtkSortType sort_type)
980 ModestHeaderViewPrivate *priv = NULL;
981 GtkTreeModel *tree_filter, *sortable = NULL;
984 /* Get model and private data */
985 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
986 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
987 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
988 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
990 /* Sort tree model */
991 type = modest_tny_folder_guess_folder_type (priv->folder);
992 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
995 /* Store new sort parameters */
996 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
998 /* Save GConf parameters */
999 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1000 /* G_OBJECT(self), "header-view"); */
1005 modest_header_view_set_sort_params (ModestHeaderView *self,
1007 GtkSortType sort_type,
1010 ModestHeaderViewPrivate *priv;
1011 ModestHeaderViewStyle style;
1013 style = modest_header_view_get_style (self);
1014 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1016 priv->sort_colid[style][type] = sort_colid;
1017 priv->sort_type[style][type] = sort_type;
1021 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1024 ModestHeaderViewPrivate *priv;
1025 ModestHeaderViewStyle style;
1027 style = modest_header_view_get_style (self);
1028 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1030 return priv->sort_colid[style][type];
1034 modest_header_view_get_sort_type (ModestHeaderView *self,
1037 ModestHeaderViewPrivate *priv;
1038 ModestHeaderViewStyle style;
1040 style = modest_header_view_get_style (self);
1041 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1043 return priv->sort_type[style][type];
1047 ModestHeaderView *header_view;
1048 RefreshAsyncUserCallback cb;
1053 folder_refreshed_cb (ModestMailOperation *mail_op,
1057 ModestHeaderViewPrivate *priv;
1058 SetFolderHelper *info;
1060 info = (SetFolderHelper*) user_data;
1062 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1066 info->cb (mail_op, folder, info->user_data);
1068 /* Start the folder count changes observer. We do not need it
1069 before the refresh. Note that the monitor could still be
1070 called for this refresh but now we know that the callback
1071 was previously called */
1072 g_mutex_lock (priv->observers_lock);
1073 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1074 g_mutex_unlock (priv->observers_lock);
1081 modest_header_view_set_folder (ModestHeaderView *self,
1083 RefreshAsyncUserCallback callback,
1086 ModestHeaderViewPrivate *priv;
1087 ModestWindowMgr *mgr = NULL;
1088 GObject *source = NULL;
1089 SetFolderHelper *info;
1091 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1094 g_mutex_lock (priv->observers_lock);
1095 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1096 g_object_unref (priv->folder);
1097 priv->folder = NULL;
1098 g_mutex_unlock (priv->observers_lock);
1102 ModestMailOperation *mail_op = NULL;
1103 GtkTreeSelection *selection;
1105 /* Get main window to use it as source of mail operation */
1106 mgr = modest_runtime_get_window_mgr ();
1107 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1109 /* Set folder in the model */
1110 modest_header_view_set_folder_intern (self, folder);
1112 /* Pick my reference. Nothing to do with the mail operation */
1113 priv->folder = g_object_ref (folder);
1115 /* no message selected */
1116 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1118 info = g_malloc0 (sizeof(SetFolderHelper));
1119 info->header_view = self;
1120 info->cb = callback;
1121 info->user_data = user_data;
1123 /* bug 57631: Clear the selection if exists */
1124 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1125 gtk_tree_selection_unselect_all(selection);
1127 /* Create the mail operation (source will be the parent widget) */
1128 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1129 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1132 /* Refresh the folder asynchronously */
1133 modest_mail_operation_refresh_folder (mail_op,
1135 folder_refreshed_cb,
1139 g_object_unref (mail_op);
1141 g_mutex_lock (priv->observers_lock);
1143 if (priv->monitor) {
1144 tny_folder_monitor_stop (priv->monitor);
1145 g_object_unref (G_OBJECT (priv->monitor));
1146 priv->monitor = NULL;
1148 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1150 modest_header_view_notify_observers(self, NULL, NULL);
1152 g_mutex_unlock (priv->observers_lock);
1157 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1158 GtkTreeViewColumn *column, gpointer userdata)
1160 ModestHeaderView *self = NULL;
1161 ModestHeaderViewPrivate *priv = NULL;
1163 GtkTreeModel *model = NULL;
1164 TnyHeader *header = NULL;
1165 TnyHeaderFlags flags;
1167 self = MODEST_HEADER_VIEW (treeview);
1168 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1170 model = gtk_tree_view_get_model (treeview);
1171 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1174 /* get the first selected item */
1175 gtk_tree_model_get (model, &iter,
1176 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1177 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1180 /* Dont open DELETED messages */
1181 if (flags & TNY_HEADER_FLAG_DELETED) {
1182 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1187 g_signal_emit (G_OBJECT(self),
1188 signals[HEADER_ACTIVATED_SIGNAL],
1194 g_object_unref (G_OBJECT (header));
1199 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1201 GtkTreeModel *model;
1202 TnyHeader *header = NULL;
1203 GtkTreePath *path = NULL;
1205 ModestHeaderView *self;
1206 ModestHeaderViewPrivate *priv;
1207 GList *selected = NULL;
1209 g_return_if_fail (sel);
1210 g_return_if_fail (user_data);
1212 self = MODEST_HEADER_VIEW (user_data);
1213 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1215 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1216 if (selected != NULL)
1217 path = (GtkTreePath *) selected->data;
1218 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1219 return; /* msg was _un_selected */
1221 gtk_tree_model_get (model, &iter,
1222 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1226 g_signal_emit (G_OBJECT(self),
1227 signals[HEADER_SELECTED_SIGNAL],
1230 g_object_unref (G_OBJECT (header));
1232 /* free all items in 'selected' */
1233 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1234 g_list_free (selected);
1238 /* PROTECTED method. It's useful when we want to force a given
1239 selection to reload a msg. For example if we have selected a header
1240 in offline mode, when Modest become online, we want to reload the
1241 message automatically without an user click over the header */
1243 _modest_header_view_change_selection (GtkTreeSelection *selection,
1246 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1247 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1249 on_selection_changed (selection, user_data);
1252 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1254 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1255 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1257 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1259 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1264 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1271 /* static int counter = 0; */
1273 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1274 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1275 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1279 case TNY_HEADER_FLAG_ATTACHMENTS:
1281 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1282 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1283 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1284 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1286 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1287 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1289 return cmp ? cmp : t1 - t2;
1291 case TNY_HEADER_FLAG_PRIORITY:
1292 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1293 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1294 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1295 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1297 /* This is for making priority values respect the intuitive sort relationship
1298 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1299 cmp = compare_priorities (val1, val2);
1301 return cmp ? cmp : t1 - t2;
1304 return &iter1 - &iter2; /* oughhhh */
1309 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1315 /* static int counter = 0; */
1317 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1319 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1320 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1321 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1322 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1324 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1325 val2 + modest_text_utils_get_subject_prefix_len(val2),
1332 /* Drag and drop stuff */
1334 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1335 GtkSelectionData *selection_data,
1336 guint info, guint time, gpointer data)
1338 GtkTreeModel *model = NULL;
1340 GtkTreePath *source_row = NULL;
1341 GtkTreeSelection *sel = NULL;
1343 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1345 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1347 case MODEST_HEADER_ROW:
1348 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1351 TnyHeader *hdr = NULL;
1352 gtk_tree_model_get (model, &iter,
1353 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1356 g_object_unref (G_OBJECT(hdr));
1361 g_message ("%s: default switch case.", __FUNCTION__);
1364 /* Set focus on next header */
1365 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1366 gtk_tree_path_next (source_row);
1367 gtk_tree_selection_select_path (sel, source_row);
1369 gtk_tree_path_free (source_row);
1372 /* Header view drag types */
1373 const GtkTargetEntry header_view_drag_types[] = {
1374 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1375 { "text/uri-list", 0, MODEST_MSG },
1379 setup_drag_and_drop (GtkTreeView *self)
1381 gtk_drag_source_set (GTK_WIDGET (self),
1383 header_view_drag_types,
1384 G_N_ELEMENTS (header_view_drag_types),
1385 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1387 g_signal_connect(G_OBJECT (self), "drag_data_get",
1388 G_CALLBACK(drag_data_get_cb), NULL);
1391 static GtkTreePath *
1392 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1394 GtkTreePath *path = NULL;
1395 GtkTreeSelection *sel = NULL;
1398 sel = gtk_tree_view_get_selection(self);
1399 rows = gtk_tree_selection_get_selected_rows (sel, model);
1401 if ((rows == NULL) || (g_list_length(rows) != 1))
1404 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1409 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1416 * This function moves the tree view scroll to the current selected
1417 * row when the widget grabs the focus
1420 on_focus_in (GtkWidget *self,
1421 GdkEventFocus *event,
1424 GtkTreeSelection *selection;
1425 GtkTreeModel *model;
1426 GList *selected = NULL;
1427 GtkTreePath *selected_path = NULL;
1429 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1433 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1434 /* If none selected yet, pick the first one */
1435 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1439 /* Return if the model is empty */
1440 if (!gtk_tree_model_get_iter_first (model, &iter))
1443 path = gtk_tree_model_get_path (model, &iter);
1444 gtk_tree_selection_select_path (selection, path);
1445 gtk_tree_path_free (path);
1448 /* Need to get the all the rows because is selection multiple */
1449 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1450 if (selected == NULL) return FALSE;
1451 selected_path = (GtkTreePath *) selected->data;
1453 /* Check if we need to scroll */
1454 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1455 GtkTreePath *start_path = NULL;
1456 GtkTreePath *end_path = NULL;
1457 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1461 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1462 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1464 /* Scroll to first path */
1465 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1474 gtk_tree_path_free (start_path);
1476 gtk_tree_path_free (end_path);
1478 #endif /* GTK_CHECK_VERSION */
1481 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1482 g_list_free (selected);
1488 idle_notify_headers_count_changed_destroy (gpointer data)
1490 HeadersCountChangedHelper *helper = NULL;
1492 g_return_if_fail (data != NULL);
1493 helper = (HeadersCountChangedHelper *) data;
1495 g_object_unref (helper->change);
1496 g_slice_free (HeadersCountChangedHelper, helper);
1500 idle_notify_headers_count_changed (gpointer data)
1502 TnyFolder *folder = NULL;
1503 ModestHeaderViewPrivate *priv = NULL;
1504 HeadersCountChangedHelper *helper = NULL;
1506 g_return_val_if_fail (data != NULL, FALSE);
1507 helper = (HeadersCountChangedHelper *) data;
1508 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1509 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1511 folder = tny_folder_change_get_folder (helper->change);
1513 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1515 g_mutex_lock (priv->observers_lock);
1517 /* Emit signal to evaluate how headers changes affects to the window view */
1518 g_signal_emit (G_OBJECT(helper->self),
1519 signals[MSG_COUNT_CHANGED_SIGNAL],
1520 0, folder, helper->change);
1522 /* Added or removed headers, so data stored on cliboard are invalid */
1523 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1524 modest_email_clipboard_clear (priv->clipboard);
1526 g_mutex_unlock (priv->observers_lock);
1532 folder_monitor_update (TnyFolderObserver *self,
1533 TnyFolderChange *change)
1535 ModestHeaderViewPrivate *priv = NULL;
1536 TnyFolderChangeChanged changed;
1537 HeadersCountChangedHelper *helper = NULL;
1539 changed = tny_folder_change_get_changed (change);
1541 /* Do not notify the observers if the folder of the header
1542 view has changed before this call to the observer
1544 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1545 if (tny_folder_change_get_folder (change) != priv->folder)
1548 /* Check folder count */
1549 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1550 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1551 helper = g_slice_new0 (HeadersCountChangedHelper);
1552 helper->self = MODEST_HEADER_VIEW(self);
1553 helper->change = g_object_ref(change);
1555 g_idle_add_full (G_PRIORITY_DEFAULT,
1556 idle_notify_headers_count_changed,
1558 idle_notify_headers_count_changed_destroy);
1563 modest_header_view_is_empty (ModestHeaderView *self)
1565 ModestHeaderViewPrivate *priv = NULL;
1567 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1573 modest_header_view_clear (ModestHeaderView *self)
1575 modest_header_view_set_folder (self, NULL, NULL, NULL);
1579 modest_header_view_copy_selection (ModestHeaderView *header_view)
1581 /* Copy selection */
1582 _clipboard_set_selected_data (header_view, FALSE);
1586 modest_header_view_cut_selection (ModestHeaderView *header_view)
1588 ModestHeaderViewPrivate *priv = NULL;
1589 const gchar **hidding = NULL;
1590 guint i, n_selected;
1592 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1593 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1595 /* Copy selection */
1596 _clipboard_set_selected_data (header_view, TRUE);
1598 /* Get hidding ids */
1599 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1601 /* Clear hidding array created by previous cut operation */
1602 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1604 /* Copy hidding array */
1605 priv->n_selected = n_selected;
1606 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1607 for (i=0; i < n_selected; i++)
1608 priv->hidding_ids[i] = g_strdup(hidding[i]);
1610 /* Hide cut headers */
1611 modest_header_view_refilter (header_view);
1618 _clipboard_set_selected_data (ModestHeaderView *header_view,
1621 ModestHeaderViewPrivate *priv = NULL;
1622 TnyList *headers = NULL;
1624 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1625 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1627 /* Set selected data on clipboard */
1628 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1629 headers = modest_header_view_get_selected_headers (header_view);
1630 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1633 g_object_unref (headers);
1639 filter_row (GtkTreeModel *model,
1643 ModestHeaderViewPrivate *priv = NULL;
1644 TnyHeaderFlags flags;
1645 TnyHeader *header = NULL;
1648 gboolean visible = TRUE;
1649 gboolean found = FALSE;
1651 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1652 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1654 /* Get header from model */
1655 gtk_tree_model_get (model, iter,
1656 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1657 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1660 /* Hide mark as deleted heders */
1661 if (flags & TNY_HEADER_FLAG_DELETED) {
1666 /* If no data on clipboard, return always TRUE */
1667 if (modest_email_clipboard_cleared(priv->clipboard)) {
1672 /* Get message id from header (ensure is a valid id) */
1673 if (!header) return FALSE;
1674 id = g_strdup(tny_header_get_message_id (header));
1677 if (priv->hidding_ids != NULL) {
1678 for (i=0; i < priv->n_selected && !found; i++)
1679 if (priv->hidding_ids[i] != NULL && id != NULL)
1680 found = (!strcmp (priv->hidding_ids[i], id));
1686 priv->empty = priv->empty && !visible;
1690 g_object_unref (header);
1697 _clear_hidding_filter (ModestHeaderView *header_view)
1699 ModestHeaderViewPrivate *priv = NULL;
1702 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1703 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1705 if (priv->hidding_ids != NULL) {
1706 for (i=0; i < priv->n_selected; i++)
1707 g_free (priv->hidding_ids[i]);
1708 g_free(priv->hidding_ids);
1713 modest_header_view_refilter (ModestHeaderView *header_view)
1715 GtkTreeModel *model = NULL;
1716 ModestHeaderViewPrivate *priv = NULL;
1718 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1719 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1723 /* Hide cut headers */
1724 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1725 if (GTK_IS_TREE_MODEL_FILTER (model))
1726 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1730 * Called when an account is removed. If I'm showing a folder of the
1731 * account that has been removed then clear the view
1734 on_account_removed (TnyAccountStore *self,
1735 TnyAccount *account,
1738 ModestHeaderViewPrivate *priv = NULL;
1740 /* Ignore changes in transport accounts */
1741 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1744 g_print ("--------------------- HEADER ---------------\n");
1746 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1749 TnyAccount *my_account;
1751 my_account = tny_folder_get_account (priv->folder);
1752 if (my_account == account)
1753 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1754 g_object_unref (account);
1758 void modest_header_view_add_observer(
1759 ModestHeaderView *header_view,
1760 ModestHeaderViewObserver *observer)
1762 ModestHeaderViewPrivate *priv = NULL;
1764 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1765 g_assert(observer != NULL);
1766 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1768 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1770 g_mutex_lock(priv->observer_list_lock);
1771 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1772 g_mutex_unlock(priv->observer_list_lock);
1775 void modest_header_view_remove_observer(
1776 ModestHeaderView *header_view,
1777 ModestHeaderViewObserver *observer)
1779 ModestHeaderViewPrivate *priv = NULL;
1781 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1782 g_assert(observer != NULL);
1783 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1785 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1787 g_mutex_lock(priv->observer_list_lock);
1788 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1789 g_mutex_unlock(priv->observer_list_lock);
1792 static void modest_header_view_notify_observers(
1793 ModestHeaderView *header_view,
1794 GtkTreeModel *model,
1795 const gchar *tny_folder_id)
1797 ModestHeaderViewPrivate *priv = NULL;
1799 ModestHeaderViewObserver *observer;
1801 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1803 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1805 g_mutex_lock(priv->observer_list_lock);
1806 iter = priv->observer_list;
1807 while(iter != NULL){
1808 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1809 modest_header_view_observer_update(observer, model,
1811 iter = g_slist_next(iter);
1813 g_mutex_unlock(priv->observer_list_lock);