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;
142 typedef struct _HeadersCountChangedHelper HeadersCountChangedHelper;
143 struct _HeadersCountChangedHelper {
144 ModestHeaderView *self;
145 TnyFolderChange *change;
149 #define MODEST_HEADER_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
150 MODEST_TYPE_HEADER_VIEW, \
151 ModestHeaderViewPrivate))
155 #define MODEST_HEADER_VIEW_PTR "modest-header-view"
158 HEADER_SELECTED_SIGNAL,
159 HEADER_ACTIVATED_SIGNAL,
160 ITEM_NOT_FOUND_SIGNAL,
161 MSG_COUNT_CHANGED_SIGNAL,
166 static GObjectClass *parent_class = NULL;
168 /* uncomment the following if you have defined any signals */
169 static guint signals[LAST_SIGNAL] = {0};
172 modest_header_view_get_type (void)
174 static GType my_type = 0;
176 static const GTypeInfo my_info = {
177 sizeof(ModestHeaderViewClass),
178 NULL, /* base init */
179 NULL, /* base finalize */
180 (GClassInitFunc) modest_header_view_class_init,
181 NULL, /* class finalize */
182 NULL, /* class data */
183 sizeof(ModestHeaderView),
185 (GInstanceInitFunc) modest_header_view_init,
189 static const GInterfaceInfo tny_folder_observer_info =
191 (GInterfaceInitFunc) tny_folder_observer_init, /* interface_init */
192 NULL, /* interface_finalize */
193 NULL /* interface_data */
195 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
199 g_type_add_interface_static (my_type, TNY_TYPE_FOLDER_OBSERVER,
200 &tny_folder_observer_info);
208 modest_header_view_class_init (ModestHeaderViewClass *klass)
210 GObjectClass *gobject_class;
211 gobject_class = (GObjectClass*) klass;
213 parent_class = g_type_class_peek_parent (klass);
214 gobject_class->finalize = modest_header_view_finalize;
215 gobject_class->dispose = modest_header_view_dispose;
217 g_type_class_add_private (gobject_class, sizeof(ModestHeaderViewPrivate));
219 signals[HEADER_SELECTED_SIGNAL] =
220 g_signal_new ("header_selected",
221 G_TYPE_FROM_CLASS (gobject_class),
223 G_STRUCT_OFFSET (ModestHeaderViewClass,header_selected),
225 g_cclosure_marshal_VOID__POINTER,
226 G_TYPE_NONE, 1, G_TYPE_POINTER);
228 signals[HEADER_ACTIVATED_SIGNAL] =
229 g_signal_new ("header_activated",
230 G_TYPE_FROM_CLASS (gobject_class),
232 G_STRUCT_OFFSET (ModestHeaderViewClass,header_activated),
234 g_cclosure_marshal_VOID__POINTER,
235 G_TYPE_NONE, 1, G_TYPE_POINTER);
238 signals[ITEM_NOT_FOUND_SIGNAL] =
239 g_signal_new ("item_not_found",
240 G_TYPE_FROM_CLASS (gobject_class),
242 G_STRUCT_OFFSET (ModestHeaderViewClass,item_not_found),
244 g_cclosure_marshal_VOID__INT,
245 G_TYPE_NONE, 1, G_TYPE_INT);
247 signals[MSG_COUNT_CHANGED_SIGNAL] =
248 g_signal_new ("msg_count_changed",
249 G_TYPE_FROM_CLASS (gobject_class),
251 G_STRUCT_OFFSET (ModestHeaderViewClass, msg_count_changed),
253 modest_marshal_VOID__POINTER_POINTER,
254 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
258 tny_folder_observer_init (TnyFolderObserverIface *klass)
260 klass->update_func = folder_monitor_update;
263 static GtkTreeViewColumn*
264 get_new_column (const gchar *name, GtkCellRenderer *renderer,
265 gboolean resizable, gint sort_col_id, gboolean show_as_text,
266 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
268 GtkTreeViewColumn *column;
270 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
271 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
273 gtk_tree_view_column_set_resizable (column, resizable);
275 gtk_tree_view_column_set_expand (column, TRUE);
278 gtk_tree_view_column_add_attribute (column, renderer, "text",
280 if (sort_col_id >= 0)
281 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
283 gtk_tree_view_column_set_sort_indicator (column, FALSE);
284 gtk_tree_view_column_set_reorderable (column, TRUE);
287 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
294 remove_all_columns (ModestHeaderView *obj)
296 GList *columns, *cursor;
298 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW(obj));
300 for (cursor = columns; cursor; cursor = cursor->next)
301 gtk_tree_view_remove_column (GTK_TREE_VIEW(obj),
302 GTK_TREE_VIEW_COLUMN(cursor->data));
303 g_list_free (columns);
307 modest_header_view_set_columns (ModestHeaderView *self, const GList *columns, TnyFolderType type)
309 GtkTreeModel *tree_filter, *sortable;
310 GtkTreeViewColumn *column=NULL;
311 GtkTreeSelection *selection = NULL;
312 GtkCellRenderer *renderer_msgtype,*renderer_header,
313 *renderer_attach, *renderer_compact_date_or_status;
314 GtkCellRenderer *renderer_compact_header, *renderer_recpt_box,
315 *renderer_subject, *renderer_subject_box, *renderer_recpt,
317 ModestHeaderViewPrivate *priv;
318 GtkTreeViewColumn *compact_column = NULL;
321 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
323 /* FIXME: check whether these renderers need to be freed */
324 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
325 renderer_attach = gtk_cell_renderer_pixbuf_new ();
326 renderer_priority = gtk_cell_renderer_pixbuf_new ();
327 renderer_header = gtk_cell_renderer_text_new ();
329 renderer_compact_header = modest_vbox_cell_renderer_new ();
330 renderer_recpt_box = modest_hbox_cell_renderer_new ();
331 renderer_subject_box = modest_hbox_cell_renderer_new ();
332 renderer_recpt = gtk_cell_renderer_text_new ();
333 renderer_subject = gtk_cell_renderer_text_new ();
334 renderer_compact_date_or_status = gtk_cell_renderer_text_new ();
336 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_subject_box, FALSE);
337 g_object_set_data (G_OBJECT (renderer_compact_header), "subject-box-renderer", renderer_subject_box);
338 modest_vbox_cell_renderer_append (MODEST_VBOX_CELL_RENDERER (renderer_compact_header), renderer_recpt_box, FALSE);
339 g_object_set_data (G_OBJECT (renderer_compact_header), "recpt-box-renderer", renderer_recpt_box);
340 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_priority, FALSE);
341 g_object_set_data (G_OBJECT (renderer_subject_box), "priority-renderer", renderer_priority);
342 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_subject_box), renderer_subject, TRUE);
343 g_object_set_data (G_OBJECT (renderer_subject_box), "subject-renderer", renderer_subject);
344 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_attach, FALSE);
345 g_object_set_data (G_OBJECT (renderer_recpt_box), "attach-renderer", renderer_attach);
346 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_recpt, TRUE);
347 g_object_set_data (G_OBJECT (renderer_recpt_box), "recipient-renderer", renderer_recpt);
348 modest_hbox_cell_renderer_append (MODEST_HBOX_CELL_RENDERER (renderer_recpt_box), renderer_compact_date_or_status, FALSE);
349 g_object_set_data (G_OBJECT (renderer_recpt_box), "date-renderer", renderer_compact_date_or_status);
351 g_object_set (G_OBJECT (renderer_subject_box), "yalign", 1.0, NULL);
352 gtk_cell_renderer_set_fixed_size (renderer_subject_box, -1, 32);
353 gtk_cell_renderer_set_fixed_size (renderer_recpt_box, -1, 32);
354 g_object_set (G_OBJECT (renderer_recpt_box), "yalign", 0.0, NULL);
355 g_object_set(G_OBJECT(renderer_header),
356 "ellipsize", PANGO_ELLIPSIZE_END,
358 g_object_set (G_OBJECT (renderer_subject),
359 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 1.0,
361 g_object_set (G_OBJECT (renderer_recpt),
362 "ellipsize", PANGO_ELLIPSIZE_END, "yalign", 0.0,
364 g_object_set(G_OBJECT(renderer_compact_date_or_status),
365 "xalign", 1.0, "yalign", 0.0,
367 g_object_set (G_OBJECT (renderer_priority),
368 "yalign", 1.0, NULL);
369 g_object_set (G_OBJECT (renderer_attach),
370 "yalign", 0.0, NULL);
372 gtk_cell_renderer_set_fixed_size (renderer_attach, 32, 26);
373 gtk_cell_renderer_set_fixed_size (renderer_priority, 32, 26);
374 gtk_cell_renderer_set_fixed_size (renderer_compact_header, -1, 64);
376 remove_all_columns (self);
378 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
379 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
380 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
381 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
383 /* Add new columns */
384 for (cursor = columns; cursor; cursor = g_list_next(cursor)) {
385 ModestHeaderViewColumn col =
386 (ModestHeaderViewColumn) GPOINTER_TO_INT(cursor->data);
388 if (0> col || col >= MODEST_HEADER_VIEW_COLUMN_NUM) {
389 g_printerr ("modest: invalid column %d in column list\n", col);
395 case MODEST_HEADER_VIEW_COLUMN_MSGTYPE:
396 column = get_new_column (_("M"), renderer_msgtype, FALSE,
397 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
399 (GtkTreeCellDataFunc)_modest_header_view_msgtype_cell_data,
401 gtk_tree_view_column_set_fixed_width (column, 45);
404 case MODEST_HEADER_VIEW_COLUMN_ATTACH:
405 column = get_new_column (_("A"), renderer_attach, FALSE,
406 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
408 (GtkTreeCellDataFunc)_modest_header_view_attach_cell_data,
410 gtk_tree_view_column_set_fixed_width (column, 45);
414 case MODEST_HEADER_VIEW_COLUMN_FROM:
415 column = get_new_column (_("From"), renderer_header, TRUE,
416 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
418 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
419 GINT_TO_POINTER(TRUE));
422 case MODEST_HEADER_VIEW_COLUMN_TO:
423 column = get_new_column (_("To"), renderer_header, TRUE,
424 TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,
426 (GtkTreeCellDataFunc)_modest_header_view_sender_receiver_cell_data,
427 GINT_TO_POINTER(FALSE));
430 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN:
431 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
432 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
434 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
435 GINT_TO_POINTER(MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN));
436 compact_column = column;
439 case MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT:
440 column = get_new_column (_("Header"), renderer_compact_header, TRUE,
441 TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,
443 (GtkTreeCellDataFunc)_modest_header_view_compact_header_cell_data,
444 GINT_TO_POINTER((type == TNY_FOLDER_TYPE_OUTBOX)?
445 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX:
446 MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUT));
447 compact_column = column;
451 case MODEST_HEADER_VIEW_COLUMN_SUBJECT:
452 column = get_new_column (_("Subject"), renderer_header, TRUE,
453 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
455 (GtkTreeCellDataFunc)_modest_header_view_header_cell_data,
459 case MODEST_HEADER_VIEW_COLUMN_RECEIVED_DATE:
460 column = get_new_column (_("Received"), renderer_header, TRUE,
461 TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN,
463 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
464 GINT_TO_POINTER(TRUE));
467 case MODEST_HEADER_VIEW_COLUMN_SENT_DATE:
468 column = get_new_column (_("Sent"), renderer_header, TRUE,
469 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN,
471 (GtkTreeCellDataFunc)_modest_header_view_date_cell_data,
472 GINT_TO_POINTER(FALSE));
475 case MODEST_HEADER_VIEW_COLUMN_SIZE:
476 column = get_new_column (_("Size"), renderer_header, TRUE,
477 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
479 (GtkTreeCellDataFunc)_modest_header_view_size_cell_data,
482 case MODEST_HEADER_VIEW_COLUMN_STATUS:
483 column = get_new_column (_("Status"), renderer_compact_date_or_status, TRUE,
484 TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN,
486 (GtkTreeCellDataFunc)_modest_header_view_status_cell_data,
491 g_return_val_if_reached(FALSE);
494 /* we keep the column id around */
495 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_COLUMN,
496 GINT_TO_POINTER(col));
498 /* we need this ptr when sorting the rows */
499 g_object_set_data (G_OBJECT(column), MODEST_HEADER_VIEW_PTR,
501 gtk_tree_view_append_column (GTK_TREE_VIEW(self), column);
505 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
506 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
507 (GtkTreeIterCompareFunc) cmp_rows,
508 compact_column, NULL);
509 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sortable),
510 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
511 (GtkTreeIterCompareFunc) cmp_subject_rows,
512 compact_column, NULL);
520 modest_header_view_init (ModestHeaderView *obj)
522 ModestHeaderViewPrivate *priv;
525 priv = MODEST_HEADER_VIEW_GET_PRIVATE(obj);
529 priv->monitor = NULL;
530 priv->observers_lock = g_mutex_new ();
532 priv->status = HEADER_VIEW_INIT;
534 priv->observer_list_lock = g_mutex_new();
535 priv->observer_list = NULL;
537 priv->clipboard = modest_runtime_get_email_clipboard ();
538 priv->hidding_ids = NULL;
539 priv->n_selected = 0;
540 priv->selection_changed_handler = 0;
541 priv->acc_removed_handler = 0;
543 /* Sort parameters */
544 for (j=0; j < 2; j++) {
545 for (i=0; i < TNY_FOLDER_TYPE_NUM; i++) {
546 priv->sort_colid[j][i] = -1;
547 priv->sort_type[j][i] = GTK_SORT_DESCENDING;
551 setup_drag_and_drop (GTK_TREE_VIEW (obj));
555 modest_header_view_dispose (GObject *obj)
557 ModestHeaderView *self;
558 ModestHeaderViewPrivate *priv;
559 GtkTreeSelection *sel;
561 self = MODEST_HEADER_VIEW(obj);
562 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
564 /* Free in the dispose to avoid unref cycles */
566 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (obj));
567 g_object_unref (G_OBJECT (priv->folder));
571 /* We need to do this here in the dispose because the
572 selection won't exist when finalizing */
573 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(self));
574 if (sel && g_signal_handler_is_connected (sel, priv->selection_changed_handler)) {
575 g_signal_handler_disconnect (sel, priv->selection_changed_handler);
576 priv->selection_changed_handler = 0;
579 G_OBJECT_CLASS(parent_class)->dispose (obj);
583 modest_header_view_finalize (GObject *obj)
585 ModestHeaderView *self;
586 ModestHeaderViewPrivate *priv;
588 self = MODEST_HEADER_VIEW(obj);
589 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
591 if (g_signal_handler_is_connected (modest_runtime_get_account_store (),
592 priv->acc_removed_handler)) {
593 g_signal_handler_disconnect (modest_runtime_get_account_store (),
594 priv->acc_removed_handler);
597 /* There is no need to lock because there should not be any
598 * reference to self now. */
599 g_mutex_free(priv->observer_list_lock);
600 g_slist_free(priv->observer_list);
602 g_mutex_lock (priv->observers_lock);
604 tny_folder_monitor_stop (priv->monitor);
605 g_object_unref (G_OBJECT (priv->monitor));
607 g_mutex_unlock (priv->observers_lock);
608 g_mutex_free (priv->observers_lock);
610 /* Clear hidding array created by cut operation */
611 _clear_hidding_filter (MODEST_HEADER_VIEW (obj));
613 G_OBJECT_CLASS(parent_class)->finalize (obj);
618 modest_header_view_new (TnyFolder *folder, ModestHeaderViewStyle style)
621 GtkTreeSelection *sel;
622 ModestHeaderView *self;
623 ModestHeaderViewPrivate *priv;
625 g_return_val_if_fail (style >= 0 && style < MODEST_HEADER_VIEW_STYLE_NUM,
628 obj = G_OBJECT(g_object_new(MODEST_TYPE_HEADER_VIEW, NULL));
629 self = MODEST_HEADER_VIEW(obj);
630 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
632 modest_header_view_set_style (self, style);
633 /* modest_header_view_set_folder (self, NULL, NULL, NULL); */
635 gtk_tree_view_columns_autosize (GTK_TREE_VIEW(obj));
636 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW(obj),TRUE);
637 gtk_tree_view_set_enable_search (GTK_TREE_VIEW(obj), TRUE);
639 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj),
640 TRUE); /* alternating row colors */
642 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
643 priv->selection_changed_handler =
644 g_signal_connect_after (sel, "changed",
645 G_CALLBACK(on_selection_changed), self);
647 g_signal_connect (self, "row-activated",
648 G_CALLBACK (on_header_row_activated), NULL);
650 g_signal_connect (self, "focus-in-event",
651 G_CALLBACK(on_focus_in), NULL);
653 priv->acc_removed_handler = g_signal_connect (modest_runtime_get_account_store (),
655 G_CALLBACK (on_account_removed),
658 g_signal_connect (self, "expose-event",
659 G_CALLBACK(modest_header_view_on_expose_event),
662 return GTK_WIDGET(self);
667 modest_header_view_count_selected_headers (ModestHeaderView *self)
669 GtkTreeSelection *sel;
672 g_return_val_if_fail (self, 0);
674 /* Get selection object and check selected rows count */
675 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
676 selected_rows = gtk_tree_selection_count_selected_rows (sel);
678 return selected_rows;
682 modest_header_view_has_selected_headers (ModestHeaderView *self)
684 GtkTreeSelection *sel;
687 g_return_val_if_fail (self, FALSE);
689 /* Get selection object and check selected rows count */
690 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
691 empty = gtk_tree_selection_count_selected_rows (sel) == 0;
698 modest_header_view_get_selected_headers (ModestHeaderView *self)
700 GtkTreeSelection *sel;
701 ModestHeaderViewPrivate *priv;
702 TnyList *header_list = NULL;
704 GList *list, *tmp = NULL;
705 GtkTreeModel *tree_model = NULL;
708 g_return_val_if_fail (self, NULL);
710 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
712 /* Get selected rows */
713 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
714 list = gtk_tree_selection_get_selected_rows (sel, &tree_model);
717 header_list = tny_simple_list_new();
719 list = g_list_reverse (list);
722 /* get header from selection */
723 gtk_tree_model_get_iter (tree_model, &iter, (GtkTreePath *) (tmp->data));
724 gtk_tree_model_get (tree_model, &iter,
725 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
727 /* Prepend to list */
728 tny_list_prepend (header_list, G_OBJECT (header));
729 g_object_unref (G_OBJECT (header));
731 tmp = g_list_next (tmp);
734 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
741 /* scroll our list view so the selected item is visible */
743 scroll_to_selected (ModestHeaderView *self, GtkTreeIter *iter, gboolean up)
745 #ifdef MODEST_PLATFORM_GNOME
747 GtkTreePath *selected_path;
748 GtkTreePath *start, *end;
752 model = gtk_tree_view_get_model (GTK_TREE_VIEW(self));
753 selected_path = gtk_tree_model_get_path (model, iter);
755 start = gtk_tree_path_new ();
756 end = gtk_tree_path_new ();
758 gtk_tree_view_get_visible_range (GTK_TREE_VIEW(self), &start, &end);
760 if (gtk_tree_path_compare (selected_path, start) < 0 ||
761 gtk_tree_path_compare (end, selected_path) < 0)
762 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(self),
763 selected_path, NULL, TRUE,
766 gtk_tree_path_free (selected_path);
767 gtk_tree_path_free (start);
768 gtk_tree_path_free (end);
770 #endif /* MODEST_PLATFORM_GNOME */
775 modest_header_view_select_next (ModestHeaderView *self)
777 GtkTreeSelection *sel;
782 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
783 path = get_selected_row (GTK_TREE_VIEW(self), &model);
784 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
785 /* Unselect previous path */
786 gtk_tree_selection_unselect_path (sel, path);
788 /* Move path down and selects new one */
789 if (gtk_tree_model_iter_next (model, &iter)) {
790 gtk_tree_selection_select_iter (sel, &iter);
791 scroll_to_selected (self, &iter, FALSE);
793 gtk_tree_path_free(path);
799 modest_header_view_select_prev (ModestHeaderView *self)
801 GtkTreeSelection *sel;
806 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
807 path = get_selected_row (GTK_TREE_VIEW(self), &model);
808 if ((path != NULL) && (gtk_tree_model_get_iter(model, &iter, path))) {
809 /* Unselect previous path */
810 gtk_tree_selection_unselect_path (sel, path);
813 if (gtk_tree_path_prev (path)) {
814 gtk_tree_model_get_iter (model, &iter, path);
816 /* Select the new one */
817 gtk_tree_selection_select_iter (sel, &iter);
818 scroll_to_selected (self, &iter, TRUE);
821 gtk_tree_path_free (path);
826 modest_header_view_get_columns (ModestHeaderView *self)
828 g_return_val_if_fail (self, FALSE);
829 return gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
835 modest_header_view_set_style (ModestHeaderView *self,
836 ModestHeaderViewStyle style)
838 ModestHeaderViewPrivate *priv;
839 gboolean show_col_headers = FALSE;
840 ModestHeaderViewStyle old_style;
842 g_return_val_if_fail (self, FALSE);
843 g_return_val_if_fail (style >= 0 && MODEST_HEADER_VIEW_STYLE_NUM,
846 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
847 if (priv->style == style)
848 return TRUE; /* nothing to do */
851 case MODEST_HEADER_VIEW_STYLE_DETAILS:
852 show_col_headers = TRUE;
854 case MODEST_HEADER_VIEW_STYLE_TWOLINES:
857 g_return_val_if_reached (FALSE);
859 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(self), show_col_headers);
860 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), show_col_headers);
862 old_style = priv->style;
869 ModestHeaderViewStyle
870 modest_header_view_get_style (ModestHeaderView *self)
872 g_return_val_if_fail (self, FALSE);
873 return MODEST_HEADER_VIEW_GET_PRIVATE(self)->style;
876 /* This is used to automatically select the first header if the user
877 * has not selected any header yet.
880 modest_header_view_on_expose_event(GtkTreeView *header_view,
881 GdkEventExpose *event,
884 GtkTreeSelection *sel;
886 GtkTreeIter tree_iter;
888 /* I'm invalidating this method because it causes an annoying
889 efect, the focus changes to the header view when selecting
890 a folder in the folder view because of this code and it
891 shouldn't. We need to find another way to set the passive
892 focus on it. Sergio. */
895 model = gtk_tree_view_get_model(header_view);
897 sel = gtk_tree_view_get_selection(header_view);
898 if(!gtk_tree_selection_count_selected_rows(sel))
899 if (gtk_tree_model_get_iter_first(model, &tree_iter))
900 gtk_tree_selection_select_iter(sel, &tree_iter);
906 * This function sets a sortable model in the header view. It's just
907 * used for developing purposes, because it only does a
908 * gtk_tree_view_set_model
911 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
913 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
914 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
915 /* GtkTreeModel *old_model; */
916 /* ModestHeaderViewPrivate *priv; */
917 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
918 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
920 /* /\* Set new model *\/ */
921 /* gtk_tree_view_set_model (header_view, model); */
923 gtk_tree_view_set_model (header_view, model);
927 modest_header_view_get_folder (ModestHeaderView *self)
929 ModestHeaderViewPrivate *priv;
930 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
933 g_object_ref (priv->folder);
939 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
943 ModestHeaderViewPrivate *priv;
944 GList *cols, *cursor;
945 GtkTreeModel *filter_model, *sortable;
947 GtkSortType sort_type;
949 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
951 headers = TNY_LIST (tny_gtk_header_list_model_new ());
953 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
956 /* Add IDLE observer (monitor) and another folder observer for
957 new messages (self) */
958 g_mutex_lock (priv->observers_lock);
960 tny_folder_monitor_stop (priv->monitor);
961 g_object_unref (G_OBJECT (priv->monitor));
963 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
964 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
965 tny_folder_monitor_start (priv->monitor);
966 g_mutex_unlock (priv->observers_lock);
968 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
969 g_object_unref (G_OBJECT (headers));
971 /* Init filter_row function to examine empty status */
972 priv->status = HEADER_VIEW_INIT;
974 /* Create a tree model filter to hide and show rows for cut operations */
975 filter_model = gtk_tree_model_filter_new (sortable, NULL);
976 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
980 g_object_unref (G_OBJECT (sortable));
982 /* install our special sorting functions */
983 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
985 /* Restore sort column id */
987 type = modest_tny_folder_guess_folder_type (folder);
988 sort_colid = modest_header_view_get_sort_column_id (self, type);
989 sort_type = modest_header_view_get_sort_type (self, type);
990 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
993 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
994 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
995 (GtkTreeIterCompareFunc) cmp_rows,
997 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
998 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
999 (GtkTreeIterCompareFunc) cmp_subject_rows,
1004 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
1005 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
1006 tny_folder_get_id(folder));
1007 g_object_unref (G_OBJECT (filter_model));
1008 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1009 /* g_object_unref (G_OBJECT (sortable)); */
1016 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1018 GtkSortType sort_type)
1020 ModestHeaderViewPrivate *priv = NULL;
1021 GtkTreeModel *tree_filter, *sortable = NULL;
1024 /* Get model and private data */
1025 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1026 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1027 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1028 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1030 /* Sort tree model */
1031 type = modest_tny_folder_guess_folder_type (priv->folder);
1032 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1035 /* Store new sort parameters */
1036 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1038 /* Save GConf parameters */
1039 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1040 /* G_OBJECT(self), "header-view"); */
1045 modest_header_view_set_sort_params (ModestHeaderView *self,
1047 GtkSortType sort_type,
1050 ModestHeaderViewPrivate *priv;
1051 ModestHeaderViewStyle style;
1053 style = modest_header_view_get_style (self);
1054 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1056 priv->sort_colid[style][type] = sort_colid;
1057 priv->sort_type[style][type] = sort_type;
1061 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1064 ModestHeaderViewPrivate *priv;
1065 ModestHeaderViewStyle style;
1067 style = modest_header_view_get_style (self);
1068 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1070 return priv->sort_colid[style][type];
1074 modest_header_view_get_sort_type (ModestHeaderView *self,
1077 ModestHeaderViewPrivate *priv;
1078 ModestHeaderViewStyle style;
1080 style = modest_header_view_get_style (self);
1081 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1083 return priv->sort_type[style][type];
1087 ModestHeaderView *header_view;
1088 RefreshAsyncUserCallback cb;
1093 folder_refreshed_cb (ModestMailOperation *mail_op,
1097 ModestHeaderViewPrivate *priv;
1098 SetFolderHelper *info;
1100 info = (SetFolderHelper*) user_data;
1102 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1106 info->cb (mail_op, folder, info->user_data);
1108 /* Start the folder count changes observer. We do not need it
1109 before the refresh. Note that the monitor could still be
1110 called for this refresh but now we know that the callback
1111 was previously called */
1112 g_mutex_lock (priv->observers_lock);
1113 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1114 g_mutex_unlock (priv->observers_lock);
1121 modest_header_view_set_folder (ModestHeaderView *self,
1123 RefreshAsyncUserCallback callback,
1126 ModestHeaderViewPrivate *priv;
1127 ModestWindowMgr *mgr = NULL;
1128 GObject *source = NULL;
1129 SetFolderHelper *info;
1131 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1134 g_mutex_lock (priv->observers_lock);
1135 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1136 g_object_unref (priv->folder);
1137 priv->folder = NULL;
1138 g_mutex_unlock (priv->observers_lock);
1142 ModestMailOperation *mail_op = NULL;
1143 GtkTreeSelection *selection;
1145 /* Get main window to use it as source of mail operation */
1146 mgr = modest_runtime_get_window_mgr ();
1147 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1149 /* Set folder in the model */
1150 modest_header_view_set_folder_intern (self, folder);
1152 /* Pick my reference. Nothing to do with the mail operation */
1153 priv->folder = g_object_ref (folder);
1155 /* Clear the selection if exists */
1156 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1157 gtk_tree_selection_unselect_all(selection);
1158 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1160 /* create the helper */
1161 info = g_malloc0 (sizeof(SetFolderHelper));
1162 info->header_view = self;
1163 info->cb = callback;
1164 info->user_data = user_data;
1166 /* Create the mail operation (source will be the parent widget) */
1167 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1168 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1171 /* Refresh the folder asynchronously */
1172 modest_mail_operation_refresh_folder (mail_op,
1174 folder_refreshed_cb,
1178 g_object_unref (mail_op);
1180 g_mutex_lock (priv->observers_lock);
1182 if (priv->monitor) {
1183 tny_folder_monitor_stop (priv->monitor);
1184 g_object_unref (G_OBJECT (priv->monitor));
1185 priv->monitor = NULL;
1187 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1189 modest_header_view_notify_observers(self, NULL, NULL);
1191 g_mutex_unlock (priv->observers_lock);
1196 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1197 GtkTreeViewColumn *column, gpointer userdata)
1199 ModestHeaderView *self = NULL;
1200 ModestHeaderViewPrivate *priv = NULL;
1202 GtkTreeModel *model = NULL;
1203 TnyHeader *header = NULL;
1204 TnyHeaderFlags flags;
1206 self = MODEST_HEADER_VIEW (treeview);
1207 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1209 model = gtk_tree_view_get_model (treeview);
1210 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1213 /* get the first selected item */
1214 gtk_tree_model_get (model, &iter,
1215 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1216 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1219 /* Dont open DELETED messages */
1220 if (flags & TNY_HEADER_FLAG_DELETED) {
1221 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1226 g_signal_emit (G_OBJECT(self),
1227 signals[HEADER_ACTIVATED_SIGNAL],
1233 g_object_unref (G_OBJECT (header));
1238 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1240 GtkTreeModel *model;
1241 TnyHeader *header = NULL;
1242 GtkTreePath *path = NULL;
1244 ModestHeaderView *self;
1245 ModestHeaderViewPrivate *priv;
1246 GList *selected = NULL;
1248 g_return_if_fail (sel);
1249 g_return_if_fail (user_data);
1251 self = MODEST_HEADER_VIEW (user_data);
1252 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1254 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1255 if (selected != NULL)
1256 path = (GtkTreePath *) selected->data;
1257 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1258 return; /* msg was _un_selected */
1260 gtk_tree_model_get (model, &iter,
1261 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1265 g_signal_emit (G_OBJECT(self),
1266 signals[HEADER_SELECTED_SIGNAL],
1269 g_object_unref (G_OBJECT (header));
1271 /* free all items in 'selected' */
1272 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1273 g_list_free (selected);
1277 /* PROTECTED method. It's useful when we want to force a given
1278 selection to reload a msg. For example if we have selected a header
1279 in offline mode, when Modest become online, we want to reload the
1280 message automatically without an user click over the header */
1282 _modest_header_view_change_selection (GtkTreeSelection *selection,
1285 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1286 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1288 on_selection_changed (selection, user_data);
1291 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1293 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1294 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1296 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1298 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1303 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1310 /* static int counter = 0; */
1312 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1313 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1314 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1318 case TNY_HEADER_FLAG_ATTACHMENTS:
1320 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1321 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1322 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1323 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1325 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1326 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1328 return cmp ? cmp : t1 - t2;
1330 case TNY_HEADER_FLAG_PRIORITY:
1331 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1332 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1333 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1334 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1336 /* This is for making priority values respect the intuitive sort relationship
1337 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1338 cmp = compare_priorities (val1, val2);
1340 return cmp ? cmp : t1 - t2;
1343 return &iter1 - &iter2; /* oughhhh */
1348 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1354 /* static int counter = 0; */
1356 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1358 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1359 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1360 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1361 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1363 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1364 val2 + modest_text_utils_get_subject_prefix_len(val2),
1371 /* Drag and drop stuff */
1373 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1374 GtkSelectionData *selection_data,
1375 guint info, guint time, gpointer data)
1377 GtkTreeModel *model = NULL;
1379 GtkTreePath *source_row = NULL;
1380 /* GtkTreeSelection *sel = NULL;*/
1382 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1384 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1387 case MODEST_HEADER_ROW:
1388 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1391 TnyHeader *hdr = NULL;
1392 gtk_tree_model_get (model, &iter,
1393 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1396 g_object_unref (G_OBJECT(hdr));
1401 g_message ("%s: default switch case.", __FUNCTION__);
1404 /* commenting out the next, fixes NB#62963 */
1406 /* Set focus on next header */
1407 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1408 gtk_tree_path_next (source_row);
1409 gtk_tree_selection_select_path (sel, source_row);
1411 gtk_tree_path_free (source_row);
1415 /* Header view drag types */
1416 const GtkTargetEntry header_view_drag_types[] = {
1417 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1418 { "text/uri-list", 0, MODEST_MSG },
1422 setup_drag_and_drop (GtkTreeView *self)
1424 gtk_drag_source_set (GTK_WIDGET (self),
1426 header_view_drag_types,
1427 G_N_ELEMENTS (header_view_drag_types),
1428 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1430 g_signal_connect(G_OBJECT (self), "drag_data_get",
1431 G_CALLBACK(drag_data_get_cb), NULL);
1434 static GtkTreePath *
1435 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1437 GtkTreePath *path = NULL;
1438 GtkTreeSelection *sel = NULL;
1441 sel = gtk_tree_view_get_selection(self);
1442 rows = gtk_tree_selection_get_selected_rows (sel, model);
1444 if ((rows == NULL) || (g_list_length(rows) != 1))
1447 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1452 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1459 * This function moves the tree view scroll to the current selected
1460 * row when the widget grabs the focus
1463 on_focus_in (GtkWidget *self,
1464 GdkEventFocus *event,
1467 GtkTreeSelection *selection;
1468 GtkTreeModel *model;
1469 GList *selected = NULL;
1470 GtkTreePath *selected_path = NULL;
1472 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1476 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1477 /* If none selected yet, pick the first one */
1478 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1482 /* Return if the model is empty */
1483 if (!gtk_tree_model_get_iter_first (model, &iter))
1486 path = gtk_tree_model_get_path (model, &iter);
1487 gtk_tree_selection_select_path (selection, path);
1488 gtk_tree_path_free (path);
1491 /* Need to get the all the rows because is selection multiple */
1492 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1493 if (selected == NULL) return FALSE;
1494 selected_path = (GtkTreePath *) selected->data;
1496 /* Check if we need to scroll */
1497 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1498 GtkTreePath *start_path = NULL;
1499 GtkTreePath *end_path = NULL;
1500 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1504 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1505 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1507 /* Scroll to first path */
1508 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1517 gtk_tree_path_free (start_path);
1519 gtk_tree_path_free (end_path);
1521 #endif /* GTK_CHECK_VERSION */
1524 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1525 g_list_free (selected);
1531 idle_notify_headers_count_changed_destroy (gpointer data)
1533 HeadersCountChangedHelper *helper = NULL;
1535 g_return_if_fail (data != NULL);
1536 helper = (HeadersCountChangedHelper *) data;
1538 g_object_unref (helper->change);
1539 g_slice_free (HeadersCountChangedHelper, helper);
1543 idle_notify_headers_count_changed (gpointer data)
1545 TnyFolder *folder = NULL;
1546 ModestHeaderViewPrivate *priv = NULL;
1547 HeadersCountChangedHelper *helper = NULL;
1549 g_return_val_if_fail (data != NULL, FALSE);
1550 helper = (HeadersCountChangedHelper *) data;
1551 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1552 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1554 folder = tny_folder_change_get_folder (helper->change);
1556 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1558 g_mutex_lock (priv->observers_lock);
1560 /* Emit signal to evaluate how headers changes affects to the window view */
1561 g_signal_emit (G_OBJECT(helper->self),
1562 signals[MSG_COUNT_CHANGED_SIGNAL],
1563 0, folder, helper->change);
1565 /* Added or removed headers, so data stored on cliboard are invalid */
1566 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1567 modest_email_clipboard_clear (priv->clipboard);
1569 g_mutex_unlock (priv->observers_lock);
1575 folder_monitor_update (TnyFolderObserver *self,
1576 TnyFolderChange *change)
1578 ModestHeaderViewPrivate *priv = NULL;
1579 TnyFolderChangeChanged changed;
1580 HeadersCountChangedHelper *helper = NULL;
1581 TnyFolder *folder = NULL;
1583 changed = tny_folder_change_get_changed (change);
1585 /* Do not notify the observers if the folder of the header
1586 view has changed before this call to the observer
1588 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1589 folder = tny_folder_change_get_folder (change);
1590 if (folder != priv->folder)
1593 /* Check folder count */
1594 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1595 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1596 helper = g_slice_new0 (HeadersCountChangedHelper);
1597 helper->self = MODEST_HEADER_VIEW(self);
1598 helper->change = g_object_ref(change);
1601 tny_folder_poke_status (folder);
1603 idle_notify_headers_count_changed (helper);
1604 idle_notify_headers_count_changed_destroy (helper);
1610 g_object_unref (folder);
1614 modest_header_view_is_empty (ModestHeaderView *self)
1616 ModestHeaderViewPrivate *priv = NULL;
1618 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1620 return priv->status == HEADER_VIEW_EMPTY;
1624 modest_header_view_clear (ModestHeaderView *self)
1626 modest_header_view_set_folder (self, NULL, NULL, NULL);
1630 modest_header_view_copy_selection (ModestHeaderView *header_view)
1632 /* Copy selection */
1633 _clipboard_set_selected_data (header_view, FALSE);
1637 modest_header_view_cut_selection (ModestHeaderView *header_view)
1639 ModestHeaderViewPrivate *priv = NULL;
1640 const gchar **hidding = NULL;
1641 guint i, n_selected;
1643 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1644 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1646 /* Copy selection */
1647 _clipboard_set_selected_data (header_view, TRUE);
1649 /* Get hidding ids */
1650 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1652 /* Clear hidding array created by previous cut operation */
1653 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1655 /* Copy hidding array */
1656 priv->n_selected = n_selected;
1657 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1658 for (i=0; i < n_selected; i++)
1659 priv->hidding_ids[i] = g_strdup(hidding[i]);
1661 /* Hide cut headers */
1662 modest_header_view_refilter (header_view);
1669 _clipboard_set_selected_data (ModestHeaderView *header_view,
1672 ModestHeaderViewPrivate *priv = NULL;
1673 TnyList *headers = NULL;
1675 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1676 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1678 /* Set selected data on clipboard */
1679 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1680 headers = modest_header_view_get_selected_headers (header_view);
1681 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1684 g_object_unref (headers);
1690 filter_row (GtkTreeModel *model,
1694 ModestHeaderViewPrivate *priv = NULL;
1695 TnyHeaderFlags flags;
1696 TnyHeader *header = NULL;
1699 gboolean visible = TRUE;
1700 gboolean found = FALSE;
1702 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1703 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1705 /* Get header from model */
1706 gtk_tree_model_get (model, iter,
1707 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1708 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1711 /* Hide mark as deleted heders */
1712 if (flags & TNY_HEADER_FLAG_DELETED) {
1717 /* If no data on clipboard, return always TRUE */
1718 if (modest_email_clipboard_cleared(priv->clipboard)) {
1723 /* Get message id from header (ensure is a valid id) */
1724 if (!header) return FALSE;
1725 id = g_strdup(tny_header_get_message_id (header));
1728 if (priv->hidding_ids != NULL) {
1729 for (i=0; i < priv->n_selected && !found; i++)
1730 if (priv->hidding_ids[i] != NULL && id != NULL)
1731 found = (!strcmp (priv->hidding_ids[i], id));
1737 priv->status = ((gboolean) priv->status) && !visible;
1741 g_object_unref (header);
1748 _clear_hidding_filter (ModestHeaderView *header_view)
1750 ModestHeaderViewPrivate *priv = NULL;
1753 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1754 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1756 if (priv->hidding_ids != NULL) {
1757 for (i=0; i < priv->n_selected; i++)
1758 g_free (priv->hidding_ids[i]);
1759 g_free(priv->hidding_ids);
1764 modest_header_view_refilter (ModestHeaderView *header_view)
1766 GtkTreeModel *model = NULL;
1767 ModestHeaderViewPrivate *priv = NULL;
1769 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1770 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1772 priv->status = HEADER_VIEW_EMPTY;
1774 /* Hide cut headers */
1775 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1776 if (GTK_IS_TREE_MODEL_FILTER (model))
1777 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1781 * Called when an account is removed. If I'm showing a folder of the
1782 * account that has been removed then clear the view
1785 on_account_removed (TnyAccountStore *self,
1786 TnyAccount *account,
1789 ModestHeaderViewPrivate *priv = NULL;
1791 /* Ignore changes in transport accounts */
1792 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1795 g_print ("--------------------- HEADER ---------------\n");
1797 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1800 TnyAccount *my_account;
1802 my_account = tny_folder_get_account (priv->folder);
1803 if (my_account == account)
1804 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1805 g_object_unref (account);
1809 void modest_header_view_add_observer(
1810 ModestHeaderView *header_view,
1811 ModestHeaderViewObserver *observer)
1813 ModestHeaderViewPrivate *priv = NULL;
1815 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1816 g_assert(observer != NULL);
1817 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1819 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1821 g_mutex_lock(priv->observer_list_lock);
1822 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1823 g_mutex_unlock(priv->observer_list_lock);
1826 void modest_header_view_remove_observer(
1827 ModestHeaderView *header_view,
1828 ModestHeaderViewObserver *observer)
1830 ModestHeaderViewPrivate *priv = NULL;
1832 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1833 g_assert(observer != NULL);
1834 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1836 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1838 g_mutex_lock(priv->observer_list_lock);
1839 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1840 g_mutex_unlock(priv->observer_list_lock);
1843 static void modest_header_view_notify_observers(
1844 ModestHeaderView *header_view,
1845 GtkTreeModel *model,
1846 const gchar *tny_folder_id)
1848 ModestHeaderViewPrivate *priv = NULL;
1850 ModestHeaderViewObserver *observer;
1852 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1854 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1856 g_mutex_lock(priv->observer_list_lock);
1857 iter = priv->observer_list;
1858 while(iter != NULL){
1859 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1860 modest_header_view_observer_update(observer, model,
1862 iter = g_slist_next(iter);
1864 g_mutex_unlock(priv->observer_list_lock);