1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include <glib/gi18n.h>
32 #include <tny-simple-list.h>
33 #include <tny-folder-monitor.h>
34 #include <tny-folder-change.h>
37 #include <modest-header-view.h>
38 #include <modest-header-view-priv.h>
39 #include <modest-dnd.h>
40 #include <modest-tny-folder.h>
42 #include <modest-main-window.h>
44 #include <modest-marshal.h>
45 #include <modest-text-utils.h>
46 #include <modest-icon-names.h>
47 #include <modest-runtime.h>
48 #include "modest-platform.h"
49 #include <modest-hbox-cell-renderer.h>
50 #include <modest-vbox-cell-renderer.h>
52 static void modest_header_view_class_init (ModestHeaderViewClass *klass);
53 static void modest_header_view_init (ModestHeaderView *obj);
54 static void modest_header_view_finalize (GObject *obj);
55 static void modest_header_view_dispose (GObject *obj);
57 static void on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
58 GtkTreeViewColumn *column, gpointer userdata);
60 static gint cmp_rows (GtkTreeModel *tree_model,
65 static gint cmp_subject_rows (GtkTreeModel *tree_model,
70 static gboolean filter_row (GtkTreeModel *model,
74 static void on_account_removed (TnyAccountStore *self,
78 static void on_selection_changed (GtkTreeSelection *sel,
81 static void setup_drag_and_drop (GtkTreeView *self);
83 static GtkTreePath * get_selected_row (GtkTreeView *self, GtkTreeModel **model);
85 static gboolean on_focus_in (GtkWidget *sef,
89 static void folder_monitor_update (TnyFolderObserver *self,
90 TnyFolderChange *change);
92 static void tny_folder_observer_init (TnyFolderObserverIface *klass);
94 static void _clipboard_set_selected_data (ModestHeaderView *header_view, gboolean delete);
96 static void _clear_hidding_filter (ModestHeaderView *header_view);
99 typedef struct _ModestHeaderViewPrivate ModestHeaderViewPrivate;
100 struct _ModestHeaderViewPrivate {
102 ModestHeaderViewStyle style;
104 TnyFolderMonitor *monitor;
105 GMutex *observers_lock;
107 /* not unref this object, its a singlenton */
108 ModestEmailClipboard *clipboard;
110 /* Filter tree model */
114 gint sort_colid[2][TNY_FOLDER_TYPE_NUM];
115 gint sort_type[2][TNY_FOLDER_TYPE_NUM];
117 gulong selection_changed_handler;
118 gulong acc_removed_handler;
121 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
122 struct _HeadersCountChangedHelper {
123 ModestHeaderView *self;
124 TnyFolderChange *change;
128 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
129 MODEST_TYPE_HEADER_VIEW, \
130 ModestHeaderViewPrivate))
134 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
137 HEADER_SELECTED_SIGNAL,
138 HEADER_ACTIVATED_SIGNAL,
139 ITEM_NOT_FOUND_SIGNAL,
140 MSG_COUNT_CHANGED_SIGNAL,
145 static GObjectClass *parent_class = NULL;
147 /* uncomment the following if you have defined any signals */
148 static guint signals[LAST_SIGNAL] = {0};
151 modest_header_view_get_type (void)
153 static GType my_type = 0;
155 static const GTypeInfo my_info = {
156 sizeof(ModestHeaderViewClass),
157 NULL, /* base init */
158 NULL, /* base finalize */
159 (GClassInitFunc) modest_header_view_class_init,
160 NULL, /* class finalize */
161 NULL, /* class data */
162 sizeof(ModestHeaderView),
164 (GInstanceInitFunc) modest_header_view_init,
168 static const GInterfaceInfo tny_folder_observer_info =
170 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
171 NULL, /* interface_finalize */
172 NULL /* interface_data */
174 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
178 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
179 &tny_folder_observer_info);
187 modest_header_view_class_init (ModestHeaderViewClass *klass)
189 GObjectClass *gobject_class;
190 gobject_class = (GObjectClass*) klass;
192 parent_class = g_type_class_peek_parent (klass);
193 gobject_class->finalize = modest_header_view_finalize;
194 gobject_class->dispose = modest_header_view_dispose;
196 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
198 signals[HEADER_SELECTED_SIGNAL] =
199 g_signal_new ("header_selected",
200 G_TYPE_FROM_CLASS (gobject_class),
202 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
204 g_cclosure_marshal_VOID__POINTER,
205 G_TYPE_NONE, 1, G_TYPE_POINTER);
207 signals[HEADER_ACTIVATED_SIGNAL] =
208 g_signal_new ("header_activated",
209 G_TYPE_FROM_CLASS (gobject_class),
211 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
213 g_cclosure_marshal_VOID__POINTER,
214 G_TYPE_NONE, 1, G_TYPE_POINTER);
217 signals[ITEM_NOT_FOUND_SIGNAL] =
218 g_signal_new ("item_not_found",
219 G_TYPE_FROM_CLASS (gobject_class),
221 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
223 g_cclosure_marshal_VOID__INT,
224 G_TYPE_NONE, 1, G_TYPE_INT);
226 signals[MSG_COUNT_CHANGED_SIGNAL] =
227 g_signal_new ("msg_count_changed",
228 G_TYPE_FROM_CLASS (gobject_class),
230 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
232 modest_marshal_VOID__POINTER_POINTER,
233 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
237 tny_folder_observer_init (TnyFolderObserverIface *klass)
239 klass->update_func = folder_monitor_update;
242 static GtkTreeViewColumn*
243 get_new_column (const gchar *name, GtkCellRenderer *renderer,
244 gboolean resizable, gint sort_col_id, gboolean show_as_text,
245 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
247 GtkTreeViewColumn *column;
249 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
250 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
252 gtk_tree_view_column_set_resizable (column, resizable);
254 gtk_tree_view_column_set_expand (column, TRUE);
257 gtk_tree_view_column_add_attribute (column, renderer, "text",
259 if (sort_col_id >= 0)
260 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
262 gtk_tree_view_column_set_sort_indicator (column, FALSE);
263 gtk_tree_view_column_set_reorderable (column, TRUE);
266 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
273 remove_all_columns (ModestHeaderView *obj)
275 GList *columns, *cursor;
277 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
279 for (cursor = columns; cursor; cursor = cursor->next)
280 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
281 GTK_TREE_VIEW_COLUMN(cursor->data));
282 g_list_free (columns);
286 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
288 GtkTreeModel *tree_filter, *sortable;
289 GtkTreeViewColumn *column=NULL;
290 GtkTreeSelection *selection = NULL;
291 GtkCellRenderer *renderer_msgtype,*renderer_header,
292 *renderer_attach, *renderer_compact_date_or_status;
293 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
294 *renderer_subject, *renderer_subject_box, *renderer_recpt,
296 ModestHeaderViewPrivate *priv;
297 GtkTreeViewColumn *compact_column = NULL;
300 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
302 /* FIXME: check whether these renderers need to be freed */
303 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
304 renderer_attach = gtk_cell_renderer_pixbuf_new ();
305 renderer_priority = gtk_cell_renderer_pixbuf_new ();
306 renderer_header = gtk_cell_renderer_text_new ();
308 renderer_compact_header = modest_vbox_cell_renderer_new ();
309 renderer_recpt_box = modest_hbox_cell_renderer_new ();
310 renderer_subject_box = modest_hbox_cell_renderer_new ();
311 renderer_recpt = gtk_cell_renderer_text_new ();
312 renderer_subject = gtk_cell_renderer_text_new ();
313 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
315 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
316 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
317 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
318 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
319 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
320 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
321 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
322 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
323 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
324 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
325 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
326 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
327 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
328 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
330 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
331 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
332 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
333 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
334 g_object_set(G_OBJECT(renderer_header),
335 "ellipsize", PANGO_ELLIPSIZE_END,
337 g_object_set (G_OBJECT (renderer_subject),
338 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
340 g_object_set (G_OBJECT (renderer_recpt),
341 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
343 g_object_set(G_OBJECT(renderer_compact_date_or_status),
344 "xalign", 1.0, "yalign", 0.0,
346 g_object_set (G_OBJECT (renderer_priority),
347 "yalign", 1.0, NULL);
348 g_object_set (G_OBJECT (renderer_attach),
349 "yalign", 0.0, NULL);
351 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
352 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
353 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
355 remove_all_columns (self);
357 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
358 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
359 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
360 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
362 /* Add new columns */
363 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
364 ModestHeaderViewColumn col =
365 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
367 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
368 g_printerr ("modest: invalid column %d in column list\n", col);
374 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
375 column = get_new_column (_("M"), renderer_msgtype, FALSE,
376 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
378 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
380 gtk_tree_view_column_set_fixed_width (column, 45);
383 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
384 column = get_new_column (_("A"), renderer_attach, FALSE,
385 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
387 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
389 gtk_tree_view_column_set_fixed_width (column, 45);
393 case MODEST_HEADER_VIEW_COLUMN_FROM:
394 column = get_new_column (_("From"), renderer_header, TRUE,
395 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
397 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
398 GINT_TO_POINTER(TRUE));
401 case MODEST_HEADER_VIEW_COLUMN_TO:
402 column = get_new_column (_("To"), renderer_header, TRUE,
403 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
405 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
406 GINT_TO_POINTER(FALSE));
409 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
410 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
411 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
413 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
414 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
415 compact_column = column;
418 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
419 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
420 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
422 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
423 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
424 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
425 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
426 compact_column = column;
430 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
431 column = get_new_column (_("Subject"), renderer_header, TRUE,
432 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
434 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
438 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
439 column = get_new_column (_("Received"), renderer_header, TRUE,
440 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
442 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
443 GINT_TO_POINTER(TRUE));
446 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
447 column = get_new_column (_("Sent"), renderer_header, TRUE,
448 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
450 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
451 GINT_TO_POINTER(FALSE));
454 case MODEST_HEADER_VIEW_COLUMN_SIZE:
455 column = get_new_column (_("Size"), renderer_header, TRUE,
456 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
458 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
461 case MODEST_HEADER_VIEW_COLUMN_STATUS:
462 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
463 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
465 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
470 g_return_val_if_reached(FALSE);
473 /* we keep the column id around */
474 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
475 GINT_TO_POINTER(col));
477 /* we need this ptr when sorting the rows */
478 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
480 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
484 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
485 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
486 (GtkTreeIterCompareFunc) cmp_rows,
487 compact_column, NULL);
488 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
489 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
490 (GtkTreeIterCompareFunc) cmp_subject_rows,
491 compact_column, NULL);
499 modest_header_view_init (ModestHeaderView *obj)
501 ModestHeaderViewPrivate *priv;
504 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
508 priv->monitor = NULL;
509 priv->observers_lock = g_mutex_new ();
511 priv->clipboard = modest_runtime_get_email_clipboard ();
512 priv->hidding_ids = NULL;
513 priv->n_selected = 0;
514 priv->selection_changed_handler = 0;
515 priv->acc_removed_handler = 0;
517 /* Sort parameters */
518 for (j=0; j < 2; j++) {
519 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
520 priv->sort_colid[j][i] = -1;
521 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
525 setup_drag_and_drop (GTK_TREE_VIEW (obj));
529 modest_header_view_dispose (GObject *obj)
531 ModestHeaderView *self;
532 ModestHeaderViewPrivate *priv;
533 GtkTreeSelection *sel;
535 self = MODEST_HEADER_VIEW(obj);
536 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
538 /* Free in the dispose to avoid unref cycles */
540 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
541 g_object_unref (G_OBJECT (priv->folder));
545 /* We need to do this here in the dispose because the
546 selection won't exist when finalizing */
547 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
548 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
549 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
550 priv->selection_changed_handler = 0;
553 G_OBJECT_CLASS(parent_class)->dispose (obj);
557 modest_header_view_finalize (GObject *obj)
559 ModestHeaderView *self;
560 ModestHeaderViewPrivate *priv;
562 self = MODEST_HEADER_VIEW(obj);
563 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
565 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
566 priv->acc_removed_handler)) {
567 g_signal_handler_disconnect (modest_runtime_get_account_store (),
568 priv->acc_removed_handler);
571 g_mutex_lock (priv->observers_lock);
573 tny_folder_monitor_stop (priv->monitor);
574 g_object_unref (G_OBJECT (priv->monitor));
576 g_mutex_unlock (priv->observers_lock);
577 g_mutex_free (priv->observers_lock);
579 /* Clear hidding array created by cut operation */
580 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
582 G_OBJECT_CLASS(parent_class)->finalize (obj);
587 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
590 GtkTreeSelection *sel;
591 ModestHeaderView *self;
592 ModestHeaderViewPrivate *priv;
594 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
597 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
598 self = MODEST_HEADER_VIEW(obj);
599 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
601 modest_header_view_set_style (self, style);
602 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
604 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
605 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
606 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
608 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
609 TRUE); /* alternating row colors */
611 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
612 priv->selection_changed_handler =
613 g_signal_connect_after (sel, "changed",
614 G_CALLBACK(on_selection_changed), self);
616 g_signal_connect (self, "row-activated",
617 G_CALLBACK (on_header_row_activated), NULL);
619 g_signal_connect (self, "focus-in-event",
620 G_CALLBACK(on_focus_in), NULL);
622 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
624 G_CALLBACK (on_account_removed),
627 return GTK_WIDGET(self);
632 modest_header_view_count_selected_headers (ModestHeaderView *self)
634 GtkTreeSelection *sel;
637 g_return_val_if_fail (self, 0);
639 /* Get selection object and check selected rows count */
640 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
641 selected_rows = gtk_tree_selection_count_selected_rows (sel);
643 return selected_rows;
647 modest_header_view_has_selected_headers (ModestHeaderView *self)
649 GtkTreeSelection *sel;
652 g_return_val_if_fail (self, FALSE);
654 /* Get selection object and check selected rows count */
655 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
656 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
663 modest_header_view_get_selected_headers (ModestHeaderView *self)
665 GtkTreeSelection *sel;
666 ModestHeaderViewPrivate *priv;
667 TnyList *header_list = NULL;
669 GList *list, *tmp = NULL;
670 GtkTreeModel *tree_model = NULL;
673 g_return_val_if_fail (self, NULL);
675 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
677 /* Get selected rows */
678 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
679 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
682 header_list = tny_simple_list_new();
684 list = g_list_reverse (list);
687 /* get header from selection */
688 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
689 gtk_tree_model_get (tree_model, &iter,
690 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
692 /* Prepend to list */
693 tny_list_prepend (header_list, G_OBJECT (header));
694 g_object_unref (G_OBJECT (header));
696 tmp = g_list_next (tmp);
699 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
706 /* scroll our list view so the selected item is visible */
708 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
710 #ifdef MODEST_PLATFORM_GNOME
712 GtkTreePath *selected_path;
713 GtkTreePath *start, *end;
717 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
718 selected_path = gtk_tree_model_get_path (model, iter);
720 start = gtk_tree_path_new ();
721 end = gtk_tree_path_new ();
723 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
725 if (gtk_tree_path_compare (selected_path, start) < 0 ||
726 gtk_tree_path_compare (end, selected_path) < 0)
727 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
728 selected_path, NULL, TRUE,
731 gtk_tree_path_free (selected_path);
732 gtk_tree_path_free (start);
733 gtk_tree_path_free (end);
735 #endif /* MODEST_PLATFORM_GNOME */
740 modest_header_view_select_next (ModestHeaderView *self)
742 GtkTreeSelection *sel;
747 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
748 path = get_selected_row (GTK_TREE_VIEW(self), &model);
749 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
750 /* Unselect previous path */
751 gtk_tree_selection_unselect_path (sel, path);
753 /* Move path down and selects new one */
754 if (gtk_tree_model_iter_next (model, &iter)) {
755 gtk_tree_selection_select_iter (sel, &iter);
756 scroll_to_selected (self, &iter, FALSE);
758 gtk_tree_path_free(path);
764 modest_header_view_select_prev (ModestHeaderView *self)
766 GtkTreeSelection *sel;
771 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
772 path = get_selected_row (GTK_TREE_VIEW(self), &model);
773 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
774 /* Unselect previous path */
775 gtk_tree_selection_unselect_path (sel, path);
778 if (gtk_tree_path_prev (path)) {
779 gtk_tree_model_get_iter (model, &iter, path);
781 /* Select the new one */
782 gtk_tree_selection_select_iter (sel, &iter);
783 scroll_to_selected (self, &iter, TRUE);
786 gtk_tree_path_free (path);
791 modest_header_view_get_columns (ModestHeaderView *self)
793 g_return_val_if_fail (self, FALSE);
794 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
799 modest_header_view_is_empty (ModestHeaderView *self)
801 g_return_val_if_fail (self, FALSE);
802 return FALSE; /* FIXME */
807 modest_header_view_set_style (ModestHeaderView *self,
808 ModestHeaderViewStyle style)
810 ModestHeaderViewPrivate *priv;
811 gboolean show_col_headers = FALSE;
812 ModestHeaderViewStyle old_style;
814 g_return_val_if_fail (self, FALSE);
815 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
818 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
819 if (priv->style == style)
820 return TRUE; /* nothing to do */
823 case MODEST_HEADER_VIEW_STYLE_DETAILS:
824 show_col_headers = TRUE;
826 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
829 g_return_val_if_reached (FALSE);
831 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
832 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
834 old_style = priv->style;
841 ModestHeaderViewStyle
842 modest_header_view_get_style (ModestHeaderView *self)
844 g_return_val_if_fail (self, FALSE);
845 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
849 * This function sets a sortable model in the header view. It's just
850 * used for developing purposes, because it only does a
851 * gtk_tree_view_set_model
854 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
856 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
857 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
858 /* GtkTreeModel *old_model; */
859 /* ModestHeaderViewPrivate *priv; */
860 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
861 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
863 /* /\* Set new model *\/ */
864 /* gtk_tree_view_set_model (header_view, model); */
866 gtk_tree_view_set_model (header_view, model);
870 modest_header_view_get_folder (ModestHeaderView *self)
872 ModestHeaderViewPrivate *priv;
873 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
876 g_object_ref (priv->folder);
882 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
886 ModestHeaderViewPrivate *priv;
887 GList *cols, *cursor;
888 GtkTreeModel *filter_model, *sortable;
890 GtkSortType sort_type;
892 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
894 headers = TNY_LIST (tny_gtk_header_list_model_new ());
896 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
899 /* Add IDLE observer (monitor) and another folder observer for
900 new messages (self) */
901 g_mutex_lock (priv->observers_lock);
903 tny_folder_monitor_stop (priv->monitor);
904 g_object_unref (G_OBJECT (priv->monitor));
906 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
907 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
908 tny_folder_monitor_start (priv->monitor);
909 g_mutex_unlock (priv->observers_lock);
911 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
912 g_object_unref (G_OBJECT (headers));
914 /* Create a tree model filter to hide and show rows for cut operations */
915 filter_model = gtk_tree_model_filter_new (sortable, NULL);
916 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
920 g_object_unref (G_OBJECT (sortable));
922 /* install our special sorting functions */
923 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
925 /* Restore sort column id */
927 type = modest_tny_folder_guess_folder_type (folder);
928 sort_colid = modest_header_view_get_sort_column_id (self, type);
929 sort_type = modest_header_view_get_sort_type (self, type);
930 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
933 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
934 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
935 (GtkTreeIterCompareFunc) cmp_rows,
937 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
938 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
939 (GtkTreeIterCompareFunc) cmp_subject_rows,
944 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
945 g_object_unref (G_OBJECT (filter_model));
946 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
947 /* g_object_unref (G_OBJECT (sortable)); */
954 modest_header_view_sort_by_column_id (ModestHeaderView *self,
956 GtkSortType sort_type)
958 ModestHeaderViewPrivate *priv = NULL;
959 GtkTreeModel *tree_filter, *sortable = NULL;
962 /* Get model and private data */
963 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
964 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
965 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
966 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
968 /* Sort tree model */
969 type = modest_tny_folder_guess_folder_type (priv->folder);
970 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
973 /* Store new sort parameters */
974 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
976 /* Save GConf parameters */
977 /* modest_widget_memory_save (modest_runtime_get_conf(), */
978 /* G_OBJECT(self), "header-view"); */
983 modest_header_view_set_sort_params (ModestHeaderView *self,
985 GtkSortType sort_type,
988 ModestHeaderViewPrivate *priv;
989 ModestHeaderViewStyle style;
991 style = modest_header_view_get_style (self);
992 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
994 priv->sort_colid[style][type] = sort_colid;
995 priv->sort_type[style][type] = sort_type;
999 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1002 ModestHeaderViewPrivate *priv;
1003 ModestHeaderViewStyle style;
1005 style = modest_header_view_get_style (self);
1006 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1008 return priv->sort_colid[style][type];
1012 modest_header_view_get_sort_type (ModestHeaderView *self,
1015 ModestHeaderViewPrivate *priv;
1016 ModestHeaderViewStyle style;
1018 style = modest_header_view_get_style (self);
1019 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1021 return priv->sort_type[style][type];
1025 ModestHeaderView *header_view;
1026 RefreshAsyncUserCallback cb;
1031 folder_refreshed_cb (ModestMailOperation *mail_op,
1035 ModestHeaderViewPrivate *priv;
1036 SetFolderHelper *info;
1038 info = (SetFolderHelper*) user_data;
1040 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1044 info->cb (mail_op, folder, info->user_data);
1046 /* Start the folder count changes observer. We do not need it
1047 before the refresh. Note that the monitor could still be
1048 called for this refresh but now we know that the callback
1049 was previously called */
1050 g_mutex_lock (priv->observers_lock);
1051 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1052 g_mutex_unlock (priv->observers_lock);
1059 modest_header_view_set_folder (ModestHeaderView *self,
1061 RefreshAsyncUserCallback callback,
1064 ModestHeaderViewPrivate *priv;
1065 ModestWindowMgr *mgr = NULL;
1066 GObject *source = NULL;
1067 SetFolderHelper *info;
1069 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1072 g_mutex_lock (priv->observers_lock);
1073 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1074 g_object_unref (priv->folder);
1075 priv->folder = NULL;
1076 g_mutex_unlock (priv->observers_lock);
1080 ModestMailOperation *mail_op = NULL;
1081 GtkTreeSelection *selection;
1083 /* Get main window to use it as source of mail operation */
1084 mgr = modest_runtime_get_window_mgr ();
1085 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1087 /* Set folder in the model */
1088 modest_header_view_set_folder_intern (self, folder);
1090 /* Pick my reference. Nothing to do with the mail operation */
1091 priv->folder = g_object_ref (folder);
1093 /* no message selected */
1094 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1096 info = g_malloc0 (sizeof(SetFolderHelper));
1097 info->header_view = self;
1098 info->cb = callback;
1099 info->user_data = user_data;
1101 /* bug 57631: Clear the selection if exists */
1102 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1103 gtk_tree_selection_unselect_all(selection);
1105 /* Create the mail operation (source will be the parent widget) */
1106 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1107 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1110 /* Refresh the folder asynchronously */
1111 modest_mail_operation_refresh_folder (mail_op,
1113 folder_refreshed_cb,
1117 g_object_unref (mail_op);
1119 g_mutex_lock (priv->observers_lock);
1121 if (priv->monitor) {
1122 tny_folder_monitor_stop (priv->monitor);
1123 g_object_unref (G_OBJECT (priv->monitor));
1124 priv->monitor = NULL;
1126 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1128 g_mutex_unlock (priv->observers_lock);
1133 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1134 GtkTreeViewColumn *column, gpointer userdata)
1136 ModestHeaderView *self = NULL;
1137 ModestHeaderViewPrivate *priv = NULL;
1139 GtkTreeModel *model = NULL;
1140 TnyHeader *header = NULL;
1141 TnyHeaderFlags flags;
1143 self = MODEST_HEADER_VIEW (treeview);
1144 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1146 model = gtk_tree_view_get_model (treeview);
1147 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1150 /* get the first selected item */
1151 gtk_tree_model_get (model, &iter,
1152 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1153 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1156 /* Dont open DELETED messages */
1157 if (flags & TNY_HEADER_FLAG_DELETED) {
1158 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1163 g_signal_emit (G_OBJECT(self),
1164 signals[HEADER_ACTIVATED_SIGNAL],
1170 g_object_unref (G_OBJECT (header));
1175 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1177 GtkTreeModel *model;
1178 TnyHeader *header = NULL;
1179 GtkTreePath *path = NULL;
1181 ModestHeaderView *self;
1182 ModestHeaderViewPrivate *priv;
1183 GList *selected = NULL;
1185 g_return_if_fail (sel);
1186 g_return_if_fail (user_data);
1188 self = MODEST_HEADER_VIEW (user_data);
1189 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1191 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1192 if (selected != NULL)
1193 path = (GtkTreePath *) selected->data;
1194 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1195 return; /* msg was _un_selected */
1197 gtk_tree_model_get (model, &iter,
1198 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1202 g_signal_emit (G_OBJECT(self),
1203 signals[HEADER_SELECTED_SIGNAL],
1206 g_object_unref (G_OBJECT (header));
1208 /* free all items in 'selected' */
1209 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1210 g_list_free (selected);
1214 /* PROTECTED method. It's useful when we want to force a given
1215 selection to reload a msg. For example if we have selected a header
1216 in offline mode, when Modest become online, we want to reload the
1217 message automatically without an user click over the header */
1219 _modest_header_view_change_selection (GtkTreeSelection *selection,
1222 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1223 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1225 on_selection_changed (selection, user_data);
1228 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1230 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1231 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1233 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1235 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1240 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1247 /* static int counter = 0; */
1249 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1250 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1251 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1255 case TNY_HEADER_FLAG_ATTACHMENTS:
1257 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1258 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1259 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1260 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1262 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1263 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1265 return cmp ? cmp : t1 - t2;
1267 case TNY_HEADER_FLAG_PRIORITY:
1268 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1269 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1270 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1271 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1273 /* This is for making priority values respect the intuitive sort relationship
1274 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1275 cmp = compare_priorities (val1, val2);
1277 return cmp ? cmp : t1 - t2;
1280 return &iter1 - &iter2; /* oughhhh */
1285 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1291 /* static int counter = 0; */
1293 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1295 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1296 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1297 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1298 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1300 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1301 val2 + modest_text_utils_get_subject_prefix_len(val2),
1308 /* Drag and drop stuff */
1310 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1311 GtkSelectionData *selection_data,
1312 guint info, guint time, gpointer data)
1314 GtkTreeModel *model = NULL;
1316 GtkTreePath *source_row = NULL;
1317 GtkTreeSelection *sel = NULL;
1319 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1320 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1323 case MODEST_HEADER_ROW:
1324 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1327 TnyHeader *hdr = NULL;
1328 gtk_tree_model_get (model, &iter,
1329 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1332 g_object_unref (G_OBJECT(hdr));
1337 g_message ("%s: default switch case.", __FUNCTION__);
1340 /* Set focus on next header */
1341 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1342 gtk_tree_path_next (source_row);
1343 gtk_tree_selection_select_path (sel, source_row);
1345 gtk_tree_path_free (source_row);
1348 /* Header view drag types */
1349 const GtkTargetEntry header_view_drag_types[] = {
1350 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1351 { "text/uri-list", 0, MODEST_MSG },
1355 setup_drag_and_drop (GtkTreeView *self)
1357 gtk_drag_source_set (GTK_WIDGET (self),
1359 header_view_drag_types,
1360 G_N_ELEMENTS (header_view_drag_types),
1361 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1363 g_signal_connect(G_OBJECT (self), "drag_data_get",
1364 G_CALLBACK(drag_data_get_cb), NULL);
1367 static GtkTreePath *
1368 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1370 GtkTreePath *path = NULL;
1371 GtkTreeSelection *sel = NULL;
1374 sel = gtk_tree_view_get_selection(self);
1375 rows = gtk_tree_selection_get_selected_rows (sel, model);
1377 if ((rows == NULL) || (g_list_length(rows) != 1))
1380 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1385 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1392 * This function moves the tree view scroll to the current selected
1393 * row when the widget grabs the focus
1396 on_focus_in (GtkWidget *self,
1397 GdkEventFocus *event,
1400 GtkTreeSelection *selection;
1401 GtkTreeModel *model;
1402 GList *selected = NULL;
1403 GtkTreePath *selected_path = NULL;
1405 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1409 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1410 /* If none selected yet, pick the first one */
1411 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1415 /* Return if the model is empty */
1416 if (!gtk_tree_model_get_iter_first (model, &iter))
1419 path = gtk_tree_model_get_path (model, &iter);
1420 gtk_tree_selection_select_path (selection, path);
1421 gtk_tree_path_free (path);
1424 /* Need to get the all the rows because is selection multiple */
1425 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1426 if (selected == NULL) return FALSE;
1427 selected_path = (GtkTreePath *) selected->data;
1429 /* Check if we need to scroll */
1430 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1431 GtkTreePath *start_path = NULL;
1432 GtkTreePath *end_path = NULL;
1433 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1437 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1438 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1440 /* Scroll to first path */
1441 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1450 gtk_tree_path_free (start_path);
1452 gtk_tree_path_free (end_path);
1454 #endif /* GTK_CHECK_VERSION */
1457 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1458 g_list_free (selected);
1464 idle_notify_headers_count_changed_destroy (gpointer data)
1466 HeadersCountChangedHelper *helper = NULL;
1468 g_return_if_fail (data != NULL);
1469 helper = (HeadersCountChangedHelper *) data;
1471 g_object_unref (helper->change);
1472 g_slice_free (HeadersCountChangedHelper, helper);
1476 idle_notify_headers_count_changed (gpointer data)
1478 TnyFolder *folder = NULL;
1479 ModestHeaderViewPrivate *priv = NULL;
1480 HeadersCountChangedHelper *helper = NULL;
1482 g_return_val_if_fail (data != NULL, FALSE);
1483 helper = (HeadersCountChangedHelper *) data;
1484 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1485 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1487 folder = tny_folder_change_get_folder (helper->change);
1489 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1491 g_mutex_lock (priv->observers_lock);
1493 /* Emit signal to evaluate how headers changes affects to the window view */
1494 g_signal_emit (G_OBJECT(helper->self),
1495 signals[MSG_COUNT_CHANGED_SIGNAL],
1496 0, folder, helper->change);
1498 /* Added or removed headers, so data stored on cliboard are invalid */
1499 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1500 modest_email_clipboard_clear (priv->clipboard);
1502 g_mutex_unlock (priv->observers_lock);
1508 folder_monitor_update (TnyFolderObserver *self,
1509 TnyFolderChange *change)
1511 ModestHeaderViewPrivate *priv;
1512 TnyFolderChangeChanged changed;
1513 HeadersCountChangedHelper *helper = NULL;
1515 changed = tny_folder_change_get_changed (change);
1517 /* Do not notify the observers if the folder of the header
1518 view has changed before this call to the observer
1520 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1521 if (tny_folder_change_get_folder (change) != priv->folder)
1524 /* Check header removed (hide marked as DELETED headers) */
1525 if (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS) {
1526 modest_header_view_refilter (MODEST_HEADER_VIEW(self));
1529 /* Check folder count */
1530 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1531 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1532 helper = g_slice_new0 (HeadersCountChangedHelper);
1533 helper->self = MODEST_HEADER_VIEW(self);
1534 helper->change = g_object_ref(change);
1536 g_idle_add_full (G_PRIORITY_DEFAULT,
1537 idle_notify_headers_count_changed,
1539 idle_notify_headers_count_changed_destroy);
1544 modest_header_view_clear (ModestHeaderView *self)
1546 modest_header_view_set_folder (self, NULL, NULL, NULL);
1550 modest_header_view_copy_selection (ModestHeaderView *header_view)
1552 /* Copy selection */
1553 _clipboard_set_selected_data (header_view, FALSE);
1557 modest_header_view_cut_selection (ModestHeaderView *header_view)
1559 ModestHeaderViewPrivate *priv = NULL;
1560 const gchar **hidding = NULL;
1561 guint i, n_selected;
1563 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1564 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1566 /* Copy selection */
1567 _clipboard_set_selected_data (header_view, TRUE);
1569 /* Get hidding ids */
1570 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1572 /* Clear hidding array created by previous cut operation */
1573 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1575 /* Copy hidding array */
1576 priv->n_selected = n_selected;
1577 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1578 for (i=0; i < n_selected; i++)
1579 priv->hidding_ids[i] = g_strdup(hidding[i]);
1581 /* Hide cut headers */
1582 modest_header_view_refilter (header_view);
1589 _clipboard_set_selected_data (ModestHeaderView *header_view,
1592 ModestHeaderViewPrivate *priv = NULL;
1593 TnyList *headers = NULL;
1595 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1596 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1598 /* Set selected data on clipboard */
1599 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1600 headers = modest_header_view_get_selected_headers (header_view);
1601 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1604 g_object_unref (headers);
1610 filter_row (GtkTreeModel *model,
1614 ModestHeaderViewPrivate *priv = NULL;
1615 TnyHeaderFlags flags;
1616 TnyHeader *header = NULL;
1619 gboolean visible = TRUE;
1620 gboolean found = FALSE;
1622 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1623 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1625 /* Get header from model */
1626 gtk_tree_model_get (model, iter,
1627 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1628 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1631 /* Hide mark as deleted heders */
1632 if (flags & TNY_HEADER_FLAG_DELETED) {
1637 /* If no data on clipboard, return always TRUE */
1638 if (modest_email_clipboard_cleared(priv->clipboard)) {
1643 /* Get message id from header (ensure is a valid id) */
1644 if (!header) return FALSE;
1645 id = g_strdup(tny_header_get_message_id (header));
1648 if (priv->hidding_ids != NULL) {
1649 for (i=0; i < priv->n_selected && !found; i++)
1650 if (priv->hidding_ids[i] != NULL && id != NULL)
1651 found = (!strcmp (priv->hidding_ids[i], id));
1659 g_object_unref (header);
1666 _clear_hidding_filter (ModestHeaderView *header_view)
1668 ModestHeaderViewPrivate *priv;
1671 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1672 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1674 if (priv->hidding_ids != NULL) {
1675 for (i=0; i < priv->n_selected; i++)
1676 g_free (priv->hidding_ids[i]);
1677 g_free(priv->hidding_ids);
1682 modest_header_view_refilter (ModestHeaderView *header_view)
1684 GtkTreeModel *model;
1686 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1688 /* Hide cut headers */
1689 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1690 if (GTK_IS_TREE_MODEL_FILTER (model))
1691 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1695 * Called when an account is removed. If I'm showing a folder of the
1696 * account that has been removed then clear the view
1699 on_account_removed (TnyAccountStore *self,
1700 TnyAccount *account,
1703 ModestHeaderViewPrivate *priv = NULL;
1705 /* Ignore changes in transport accounts */
1706 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1709 g_print ("--------------------- HEADER ---------------\n");
1711 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1714 TnyAccount *my_account;
1716 my_account = tny_folder_get_account (priv->folder);
1717 if (my_account == account)
1718 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1719 g_object_unref (account);