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 static gboolean modest_header_view_on_expose_event(
104 GtkTreeView *header_view,
105 GdkEventExpose *event,
109 HEADER_VIEW_NON_EMPTY,
114 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
115 struct _ModestHeaderViewPrivate {
117 ModestHeaderViewStyle style;
119 TnyFolderMonitor *monitor;
120 GMutex *observers_lock;
122 /*header-view-observer observer*/
123 GMutex *observer_list_lock;
124 GSList *observer_list;
126 /* not unref this object, its a singlenton */
127 ModestEmailClipboard *clipboard;
129 /* Filter tree model */
133 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
134 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
136 gulong selection_changed_handler;
137 gulong acc_removed_handler;
139 HeaderViewStatus status;
140 GList *drag_begin_cached_selected_rows;
143 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
144 struct _HeadersCountChangedHelper {
145 ModestHeaderView *self;
146 TnyFolderChange *change;
150 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
151 MODEST_TYPE_HEADER_VIEW, \
152 ModestHeaderViewPrivate))
156 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
159 HEADER_SELECTED_SIGNAL,
160 HEADER_ACTIVATED_SIGNAL,
161 ITEM_NOT_FOUND_SIGNAL,
162 MSG_COUNT_CHANGED_SIGNAL,
167 static GObjectClass *parent_class = NULL;
169 /* uncomment the following if you have defined any signals */
170 static guint signals[LAST_SIGNAL] = {0};
173 modest_header_view_get_type (void)
175 static GType my_type = 0;
177 static const GTypeInfo my_info = {
178 sizeof(ModestHeaderViewClass),
179 NULL, /* base init */
180 NULL, /* base finalize */
181 (GClassInitFunc) modest_header_view_class_init,
182 NULL, /* class finalize */
183 NULL, /* class data */
184 sizeof(ModestHeaderView),
186 (GInstanceInitFunc) modest_header_view_init,
190 static const GInterfaceInfo tny_folder_observer_info =
192 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
193 NULL, /* interface_finalize */
194 NULL /* interface_data */
196 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
200 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
201 &tny_folder_observer_info);
209 modest_header_view_class_init (ModestHeaderViewClass *klass)
211 GObjectClass *gobject_class;
212 gobject_class = (GObjectClass*) klass;
214 parent_class = g_type_class_peek_parent (klass);
215 gobject_class->finalize = modest_header_view_finalize;
216 gobject_class->dispose = modest_header_view_dispose;
218 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
220 signals[HEADER_SELECTED_SIGNAL] =
221 g_signal_new ("header_selected",
222 G_TYPE_FROM_CLASS (gobject_class),
224 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
226 g_cclosure_marshal_VOID__POINTER,
227 G_TYPE_NONE, 1, G_TYPE_POINTER);
229 signals[HEADER_ACTIVATED_SIGNAL] =
230 g_signal_new ("header_activated",
231 G_TYPE_FROM_CLASS (gobject_class),
233 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
235 g_cclosure_marshal_VOID__POINTER,
236 G_TYPE_NONE, 1, G_TYPE_POINTER);
239 signals[ITEM_NOT_FOUND_SIGNAL] =
240 g_signal_new ("item_not_found",
241 G_TYPE_FROM_CLASS (gobject_class),
243 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
245 g_cclosure_marshal_VOID__INT,
246 G_TYPE_NONE, 1, G_TYPE_INT);
248 signals[MSG_COUNT_CHANGED_SIGNAL] =
249 g_signal_new ("msg_count_changed",
250 G_TYPE_FROM_CLASS (gobject_class),
252 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
254 modest_marshal_VOID__POINTER_POINTER,
255 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
259 tny_folder_observer_init (TnyFolderObserverIface *klass)
261 klass->update_func = folder_monitor_update;
264 static GtkTreeViewColumn*
265 get_new_column (const gchar *name, GtkCellRenderer *renderer,
266 gboolean resizable, gint sort_col_id, gboolean show_as_text,
267 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
269 GtkTreeViewColumn *column;
271 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
272 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
274 gtk_tree_view_column_set_resizable (column, resizable);
276 gtk_tree_view_column_set_expand (column, TRUE);
279 gtk_tree_view_column_add_attribute (column, renderer, "text",
281 if (sort_col_id >= 0)
282 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
284 gtk_tree_view_column_set_sort_indicator (column, FALSE);
285 gtk_tree_view_column_set_reorderable (column, TRUE);
288 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
295 remove_all_columns (ModestHeaderView *obj)
297 GList *columns, *cursor;
299 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
301 for (cursor = columns; cursor; cursor = cursor->next)
302 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
303 GTK_TREE_VIEW_COLUMN(cursor->data));
304 g_list_free (columns);
308 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
310 GtkTreeModel *tree_filter, *sortable;
311 GtkTreeViewColumn *column=NULL;
312 GtkTreeSelection *selection = NULL;
313 GtkCellRenderer *renderer_msgtype,*renderer_header,
314 *renderer_attach, *renderer_compact_date_or_status;
315 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
316 *renderer_subject, *renderer_subject_box, *renderer_recpt,
318 ModestHeaderViewPrivate *priv;
319 GtkTreeViewColumn *compact_column = NULL;
322 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
324 /* FIXME: check whether these renderers need to be freed */
325 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
326 renderer_attach = gtk_cell_renderer_pixbuf_new ();
327 renderer_priority = gtk_cell_renderer_pixbuf_new ();
328 renderer_header = gtk_cell_renderer_text_new ();
330 renderer_compact_header = modest_vbox_cell_renderer_new ();
331 renderer_recpt_box = modest_hbox_cell_renderer_new ();
332 renderer_subject_box = modest_hbox_cell_renderer_new ();
333 renderer_recpt = gtk_cell_renderer_text_new ();
334 renderer_subject = gtk_cell_renderer_text_new ();
335 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
337 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
338 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
339 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
340 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
341 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
342 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
343 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
344 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
345 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
346 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
347 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
348 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
349 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
350 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
352 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
353 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
354 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
355 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
356 g_object_set(G_OBJECT(renderer_header),
357 "ellipsize", PANGO_ELLIPSIZE_END,
359 g_object_set (G_OBJECT (renderer_subject),
360 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
362 g_object_set (G_OBJECT (renderer_recpt),
363 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
365 g_object_set(G_OBJECT(renderer_compact_date_or_status),
366 "xalign", 1.0, "yalign", 0.0,
368 g_object_set (G_OBJECT (renderer_priority),
369 "yalign", 1.0, NULL);
370 g_object_set (G_OBJECT (renderer_attach),
371 "yalign", 0.0, NULL);
373 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
374 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
375 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
377 remove_all_columns (self);
379 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
380 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
381 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
382 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
384 /* Add new columns */
385 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
386 ModestHeaderViewColumn col =
387 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
389 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
390 g_printerr ("modest: invalid column %d in column list\n", col);
396 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
397 column = get_new_column (_("M"), renderer_msgtype, FALSE,
398 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
400 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
402 gtk_tree_view_column_set_fixed_width (column, 45);
405 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
406 column = get_new_column (_("A"), renderer_attach, FALSE,
407 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
409 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
411 gtk_tree_view_column_set_fixed_width (column, 45);
415 case MODEST_HEADER_VIEW_COLUMN_FROM:
416 column = get_new_column (_("From"), renderer_header, TRUE,
417 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
419 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
420 GINT_TO_POINTER(TRUE));
423 case MODEST_HEADER_VIEW_COLUMN_TO:
424 column = get_new_column (_("To"), renderer_header, TRUE,
425 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
427 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
428 GINT_TO_POINTER(FALSE));
431 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
432 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
433 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
435 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
436 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
437 compact_column = column;
440 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
441 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
442 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
444 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
445 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
446 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
447 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
448 compact_column = column;
452 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
453 column = get_new_column (_("Subject"), renderer_header, TRUE,
454 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
456 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
460 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
461 column = get_new_column (_("Received"), renderer_header, TRUE,
462 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
464 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
465 GINT_TO_POINTER(TRUE));
468 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
469 column = get_new_column (_("Sent"), renderer_header, TRUE,
470 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
472 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
473 GINT_TO_POINTER(FALSE));
476 case MODEST_HEADER_VIEW_COLUMN_SIZE:
477 column = get_new_column (_("Size"), renderer_header, TRUE,
478 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
480 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
483 case MODEST_HEADER_VIEW_COLUMN_STATUS:
484 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
485 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
487 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
492 g_return_val_if_reached(FALSE);
495 /* we keep the column id around */
496 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
497 GINT_TO_POINTER(col));
499 /* we need this ptr when sorting the rows */
500 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
502 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
506 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
507 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
508 (GtkTreeIterCompareFunc) cmp_rows,
509 compact_column, NULL);
510 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
511 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
512 (GtkTreeIterCompareFunc) cmp_subject_rows,
513 compact_column, NULL);
521 modest_header_view_init (ModestHeaderView *obj)
523 ModestHeaderViewPrivate *priv;
526 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
530 priv->monitor = NULL;
531 priv->observers_lock = g_mutex_new ();
533 priv->status = HEADER_VIEW_INIT;
535 priv->observer_list_lock = g_mutex_new();
536 priv->observer_list = NULL;
538 priv->clipboard = modest_runtime_get_email_clipboard ();
539 priv->hidding_ids = NULL;
540 priv->n_selected = 0;
541 priv->selection_changed_handler = 0;
542 priv->acc_removed_handler = 0;
544 /* Sort parameters */
545 for (j=0; j < 2; j++) {
546 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
547 priv->sort_colid[j][i] = -1;
548 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
552 setup_drag_and_drop (GTK_TREE_VIEW (obj));
556 modest_header_view_dispose (GObject *obj)
558 ModestHeaderView *self;
559 ModestHeaderViewPrivate *priv;
560 GtkTreeSelection *sel;
562 self = MODEST_HEADER_VIEW(obj);
563 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
565 /* Free in the dispose to avoid unref cycles */
567 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
568 g_object_unref (G_OBJECT (priv->folder));
572 /* We need to do this here in the dispose because the
573 selection won't exist when finalizing */
574 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
575 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
576 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
577 priv->selection_changed_handler = 0;
580 G_OBJECT_CLASS(parent_class)->dispose (obj);
584 modest_header_view_finalize (GObject *obj)
586 ModestHeaderView *self;
587 ModestHeaderViewPrivate *priv;
589 self = MODEST_HEADER_VIEW(obj);
590 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
592 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
593 priv->acc_removed_handler)) {
594 g_signal_handler_disconnect (modest_runtime_get_account_store (),
595 priv->acc_removed_handler);
598 /* There is no need to lock because there should not be any
599 * reference to self now. */
600 g_mutex_free(priv->observer_list_lock);
601 g_slist_free(priv->observer_list);
603 g_mutex_lock (priv->observers_lock);
605 tny_folder_monitor_stop (priv->monitor);
606 g_object_unref (G_OBJECT (priv->monitor));
608 g_mutex_unlock (priv->observers_lock);
609 g_mutex_free (priv->observers_lock);
611 /* Clear hidding array created by cut operation */
612 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
614 G_OBJECT_CLASS(parent_class)->finalize (obj);
619 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
622 GtkTreeSelection *sel;
623 ModestHeaderView *self;
624 ModestHeaderViewPrivate *priv;
626 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
629 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
630 self = MODEST_HEADER_VIEW(obj);
631 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
633 modest_header_view_set_style (self, style);
634 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
636 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
637 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
638 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
640 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
641 TRUE); /* alternating row colors */
643 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
644 priv->selection_changed_handler =
645 g_signal_connect_after (sel, "changed",
646 G_CALLBACK(on_selection_changed), self);
648 g_signal_connect (self, "row-activated",
649 G_CALLBACK (on_header_row_activated), NULL);
651 g_signal_connect (self, "focus-in-event",
652 G_CALLBACK(on_focus_in), NULL);
654 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
656 G_CALLBACK (on_account_removed),
659 g_signal_connect (self, "expose-event",
660 G_CALLBACK(modest_header_view_on_expose_event),
663 return GTK_WIDGET(self);
668 modest_header_view_count_selected_headers (ModestHeaderView *self)
670 GtkTreeSelection *sel;
673 g_return_val_if_fail (self, 0);
675 /* Get selection object and check selected rows count */
676 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
677 selected_rows = gtk_tree_selection_count_selected_rows (sel);
679 return selected_rows;
683 modest_header_view_has_selected_headers (ModestHeaderView *self)
685 GtkTreeSelection *sel;
688 g_return_val_if_fail (self, FALSE);
690 /* Get selection object and check selected rows count */
691 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
692 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
699 modest_header_view_get_selected_headers (ModestHeaderView *self)
701 GtkTreeSelection *sel;
702 ModestHeaderViewPrivate *priv;
703 TnyList *header_list = NULL;
705 GList *list, *tmp = NULL;
706 GtkTreeModel *tree_model = NULL;
709 g_return_val_if_fail (self, NULL);
711 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
713 /* Get selected rows */
714 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
715 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
718 header_list = tny_simple_list_new();
720 list = g_list_reverse (list);
723 /* get header from selection */
724 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
725 gtk_tree_model_get (tree_model, &iter,
726 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
728 /* Prepend to list */
729 tny_list_prepend (header_list, G_OBJECT (header));
730 g_object_unref (G_OBJECT (header));
732 tmp = g_list_next (tmp);
735 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
742 /* scroll our list view so the selected item is visible */
744 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
746 #ifdef MODEST_PLATFORM_GNOME
748 GtkTreePath *selected_path;
749 GtkTreePath *start, *end;
753 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
754 selected_path = gtk_tree_model_get_path (model, iter);
756 start = gtk_tree_path_new ();
757 end = gtk_tree_path_new ();
759 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
761 if (gtk_tree_path_compare (selected_path, start) < 0 ||
762 gtk_tree_path_compare (end, selected_path) < 0)
763 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
764 selected_path, NULL, TRUE,
767 gtk_tree_path_free (selected_path);
768 gtk_tree_path_free (start);
769 gtk_tree_path_free (end);
771 #endif /* MODEST_PLATFORM_GNOME */
776 modest_header_view_select_next (ModestHeaderView *self)
778 GtkTreeSelection *sel;
783 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
784 path = get_selected_row (GTK_TREE_VIEW(self), &model);
785 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
786 /* Unselect previous path */
787 gtk_tree_selection_unselect_path (sel, path);
789 /* Move path down and selects new one */
790 if (gtk_tree_model_iter_next (model, &iter)) {
791 gtk_tree_selection_select_iter (sel, &iter);
792 scroll_to_selected (self, &iter, FALSE);
794 gtk_tree_path_free(path);
800 modest_header_view_select_prev (ModestHeaderView *self)
802 GtkTreeSelection *sel;
807 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
808 path = get_selected_row (GTK_TREE_VIEW(self), &model);
809 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
810 /* Unselect previous path */
811 gtk_tree_selection_unselect_path (sel, path);
814 if (gtk_tree_path_prev (path)) {
815 gtk_tree_model_get_iter (model, &iter, path);
817 /* Select the new one */
818 gtk_tree_selection_select_iter (sel, &iter);
819 scroll_to_selected (self, &iter, TRUE);
822 gtk_tree_path_free (path);
827 modest_header_view_get_columns (ModestHeaderView *self)
829 g_return_val_if_fail (self, FALSE);
830 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
836 modest_header_view_set_style (ModestHeaderView *self,
837 ModestHeaderViewStyle style)
839 ModestHeaderViewPrivate *priv;
840 gboolean show_col_headers = FALSE;
841 ModestHeaderViewStyle old_style;
843 g_return_val_if_fail (self, FALSE);
844 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
847 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
848 if (priv->style == style)
849 return TRUE; /* nothing to do */
852 case MODEST_HEADER_VIEW_STYLE_DETAILS:
853 show_col_headers = TRUE;
855 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
858 g_return_val_if_reached (FALSE);
860 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
861 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
863 old_style = priv->style;
870 ModestHeaderViewStyle
871 modest_header_view_get_style (ModestHeaderView *self)
873 g_return_val_if_fail (self, FALSE);
874 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
877 /* This is used to automatically select the first header if the user
878 * has not selected any header yet.
881 modest_header_view_on_expose_event(GtkTreeView *header_view,
882 GdkEventExpose *event,
885 GtkTreeSelection *sel;
887 GtkTreeIter tree_iter;
889 /* I'm invalidating this method because it causes an annoying
890 efect, the focus changes to the header view when selecting
891 a folder in the folder view because of this code and it
892 shouldn't. We need to find another way to set the passive
893 focus on it. Sergio. */
896 model = gtk_tree_view_get_model(header_view);
898 sel = gtk_tree_view_get_selection(header_view);
899 if(!gtk_tree_selection_count_selected_rows(sel))
900 if (gtk_tree_model_get_iter_first(model, &tree_iter))
901 gtk_tree_selection_select_iter(sel, &tree_iter);
907 * This function sets a sortable model in the header view. It's just
908 * used for developing purposes, because it only does a
909 * gtk_tree_view_set_model
912 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
914 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
915 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
916 /* GtkTreeModel *old_model; */
917 /* ModestHeaderViewPrivate *priv; */
918 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
919 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
921 /* /\* Set new model *\/ */
922 /* gtk_tree_view_set_model (header_view, model); */
924 gtk_tree_view_set_model (header_view, model);
928 modest_header_view_get_folder (ModestHeaderView *self)
930 ModestHeaderViewPrivate *priv;
931 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
934 g_object_ref (priv->folder);
940 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
944 ModestHeaderViewPrivate *priv;
945 GList *cols, *cursor;
946 GtkTreeModel *filter_model, *sortable;
948 GtkSortType sort_type;
950 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
952 headers = TNY_LIST (tny_gtk_header_list_model_new ());
954 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
955 folder, FALSE, NULL, NULL, NULL);
957 /* Add IDLE observer (monitor) and another folder observer for
958 new messages (self) */
959 g_mutex_lock (priv->observers_lock);
961 tny_folder_monitor_stop (priv->monitor);
962 g_object_unref (G_OBJECT (priv->monitor));
964 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
965 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
966 tny_folder_monitor_start (priv->monitor);
967 g_mutex_unlock (priv->observers_lock);
969 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
970 g_object_unref (G_OBJECT (headers));
972 /* Init filter_row function to examine empty status */
973 priv->status = HEADER_VIEW_INIT;
975 /* Create a tree model filter to hide and show rows for cut operations */
976 filter_model = gtk_tree_model_filter_new (sortable, NULL);
977 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
981 g_object_unref (G_OBJECT (sortable));
983 /* install our special sorting functions */
984 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
986 /* Restore sort column id */
988 type = modest_tny_folder_guess_folder_type (folder);
989 sort_colid = modest_header_view_get_sort_column_id (self, type);
990 sort_type = modest_header_view_get_sort_type (self, type);
991 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
994 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
995 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
996 (GtkTreeIterCompareFunc) cmp_rows,
998 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
999 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
1000 (GtkTreeIterCompareFunc) cmp_subject_rows,
1005 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1006 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1007 tny_folder_get_id(folder));
1008 g_object_unref (G_OBJECT (filter_model));
1009 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1010 /* g_object_unref (G_OBJECT (sortable)); */
1017 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1019 GtkSortType sort_type)
1021 ModestHeaderViewPrivate *priv = NULL;
1022 GtkTreeModel *tree_filter, *sortable = NULL;
1025 /* Get model and private data */
1026 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1027 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1028 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1029 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1031 /* Sort tree model */
1032 type = modest_tny_folder_guess_folder_type (priv->folder);
1033 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1036 /* Store new sort parameters */
1037 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1039 /* Save GConf parameters */
1040 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1041 /* G_OBJECT(self), "header-view"); */
1046 modest_header_view_set_sort_params (ModestHeaderView *self,
1048 GtkSortType sort_type,
1051 ModestHeaderViewPrivate *priv;
1052 ModestHeaderViewStyle style;
1054 style = modest_header_view_get_style (self);
1055 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1057 priv->sort_colid[style][type] = sort_colid;
1058 priv->sort_type[style][type] = sort_type;
1062 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1065 ModestHeaderViewPrivate *priv;
1066 ModestHeaderViewStyle style;
1068 style = modest_header_view_get_style (self);
1069 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1071 return priv->sort_colid[style][type];
1075 modest_header_view_get_sort_type (ModestHeaderView *self,
1078 ModestHeaderViewPrivate *priv;
1079 ModestHeaderViewStyle style;
1081 style = modest_header_view_get_style (self);
1082 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1084 return priv->sort_type[style][type];
1088 ModestHeaderView *header_view;
1089 RefreshAsyncUserCallback cb;
1094 folder_refreshed_cb (ModestMailOperation *mail_op,
1098 ModestHeaderViewPrivate *priv;
1099 SetFolderHelper *info;
1101 info = (SetFolderHelper*) user_data;
1103 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1107 info->cb (mail_op, folder, info->user_data);
1109 /* Start the folder count changes observer. We do not need it
1110 before the refresh. Note that the monitor could still be
1111 called for this refresh but now we know that the callback
1112 was previously called */
1113 g_mutex_lock (priv->observers_lock);
1114 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1115 g_mutex_unlock (priv->observers_lock);
1122 modest_header_view_set_folder (ModestHeaderView *self,
1124 RefreshAsyncUserCallback callback,
1127 ModestHeaderViewPrivate *priv;
1128 ModestWindowMgr *mgr = NULL;
1129 GObject *source = NULL;
1130 SetFolderHelper *info;
1132 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1135 g_mutex_lock (priv->observers_lock);
1136 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1137 g_object_unref (priv->folder);
1138 priv->folder = NULL;
1139 g_mutex_unlock (priv->observers_lock);
1143 ModestMailOperation *mail_op = NULL;
1144 GtkTreeSelection *selection;
1146 /* Get main window to use it as source of mail operation */
1147 mgr = modest_runtime_get_window_mgr ();
1148 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1150 /* Set folder in the model */
1151 modest_header_view_set_folder_intern (self, folder);
1153 /* Pick my reference. Nothing to do with the mail operation */
1154 priv->folder = g_object_ref (folder);
1156 /* Clear the selection if exists */
1157 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1158 gtk_tree_selection_unselect_all(selection);
1159 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1161 /* create the helper */
1162 info = g_malloc0 (sizeof(SetFolderHelper));
1163 info->header_view = self;
1164 info->cb = callback;
1165 info->user_data = user_data;
1167 /* Create the mail operation (source will be the parent widget) */
1168 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1169 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1172 /* Refresh the folder asynchronously */
1173 modest_mail_operation_refresh_folder (mail_op,
1175 folder_refreshed_cb,
1179 g_object_unref (mail_op);
1181 g_mutex_lock (priv->observers_lock);
1183 if (priv->monitor) {
1184 tny_folder_monitor_stop (priv->monitor);
1185 g_object_unref (G_OBJECT (priv->monitor));
1186 priv->monitor = NULL;
1188 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1190 modest_header_view_notify_observers(self, NULL, NULL);
1192 g_mutex_unlock (priv->observers_lock);
1197 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1198 GtkTreeViewColumn *column, gpointer userdata)
1200 ModestHeaderView *self = NULL;
1201 ModestHeaderViewPrivate *priv = NULL;
1203 GtkTreeModel *model = NULL;
1204 TnyHeader *header = NULL;
1205 TnyHeaderFlags flags;
1207 self = MODEST_HEADER_VIEW (treeview);
1208 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1210 model = gtk_tree_view_get_model (treeview);
1211 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1214 /* get the first selected item */
1215 gtk_tree_model_get (model, &iter,
1216 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1217 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1220 /* Dont open DELETED messages */
1221 if (flags & TNY_HEADER_FLAG_DELETED) {
1222 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1227 g_signal_emit (G_OBJECT(self),
1228 signals[HEADER_ACTIVATED_SIGNAL],
1234 g_object_unref (G_OBJECT (header));
1239 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1241 GtkTreeModel *model;
1242 TnyHeader *header = NULL;
1243 GtkTreePath *path = NULL;
1245 ModestHeaderView *self;
1246 ModestHeaderViewPrivate *priv;
1247 GList *selected = NULL;
1249 g_return_if_fail (sel);
1250 g_return_if_fail (user_data);
1252 self = MODEST_HEADER_VIEW (user_data);
1253 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1255 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1256 if (selected != NULL)
1257 path = (GtkTreePath *) selected->data;
1258 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1259 return; /* msg was _un_selected */
1261 gtk_tree_model_get (model, &iter,
1262 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1266 g_signal_emit (G_OBJECT(self),
1267 signals[HEADER_SELECTED_SIGNAL],
1270 g_object_unref (G_OBJECT (header));
1272 /* free all items in 'selected' */
1273 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1274 g_list_free (selected);
1278 /* PROTECTED method. It's useful when we want to force a given
1279 selection to reload a msg. For example if we have selected a header
1280 in offline mode, when Modest become online, we want to reload the
1281 message automatically without an user click over the header */
1283 _modest_header_view_change_selection (GtkTreeSelection *selection,
1286 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1287 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1289 on_selection_changed (selection, user_data);
1292 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1294 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1295 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1297 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1299 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1304 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1311 /* static int counter = 0; */
1313 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1314 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1315 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1319 case TNY_HEADER_FLAG_ATTACHMENTS:
1321 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1322 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1323 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1324 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1326 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1327 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1329 return cmp ? cmp : t1 - t2;
1331 case TNY_HEADER_FLAG_PRIORITY:
1332 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1333 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1334 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1335 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1337 /* This is for making priority values respect the intuitive sort relationship
1338 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1339 cmp = compare_priorities (val1, val2);
1341 return cmp ? cmp : t1 - t2;
1344 return &iter1 - &iter2; /* oughhhh */
1349 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1355 /* static int counter = 0; */
1357 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1359 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1360 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1361 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1362 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1364 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1365 val2 + modest_text_utils_get_subject_prefix_len(val2),
1372 /* Drag and drop stuff */
1374 drag_data_get_cb (GtkWidget *widget,
1375 GdkDragContext *context,
1376 GtkSelectionData *selection_data,
1381 ModestHeaderView *self = NULL;
1382 ModestHeaderViewPrivate *priv = NULL;
1384 self = MODEST_HEADER_VIEW (widget);
1385 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1387 /* Set the data. Do not use the current selection because it
1388 could be different than the selection at the beginning of
1390 modest_dnd_selection_data_set_paths (selection_data,
1391 priv->drag_begin_cached_selected_rows);
1395 * We're caching the selected rows at the beginning because the
1396 * selection could change between drag-begin and drag-data-get, for
1397 * example if we have a set of rows already selected, and then we
1398 * click in one of them (without SHIFT key pressed) and begin a drag,
1399 * the selection at that moment contains all the selected lines, but
1400 * after dropping the selection, the release event provokes that only
1401 * the row used to begin the drag is selected, so at the end the
1402 * drag&drop affects only one rows instead of all the selected ones.
1406 drag_begin_cb (GtkWidget *widget,
1407 GdkDragContext *context,
1410 ModestHeaderView *self = NULL;
1411 ModestHeaderViewPrivate *priv = NULL;
1412 GtkTreeSelection *selection;
1414 self = MODEST_HEADER_VIEW (widget);
1415 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1417 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1418 priv->drag_begin_cached_selected_rows =
1419 gtk_tree_selection_get_selected_rows (selection, NULL);
1423 * We use the drag-end signal to clear the cached selection, we use
1424 * this because this allways happens, whether or not the d&d was a
1428 drag_end_cb (GtkWidget *widget,
1432 ModestHeaderView *self = NULL;
1433 ModestHeaderViewPrivate *priv = NULL;
1435 self = MODEST_HEADER_VIEW (widget);
1436 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1438 /* Free cached data */
1439 g_list_foreach (priv->drag_begin_cached_selected_rows, (GFunc) gtk_tree_path_free, NULL);
1440 g_list_free (priv->drag_begin_cached_selected_rows);
1441 priv->drag_begin_cached_selected_rows = NULL;
1444 /* Header view drag types */
1445 const GtkTargetEntry header_view_drag_types[] = {
1446 { GTK_TREE_PATH_AS_STRING_LIST, GTK_TARGET_SAME_APP, MODEST_HEADER_ROW }
1450 setup_drag_and_drop (GtkTreeView *self)
1452 gtk_drag_source_set (GTK_WIDGET (self),
1454 header_view_drag_types,
1455 G_N_ELEMENTS (header_view_drag_types),
1456 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1458 g_signal_connect(G_OBJECT (self), "drag_data_get",
1459 G_CALLBACK(drag_data_get_cb), NULL);
1461 g_signal_connect(G_OBJECT (self), "drag_begin",
1462 G_CALLBACK(drag_begin_cb), NULL);
1464 g_signal_connect(G_OBJECT (self), "drag_end",
1465 G_CALLBACK(drag_end_cb), NULL);
1468 static GtkTreePath *
1469 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1471 GtkTreePath *path = NULL;
1472 GtkTreeSelection *sel = NULL;
1475 sel = gtk_tree_view_get_selection(self);
1476 rows = gtk_tree_selection_get_selected_rows (sel, model);
1478 if ((rows == NULL) || (g_list_length(rows) != 1))
1481 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1486 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1493 * This function moves the tree view scroll to the current selected
1494 * row when the widget grabs the focus
1497 on_focus_in (GtkWidget *self,
1498 GdkEventFocus *event,
1501 GtkTreeSelection *selection;
1502 GtkTreeModel *model;
1503 GList *selected = NULL;
1504 GtkTreePath *selected_path = NULL;
1506 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1510 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1511 /* If none selected yet, pick the first one */
1512 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1516 /* Return if the model is empty */
1517 if (!gtk_tree_model_get_iter_first (model, &iter))
1520 path = gtk_tree_model_get_path (model, &iter);
1521 gtk_tree_selection_select_path (selection, path);
1522 gtk_tree_path_free (path);
1525 /* Need to get the all the rows because is selection multiple */
1526 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1527 if (selected == NULL) return FALSE;
1528 selected_path = (GtkTreePath *) selected->data;
1530 /* Check if we need to scroll */
1531 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1532 GtkTreePath *start_path = NULL;
1533 GtkTreePath *end_path = NULL;
1534 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1538 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1539 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1541 /* Scroll to first path */
1542 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1551 gtk_tree_path_free (start_path);
1553 gtk_tree_path_free (end_path);
1555 #endif /* GTK_CHECK_VERSION */
1558 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1559 g_list_free (selected);
1565 folder_monitor_update (TnyFolderObserver *self,
1566 TnyFolderChange *change)
1568 ModestHeaderViewPrivate *priv = NULL;
1569 TnyFolderChangeChanged changed;
1570 TnyFolder *folder = NULL;
1572 changed = tny_folder_change_get_changed (change);
1574 /* Do not notify the observers if the folder of the header
1575 view has changed before this call to the observer
1577 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1578 folder = tny_folder_change_get_folder (change);
1579 if (folder != priv->folder)
1582 /* Check folder count */
1583 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1584 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1586 g_mutex_lock (priv->observers_lock);
1588 /* Emit signal to evaluate how headers changes affects
1589 to the window view */
1590 g_signal_emit (G_OBJECT(self),
1591 signals[MSG_COUNT_CHANGED_SIGNAL],
1594 /* Added or removed headers, so data stored on cliboard are invalid */
1595 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1596 modest_email_clipboard_clear (priv->clipboard);
1598 g_mutex_unlock (priv->observers_lock);
1604 g_object_unref (folder);
1608 modest_header_view_is_empty (ModestHeaderView *self)
1610 ModestHeaderViewPrivate *priv = NULL;
1612 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1614 return priv->status == HEADER_VIEW_EMPTY;
1618 modest_header_view_clear (ModestHeaderView *self)
1620 modest_header_view_set_folder (self, NULL, NULL, NULL);
1624 modest_header_view_copy_selection (ModestHeaderView *header_view)
1626 /* Copy selection */
1627 _clipboard_set_selected_data (header_view, FALSE);
1631 modest_header_view_cut_selection (ModestHeaderView *header_view)
1633 ModestHeaderViewPrivate *priv = NULL;
1634 const gchar **hidding = NULL;
1635 guint i, n_selected;
1637 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1638 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1640 /* Copy selection */
1641 _clipboard_set_selected_data (header_view, TRUE);
1643 /* Get hidding ids */
1644 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1646 /* Clear hidding array created by previous cut operation */
1647 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1649 /* Copy hidding array */
1650 priv->n_selected = n_selected;
1651 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1652 for (i=0; i < n_selected; i++)
1653 priv->hidding_ids[i] = g_strdup(hidding[i]);
1655 /* Hide cut headers */
1656 modest_header_view_refilter (header_view);
1663 _clipboard_set_selected_data (ModestHeaderView *header_view,
1666 ModestHeaderViewPrivate *priv = NULL;
1667 TnyList *headers = NULL;
1669 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1670 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1672 /* Set selected data on clipboard */
1673 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1674 headers = modest_header_view_get_selected_headers (header_view);
1675 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1678 g_object_unref (headers);
1684 filter_row (GtkTreeModel *model,
1688 ModestHeaderViewPrivate *priv = NULL;
1689 TnyHeaderFlags flags;
1690 TnyHeader *header = NULL;
1693 gboolean visible = TRUE;
1694 gboolean found = FALSE;
1696 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1697 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1699 /* Get header from model */
1700 gtk_tree_model_get (model, iter,
1701 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1702 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1705 /* Hide mark as deleted heders */
1706 if (flags & TNY_HEADER_FLAG_DELETED) {
1711 /* If no data on clipboard, return always TRUE */
1712 if (modest_email_clipboard_cleared(priv->clipboard)) {
1717 /* Get message id from header (ensure is a valid id) */
1718 if (!header) return FALSE;
1719 id = g_strdup(tny_header_get_message_id (header));
1722 if (priv->hidding_ids != NULL) {
1723 for (i=0; i < priv->n_selected && !found; i++)
1724 if (priv->hidding_ids[i] != NULL && id != NULL)
1725 found = (!strcmp (priv->hidding_ids[i], id));
1731 priv->status = ((gboolean) priv->status) && !visible;
1735 g_object_unref (header);
1742 _clear_hidding_filter (ModestHeaderView *header_view)
1744 ModestHeaderViewPrivate *priv = NULL;
1747 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1748 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1750 if (priv->hidding_ids != NULL) {
1751 for (i=0; i < priv->n_selected; i++)
1752 g_free (priv->hidding_ids[i]);
1753 g_free(priv->hidding_ids);
1758 modest_header_view_refilter (ModestHeaderView *header_view)
1760 GtkTreeModel *model = NULL;
1761 ModestHeaderViewPrivate *priv = NULL;
1763 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1764 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1766 /* Hide cut headers */
1767 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1768 if (GTK_IS_TREE_MODEL_FILTER (model)) {
1770 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1775 * Called when an account is removed. If I'm showing a folder of the
1776 * account that has been removed then clear the view
1779 on_account_removed (TnyAccountStore *self,
1780 TnyAccount *account,
1783 ModestHeaderViewPrivate *priv = NULL;
1785 /* Ignore changes in transport accounts */
1786 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1789 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1792 TnyAccount *my_account;
1794 my_account = tny_folder_get_account (priv->folder);
1795 if (my_account == account)
1796 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1797 g_object_unref (account);
1801 void modest_header_view_add_observer(
1802 ModestHeaderView *header_view,
1803 ModestHeaderViewObserver *observer)
1805 ModestHeaderViewPrivate *priv = NULL;
1807 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1808 g_assert(observer != NULL);
1809 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1811 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1813 g_mutex_lock(priv->observer_list_lock);
1814 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1815 g_mutex_unlock(priv->observer_list_lock);
1819 modest_header_view_remove_observer(ModestHeaderView *header_view,
1820 ModestHeaderViewObserver *observer)
1822 ModestHeaderViewPrivate *priv = NULL;
1824 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1825 g_assert(observer != NULL);
1826 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1828 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1830 g_mutex_lock(priv->observer_list_lock);
1831 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1832 g_mutex_unlock(priv->observer_list_lock);
1836 modest_header_view_notify_observers(ModestHeaderView *header_view,
1837 GtkTreeModel *model,
1838 const gchar *tny_folder_id)
1840 ModestHeaderViewPrivate *priv = NULL;
1842 ModestHeaderViewObserver *observer;
1844 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1846 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1848 g_mutex_lock(priv->observer_list_lock);
1849 iter = priv->observer_list;
1850 while(iter != NULL){
1851 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1852 modest_header_view_observer_update(observer, model,
1854 iter = g_slist_next(iter);
1856 g_mutex_unlock(priv->observer_list_lock);