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 folder_monitor_update (TnyFolderObserver *self,
1532 TnyFolderChange *change)
1534 ModestHeaderViewPrivate *priv = NULL;
1535 TnyFolderChangeChanged changed;
1536 TnyFolder *folder = NULL;
1538 changed = tny_folder_change_get_changed (change);
1540 /* Do not notify the observers if the folder of the header
1541 view has changed before this call to the observer
1543 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1544 folder = tny_folder_change_get_folder (change);
1545 if (folder != priv->folder)
1548 /* Check folder count */
1549 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1550 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1552 g_mutex_lock (priv->observers_lock);
1554 /* Emit signal to evaluate how headers changes affects
1555 to the window view */
1556 g_signal_emit (G_OBJECT(self),
1557 signals[MSG_COUNT_CHANGED_SIGNAL],
1560 /* Added or removed headers, so data stored on cliboard are invalid */
1561 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1562 modest_email_clipboard_clear (priv->clipboard);
1564 g_mutex_unlock (priv->observers_lock);
1570 g_object_unref (folder);
1574 modest_header_view_is_empty (ModestHeaderView *self)
1576 ModestHeaderViewPrivate *priv = NULL;
1578 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1580 return priv->status == HEADER_VIEW_EMPTY;
1584 modest_header_view_clear (ModestHeaderView *self)
1586 modest_header_view_set_folder (self, NULL, NULL, NULL);
1590 modest_header_view_copy_selection (ModestHeaderView *header_view)
1592 /* Copy selection */
1593 _clipboard_set_selected_data (header_view, FALSE);
1597 modest_header_view_cut_selection (ModestHeaderView *header_view)
1599 ModestHeaderViewPrivate *priv = NULL;
1600 const gchar **hidding = NULL;
1601 guint i, n_selected;
1603 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1604 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1606 /* Copy selection */
1607 _clipboard_set_selected_data (header_view, TRUE);
1609 /* Get hidding ids */
1610 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1612 /* Clear hidding array created by previous cut operation */
1613 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1615 /* Copy hidding array */
1616 priv->n_selected = n_selected;
1617 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1618 for (i=0; i < n_selected; i++)
1619 priv->hidding_ids[i] = g_strdup(hidding[i]);
1621 /* Hide cut headers */
1622 modest_header_view_refilter (header_view);
1629 _clipboard_set_selected_data (ModestHeaderView *header_view,
1632 ModestHeaderViewPrivate *priv = NULL;
1633 TnyList *headers = NULL;
1635 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1636 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1638 /* Set selected data on clipboard */
1639 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1640 headers = modest_header_view_get_selected_headers (header_view);
1641 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1644 g_object_unref (headers);
1650 filter_row (GtkTreeModel *model,
1654 ModestHeaderViewPrivate *priv = NULL;
1655 TnyHeaderFlags flags;
1656 TnyHeader *header = NULL;
1659 gboolean visible = TRUE;
1660 gboolean found = FALSE;
1662 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1663 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1665 /* Get header from model */
1666 gtk_tree_model_get (model, iter,
1667 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1668 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1671 /* Hide mark as deleted heders */
1672 if (flags & TNY_HEADER_FLAG_DELETED) {
1677 /* If no data on clipboard, return always TRUE */
1678 if (modest_email_clipboard_cleared(priv->clipboard)) {
1683 /* Get message id from header (ensure is a valid id) */
1684 if (!header) return FALSE;
1685 id = g_strdup(tny_header_get_message_id (header));
1688 if (priv->hidding_ids != NULL) {
1689 for (i=0; i < priv->n_selected && !found; i++)
1690 if (priv->hidding_ids[i] != NULL && id != NULL)
1691 found = (!strcmp (priv->hidding_ids[i], id));
1697 priv->status = ((gboolean) priv->status) && !visible;
1701 g_object_unref (header);
1708 _clear_hidding_filter (ModestHeaderView *header_view)
1710 ModestHeaderViewPrivate *priv = NULL;
1713 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1714 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1716 if (priv->hidding_ids != NULL) {
1717 for (i=0; i < priv->n_selected; i++)
1718 g_free (priv->hidding_ids[i]);
1719 g_free(priv->hidding_ids);
1724 modest_header_view_refilter (ModestHeaderView *header_view)
1726 GtkTreeModel *model = NULL;
1727 ModestHeaderViewPrivate *priv = NULL;
1729 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1730 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1732 /* Hide cut headers */
1733 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1734 if (GTK_IS_TREE_MODEL_FILTER (model)) {
1736 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1741 * Called when an account is removed. If I'm showing a folder of the
1742 * account that has been removed then clear the view
1745 on_account_removed (TnyAccountStore *self,
1746 TnyAccount *account,
1749 ModestHeaderViewPrivate *priv = NULL;
1751 /* Ignore changes in transport accounts */
1752 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1755 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1758 TnyAccount *my_account;
1760 my_account = tny_folder_get_account (priv->folder);
1761 if (my_account == account)
1762 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1763 g_object_unref (account);
1767 void modest_header_view_add_observer(
1768 ModestHeaderView *header_view,
1769 ModestHeaderViewObserver *observer)
1771 ModestHeaderViewPrivate *priv = NULL;
1773 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1774 g_assert(observer != NULL);
1775 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1777 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1779 g_mutex_lock(priv->observer_list_lock);
1780 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1781 g_mutex_unlock(priv->observer_list_lock);
1785 modest_header_view_remove_observer(ModestHeaderView *header_view,
1786 ModestHeaderViewObserver *observer)
1788 ModestHeaderViewPrivate *priv = NULL;
1790 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1791 g_assert(observer != NULL);
1792 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1794 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1796 g_mutex_lock(priv->observer_list_lock);
1797 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1798 g_mutex_unlock(priv->observer_list_lock);
1802 modest_header_view_notify_observers(ModestHeaderView *header_view,
1803 GtkTreeModel *model,
1804 const gchar *tny_folder_id)
1806 ModestHeaderViewPrivate *priv = NULL;
1808 ModestHeaderViewObserver *observer;
1810 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1812 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1814 g_mutex_lock(priv->observer_list_lock);
1815 iter = priv->observer_list;
1816 while(iter != NULL){
1817 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1818 modest_header_view_observer_update(observer, model,
1820 iter = g_slist_next(iter);
1822 g_mutex_unlock(priv->observer_list_lock);