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. Fixes NB#58917.
879 static gboolean modest_header_view_on_expose_event(
880 GtkTreeView *header_view,
881 GdkEventExpose *event,
884 GtkTreeSelection *sel;
886 GtkTreeIter tree_iter;
888 model = gtk_tree_view_get_model(header_view);
890 sel = gtk_tree_view_get_selection(header_view);
891 if(!gtk_tree_selection_count_selected_rows(sel))
892 if (gtk_tree_model_get_iter_first(model, &tree_iter))
893 gtk_tree_selection_select_iter(sel, &tree_iter);
899 * This function sets a sortable model in the header view. It's just
900 * used for developing purposes, because it only does a
901 * gtk_tree_view_set_model
904 modest_header_view_set_model (GtkTreeView *header_view, GtkTreeModel *model)
906 /* GtkTreeModel *old_model_sort = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)); */
907 /* if (old_model_sort && GTK_IS_TREE_MODEL_SORT (old_model_sort)) { */
908 /* GtkTreeModel *old_model; */
909 /* ModestHeaderViewPrivate *priv; */
910 /* priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view); */
911 /* old_model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (old_model_sort)); */
913 /* /\* Set new model *\/ */
914 /* gtk_tree_view_set_model (header_view, model); */
916 gtk_tree_view_set_model (header_view, model);
920 modest_header_view_get_folder (ModestHeaderView *self)
922 ModestHeaderViewPrivate *priv;
923 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
926 g_object_ref (priv->folder);
932 modest_header_view_set_folder_intern (ModestHeaderView *self, TnyFolder *folder)
936 ModestHeaderViewPrivate *priv;
937 GList *cols, *cursor;
938 GtkTreeModel *filter_model, *sortable;
940 GtkSortType sort_type;
942 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
944 headers = TNY_LIST (tny_gtk_header_list_model_new ());
946 tny_gtk_header_list_model_set_folder (TNY_GTK_HEADER_LIST_MODEL(headers),
949 /* Add IDLE observer (monitor) and another folder observer for
950 new messages (self) */
951 g_mutex_lock (priv->observers_lock);
953 tny_folder_monitor_stop (priv->monitor);
954 g_object_unref (G_OBJECT (priv->monitor));
956 priv->monitor = TNY_FOLDER_MONITOR (tny_folder_monitor_new (folder));
957 tny_folder_monitor_add_list (priv->monitor, TNY_LIST (headers));
958 tny_folder_monitor_start (priv->monitor);
959 g_mutex_unlock (priv->observers_lock);
961 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(headers));
962 g_object_unref (G_OBJECT (headers));
964 /* Init filter_row function to examine empty status */
965 priv->status = HEADER_VIEW_INIT;
967 /* Create a tree model filter to hide and show rows for cut operations */
968 filter_model = gtk_tree_model_filter_new (sortable, NULL);
969 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter_model),
973 g_object_unref (G_OBJECT (sortable));
975 /* install our special sorting functions */
976 cursor = cols = gtk_tree_view_get_columns (GTK_TREE_VIEW(self));
978 /* Restore sort column id */
980 type = modest_tny_folder_guess_folder_type (folder);
981 sort_colid = modest_header_view_get_sort_column_id (self, type);
982 sort_type = modest_header_view_get_sort_type (self, type);
983 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
986 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
987 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
988 (GtkTreeIterCompareFunc) cmp_rows,
990 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable),
991 TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN,
992 (GtkTreeIterCompareFunc) cmp_subject_rows,
997 modest_header_view_set_model (GTK_TREE_VIEW (self), filter_model);
998 modest_header_view_notify_observers(self, GTK_TREE_MODEL(filter_model),
999 tny_folder_get_id(folder));
1000 g_object_unref (G_OBJECT (filter_model));
1001 /* modest_header_view_set_model (GTK_TREE_VIEW (self), sortable); */
1002 /* g_object_unref (G_OBJECT (sortable)); */
1009 modest_header_view_sort_by_column_id (ModestHeaderView *self,
1011 GtkSortType sort_type)
1013 ModestHeaderViewPrivate *priv = NULL;
1014 GtkTreeModel *tree_filter, *sortable = NULL;
1017 /* Get model and private data */
1018 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1019 tree_filter = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1020 sortable = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER(tree_filter));
1021 /* sortable = gtk_tree_view_get_model (GTK_TREE_VIEW (self)); */
1023 /* Sort tree model */
1024 type = modest_tny_folder_guess_folder_type (priv->folder);
1025 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(sortable),
1028 /* Store new sort parameters */
1029 modest_header_view_set_sort_params (self, sort_colid, sort_type, type);
1031 /* Save GConf parameters */
1032 /* modest_widget_memory_save (modest_runtime_get_conf(), */
1033 /* G_OBJECT(self), "header-view"); */
1038 modest_header_view_set_sort_params (ModestHeaderView *self,
1040 GtkSortType sort_type,
1043 ModestHeaderViewPrivate *priv;
1044 ModestHeaderViewStyle style;
1046 style = modest_header_view_get_style (self);
1047 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1049 priv->sort_colid[style][type] = sort_colid;
1050 priv->sort_type[style][type] = sort_type;
1054 modest_header_view_get_sort_column_id (ModestHeaderView *self,
1057 ModestHeaderViewPrivate *priv;
1058 ModestHeaderViewStyle style;
1060 style = modest_header_view_get_style (self);
1061 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1063 return priv->sort_colid[style][type];
1067 modest_header_view_get_sort_type (ModestHeaderView *self,
1070 ModestHeaderViewPrivate *priv;
1071 ModestHeaderViewStyle style;
1073 style = modest_header_view_get_style (self);
1074 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1076 return priv->sort_type[style][type];
1080 ModestHeaderView *header_view;
1081 RefreshAsyncUserCallback cb;
1086 folder_refreshed_cb (ModestMailOperation *mail_op,
1090 ModestHeaderViewPrivate *priv;
1091 SetFolderHelper *info;
1093 info = (SetFolderHelper*) user_data;
1095 priv = MODEST_HEADER_VIEW_GET_PRIVATE(info->header_view);
1099 info->cb (mail_op, folder, info->user_data);
1101 /* Start the folder count changes observer. We do not need it
1102 before the refresh. Note that the monitor could still be
1103 called for this refresh but now we know that the callback
1104 was previously called */
1105 g_mutex_lock (priv->observers_lock);
1106 tny_folder_add_observer (folder, TNY_FOLDER_OBSERVER (info->header_view));
1107 g_mutex_unlock (priv->observers_lock);
1114 modest_header_view_set_folder (ModestHeaderView *self,
1116 RefreshAsyncUserCallback callback,
1119 ModestHeaderViewPrivate *priv;
1120 ModestWindowMgr *mgr = NULL;
1121 GObject *source = NULL;
1122 SetFolderHelper *info;
1124 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1127 g_mutex_lock (priv->observers_lock);
1128 tny_folder_remove_observer (priv->folder, TNY_FOLDER_OBSERVER (self));
1129 g_object_unref (priv->folder);
1130 priv->folder = NULL;
1131 g_mutex_unlock (priv->observers_lock);
1135 ModestMailOperation *mail_op = NULL;
1136 GtkTreeSelection *selection;
1138 /* Get main window to use it as source of mail operation */
1139 mgr = modest_runtime_get_window_mgr ();
1140 source = G_OBJECT (modest_window_mgr_get_main_window (modest_runtime_get_window_mgr ()));
1142 /* Set folder in the model */
1143 modest_header_view_set_folder_intern (self, folder);
1145 /* Pick my reference. Nothing to do with the mail operation */
1146 priv->folder = g_object_ref (folder);
1148 /* no message selected */
1149 g_signal_emit (G_OBJECT(self), signals[HEADER_SELECTED_SIGNAL], 0, NULL);
1151 info = g_malloc0 (sizeof(SetFolderHelper));
1152 info->header_view = self;
1153 info->cb = callback;
1154 info->user_data = user_data;
1156 /* bug 57631: Clear the selection if exists */
1157 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
1158 gtk_tree_selection_unselect_all(selection);
1160 /* Create the mail operation (source will be the parent widget) */
1161 mail_op = modest_mail_operation_new (MODEST_MAIL_OPERATION_TYPE_RECEIVE, source);
1162 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1165 /* Refresh the folder asynchronously */
1166 modest_mail_operation_refresh_folder (mail_op,
1168 folder_refreshed_cb,
1172 g_object_unref (mail_op);
1174 g_mutex_lock (priv->observers_lock);
1176 if (priv->monitor) {
1177 tny_folder_monitor_stop (priv->monitor);
1178 g_object_unref (G_OBJECT (priv->monitor));
1179 priv->monitor = NULL;
1181 modest_header_view_set_model (GTK_TREE_VIEW (self), NULL);
1183 modest_header_view_notify_observers(self, NULL, NULL);
1185 g_mutex_unlock (priv->observers_lock);
1190 on_header_row_activated (GtkTreeView *treeview, GtkTreePath *path,
1191 GtkTreeViewColumn *column, gpointer userdata)
1193 ModestHeaderView *self = NULL;
1194 ModestHeaderViewPrivate *priv = NULL;
1196 GtkTreeModel *model = NULL;
1197 TnyHeader *header = NULL;
1198 TnyHeaderFlags flags;
1200 self = MODEST_HEADER_VIEW (treeview);
1201 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1203 model = gtk_tree_view_get_model (treeview);
1204 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1207 /* get the first selected item */
1208 gtk_tree_model_get (model, &iter,
1209 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1210 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1213 /* Dont open DELETED messages */
1214 if (flags & TNY_HEADER_FLAG_DELETED) {
1215 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_already_deleted"));
1220 g_signal_emit (G_OBJECT(self),
1221 signals[HEADER_ACTIVATED_SIGNAL],
1227 g_object_unref (G_OBJECT (header));
1232 on_selection_changed (GtkTreeSelection *sel, gpointer user_data)
1234 GtkTreeModel *model;
1235 TnyHeader *header = NULL;
1236 GtkTreePath *path = NULL;
1238 ModestHeaderView *self;
1239 ModestHeaderViewPrivate *priv;
1240 GList *selected = NULL;
1242 g_return_if_fail (sel);
1243 g_return_if_fail (user_data);
1245 self = MODEST_HEADER_VIEW (user_data);
1246 priv = MODEST_HEADER_VIEW_GET_PRIVATE(self);
1248 selected = gtk_tree_selection_get_selected_rows (sel, &model);
1249 if (selected != NULL)
1250 path = (GtkTreePath *) selected->data;
1251 if ((path == NULL) || (!gtk_tree_model_get_iter(model, &iter, path)))
1252 return; /* msg was _un_selected */
1254 gtk_tree_model_get (model, &iter,
1255 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN,
1259 g_signal_emit (G_OBJECT(self),
1260 signals[HEADER_SELECTED_SIGNAL],
1263 g_object_unref (G_OBJECT (header));
1265 /* free all items in 'selected' */
1266 g_list_foreach (selected, (GFunc)gtk_tree_path_free, NULL);
1267 g_list_free (selected);
1271 /* PROTECTED method. It's useful when we want to force a given
1272 selection to reload a msg. For example if we have selected a header
1273 in offline mode, when Modest become online, we want to reload the
1274 message automatically without an user click over the header */
1276 _modest_header_view_change_selection (GtkTreeSelection *selection,
1279 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
1280 g_return_if_fail (MODEST_IS_HEADER_VIEW (user_data));
1282 on_selection_changed (selection, user_data);
1285 static gint compare_priorities (TnyHeaderFlags p1, TnyHeaderFlags p2)
1287 p1 = p1 & TNY_HEADER_FLAG_PRIORITY;
1288 p2 = p2 & TNY_HEADER_FLAG_PRIORITY;
1290 p1 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1292 p2 = TNY_HEADER_FLAG_LOW_PRIORITY + 1;
1297 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1304 /* static int counter = 0; */
1306 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1307 /* col_id = gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (tree_model)); */
1308 col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), MODEST_HEADER_VIEW_FLAG_SORT));
1312 case TNY_HEADER_FLAG_ATTACHMENTS:
1314 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1315 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1316 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1317 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1319 cmp = (val1 & TNY_HEADER_FLAG_ATTACHMENTS) -
1320 (val2 & TNY_HEADER_FLAG_ATTACHMENTS);
1322 return cmp ? cmp : t1 - t2;
1324 case TNY_HEADER_FLAG_PRIORITY:
1325 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val1,
1326 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1,-1);
1327 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &val2,
1328 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2,-1);
1330 /* This is for making priority values respect the intuitive sort relationship
1331 * as HIGH is 11, LOW is 01, and we put NORMAL AS 10 (2) */
1332 cmp = compare_priorities (val1, val2);
1334 return cmp ? cmp : t1 - t2;
1337 return &iter1 - &iter2; /* oughhhh */
1342 cmp_subject_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
1348 /* static int counter = 0; */
1350 g_return_val_if_fail (GTK_IS_TREE_VIEW_COLUMN(user_data), 0);
1352 gtk_tree_model_get (tree_model, iter1, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val1,
1353 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t1, -1);
1354 gtk_tree_model_get (tree_model, iter2, TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &val2,
1355 TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &t2, -1);
1357 cmp = modest_text_utils_utf8_strcmp (val1 + modest_text_utils_get_subject_prefix_len(val1),
1358 val2 + modest_text_utils_get_subject_prefix_len(val2),
1365 /* Drag and drop stuff */
1367 drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
1368 GtkSelectionData *selection_data,
1369 guint info, guint time, gpointer data)
1371 GtkTreeModel *model = NULL;
1373 GtkTreePath *source_row = NULL;
1374 /* GtkTreeSelection *sel = NULL;*/
1376 source_row = get_selected_row (GTK_TREE_VIEW (widget), &model);
1378 if ((source_row == NULL) || (!gtk_tree_model_get_iter(model, &iter, source_row))) return;
1381 case MODEST_HEADER_ROW:
1382 gtk_tree_set_row_drag_data (selection_data, model, source_row);
1385 TnyHeader *hdr = NULL;
1386 gtk_tree_model_get (model, &iter,
1387 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &hdr,
1390 g_object_unref (G_OBJECT(hdr));
1395 g_message ("%s: default switch case.", __FUNCTION__);
1398 /* commenting out the next, fixes NB#62963 */
1400 /* Set focus on next header */
1401 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW (widget));
1402 gtk_tree_path_next (source_row);
1403 gtk_tree_selection_select_path (sel, source_row);
1405 gtk_tree_path_free (source_row);
1409 /* Header view drag types */
1410 const GtkTargetEntry header_view_drag_types[] = {
1411 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP, MODEST_HEADER_ROW },
1412 { "text/uri-list", 0, MODEST_MSG },
1416 setup_drag_and_drop (GtkTreeView *self)
1418 gtk_drag_source_set (GTK_WIDGET (self),
1420 header_view_drag_types,
1421 G_N_ELEMENTS (header_view_drag_types),
1422 GDK_ACTION_MOVE | GDK_ACTION_COPY);
1424 g_signal_connect(G_OBJECT (self), "drag_data_get",
1425 G_CALLBACK(drag_data_get_cb), NULL);
1428 static GtkTreePath *
1429 get_selected_row (GtkTreeView *self, GtkTreeModel **model)
1431 GtkTreePath *path = NULL;
1432 GtkTreeSelection *sel = NULL;
1435 sel = gtk_tree_view_get_selection(self);
1436 rows = gtk_tree_selection_get_selected_rows (sel, model);
1438 if ((rows == NULL) || (g_list_length(rows) != 1))
1441 path = gtk_tree_path_copy(g_list_nth_data (rows, 0));
1446 g_list_foreach(rows,(GFunc) gtk_tree_path_free, NULL);
1453 * This function moves the tree view scroll to the current selected
1454 * row when the widget grabs the focus
1457 on_focus_in (GtkWidget *self,
1458 GdkEventFocus *event,
1461 GtkTreeSelection *selection;
1462 GtkTreeModel *model;
1463 GList *selected = NULL;
1464 GtkTreePath *selected_path = NULL;
1466 model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
1470 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
1471 /* If none selected yet, pick the first one */
1472 if (gtk_tree_selection_count_selected_rows (selection) == 0) {
1476 /* Return if the model is empty */
1477 if (!gtk_tree_model_get_iter_first (model, &iter))
1480 path = gtk_tree_model_get_path (model, &iter);
1481 gtk_tree_selection_select_path (selection, path);
1482 gtk_tree_path_free (path);
1485 /* Need to get the all the rows because is selection multiple */
1486 selected = gtk_tree_selection_get_selected_rows (selection, &model);
1487 if (selected == NULL) return FALSE;
1488 selected_path = (GtkTreePath *) selected->data;
1490 /* Check if we need to scroll */
1491 #if GTK_CHECK_VERSION(2, 8, 0) /* TODO: gtk_tree_view_get_visible_range() is only available in GTK+ 2.8 */
1492 GtkTreePath *start_path = NULL;
1493 GtkTreePath *end_path = NULL;
1494 if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (self),
1498 if ((gtk_tree_path_compare (start_path, selected_path) != -1) ||
1499 (gtk_tree_path_compare (end_path, selected_path) != 1)) {
1501 /* Scroll to first path */
1502 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (self),
1511 gtk_tree_path_free (start_path);
1513 gtk_tree_path_free (end_path);
1515 #endif /* GTK_CHECK_VERSION */
1518 g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
1519 g_list_free (selected);
1525 idle_notify_headers_count_changed_destroy (gpointer data)
1527 HeadersCountChangedHelper *helper = NULL;
1529 g_return_if_fail (data != NULL);
1530 helper = (HeadersCountChangedHelper *) data;
1532 g_object_unref (helper->change);
1533 g_slice_free (HeadersCountChangedHelper, helper);
1537 idle_notify_headers_count_changed (gpointer data)
1539 TnyFolder *folder = NULL;
1540 ModestHeaderViewPrivate *priv = NULL;
1541 HeadersCountChangedHelper *helper = NULL;
1543 g_return_val_if_fail (data != NULL, FALSE);
1544 helper = (HeadersCountChangedHelper *) data;
1545 g_return_val_if_fail (MODEST_IS_HEADER_VIEW(helper->self), FALSE);
1546 g_return_val_if_fail (TNY_FOLDER_CHANGE(helper->change), FALSE);
1548 folder = tny_folder_change_get_folder (helper->change);
1550 priv = MODEST_HEADER_VIEW_GET_PRIVATE (helper->self);
1552 g_mutex_lock (priv->observers_lock);
1554 /* Emit signal to evaluate how headers changes affects to the window view */
1555 g_signal_emit (G_OBJECT(helper->self),
1556 signals[MSG_COUNT_CHANGED_SIGNAL],
1557 0, folder, helper->change);
1559 /* Added or removed headers, so data stored on cliboard are invalid */
1560 if (modest_email_clipboard_check_source_folder (priv->clipboard, folder))
1561 modest_email_clipboard_clear (priv->clipboard);
1563 g_mutex_unlock (priv->observers_lock);
1569 folder_monitor_update (TnyFolderObserver *self,
1570 TnyFolderChange *change)
1572 ModestHeaderViewPrivate *priv = NULL;
1573 TnyFolderChangeChanged changed;
1574 HeadersCountChangedHelper *helper = NULL;
1576 changed = tny_folder_change_get_changed (change);
1578 /* Do not notify the observers if the folder of the header
1579 view has changed before this call to the observer
1581 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1582 if (tny_folder_change_get_folder (change) != priv->folder)
1585 /* Check folder count */
1586 if ((changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) ||
1587 (changed & TNY_FOLDER_CHANGE_CHANGED_EXPUNGED_HEADERS)) {
1588 helper = g_slice_new0 (HeadersCountChangedHelper);
1589 helper->self = MODEST_HEADER_VIEW(self);
1590 helper->change = g_object_ref(change);
1592 g_idle_add_full (G_PRIORITY_DEFAULT,
1593 idle_notify_headers_count_changed,
1595 idle_notify_headers_count_changed_destroy);
1600 modest_header_view_is_empty (ModestHeaderView *self)
1602 ModestHeaderViewPrivate *priv = NULL;
1604 priv = MODEST_HEADER_VIEW_GET_PRIVATE (MODEST_HEADER_VIEW (self));
1606 return priv->status == HEADER_VIEW_EMPTY;
1610 modest_header_view_clear (ModestHeaderView *self)
1612 modest_header_view_set_folder (self, NULL, NULL, NULL);
1616 modest_header_view_copy_selection (ModestHeaderView *header_view)
1618 /* Copy selection */
1619 _clipboard_set_selected_data (header_view, FALSE);
1623 modest_header_view_cut_selection (ModestHeaderView *header_view)
1625 ModestHeaderViewPrivate *priv = NULL;
1626 const gchar **hidding = NULL;
1627 guint i, n_selected;
1629 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1630 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1632 /* Copy selection */
1633 _clipboard_set_selected_data (header_view, TRUE);
1635 /* Get hidding ids */
1636 hidding = modest_email_clipboard_get_hidding_ids (priv->clipboard, &n_selected);
1638 /* Clear hidding array created by previous cut operation */
1639 _clear_hidding_filter (MODEST_HEADER_VIEW (header_view));
1641 /* Copy hidding array */
1642 priv->n_selected = n_selected;
1643 priv->hidding_ids = g_malloc0(sizeof(gchar *) * n_selected);
1644 for (i=0; i < n_selected; i++)
1645 priv->hidding_ids[i] = g_strdup(hidding[i]);
1647 /* Hide cut headers */
1648 modest_header_view_refilter (header_view);
1655 _clipboard_set_selected_data (ModestHeaderView *header_view,
1658 ModestHeaderViewPrivate *priv = NULL;
1659 TnyList *headers = NULL;
1661 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1662 priv = MODEST_HEADER_VIEW_GET_PRIVATE (header_view);
1664 /* Set selected data on clipboard */
1665 g_return_if_fail (MODEST_IS_EMAIL_CLIPBOARD (priv->clipboard));
1666 headers = modest_header_view_get_selected_headers (header_view);
1667 modest_email_clipboard_set_data (priv->clipboard, priv->folder, headers, delete);
1670 g_object_unref (headers);
1676 filter_row (GtkTreeModel *model,
1680 ModestHeaderViewPrivate *priv = NULL;
1681 TnyHeaderFlags flags;
1682 TnyHeader *header = NULL;
1685 gboolean visible = TRUE;
1686 gboolean found = FALSE;
1688 g_return_val_if_fail (MODEST_IS_HEADER_VIEW (user_data), FALSE);
1689 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1691 /* Get header from model */
1692 gtk_tree_model_get (model, iter,
1693 TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
1694 TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &header,
1697 /* Hide mark as deleted heders */
1698 if (flags & TNY_HEADER_FLAG_DELETED) {
1703 /* If no data on clipboard, return always TRUE */
1704 if (modest_email_clipboard_cleared(priv->clipboard)) {
1709 /* Get message id from header (ensure is a valid id) */
1710 if (!header) return FALSE;
1711 id = g_strdup(tny_header_get_message_id (header));
1714 if (priv->hidding_ids != NULL) {
1715 for (i=0; i < priv->n_selected && !found; i++)
1716 if (priv->hidding_ids[i] != NULL && id != NULL)
1717 found = (!strcmp (priv->hidding_ids[i], id));
1723 priv->status = ((gboolean) priv->status) && !visible;
1727 g_object_unref (header);
1734 _clear_hidding_filter (ModestHeaderView *header_view)
1736 ModestHeaderViewPrivate *priv = NULL;
1739 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1740 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1742 if (priv->hidding_ids != NULL) {
1743 for (i=0; i < priv->n_selected; i++)
1744 g_free (priv->hidding_ids[i]);
1745 g_free(priv->hidding_ids);
1750 modest_header_view_refilter (ModestHeaderView *header_view)
1752 GtkTreeModel *model = NULL;
1753 ModestHeaderViewPrivate *priv = NULL;
1755 g_return_if_fail (MODEST_IS_HEADER_VIEW (header_view));
1756 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1758 priv->status = HEADER_VIEW_EMPTY;
1760 /* Hide cut headers */
1761 model = gtk_tree_view_get_model (GTK_TREE_VIEW (header_view));
1762 if (GTK_IS_TREE_MODEL_FILTER (model))
1763 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
1767 * Called when an account is removed. If I'm showing a folder of the
1768 * account that has been removed then clear the view
1771 on_account_removed (TnyAccountStore *self,
1772 TnyAccount *account,
1775 ModestHeaderViewPrivate *priv = NULL;
1777 /* Ignore changes in transport accounts */
1778 if (TNY_IS_TRANSPORT_ACCOUNT (account))
1781 g_print ("--------------------- HEADER ---------------\n");
1783 priv = MODEST_HEADER_VIEW_GET_PRIVATE (user_data);
1786 TnyAccount *my_account;
1788 my_account = tny_folder_get_account (priv->folder);
1789 if (my_account == account)
1790 modest_header_view_clear (MODEST_HEADER_VIEW (user_data));
1791 g_object_unref (account);
1795 void modest_header_view_add_observer(
1796 ModestHeaderView *header_view,
1797 ModestHeaderViewObserver *observer)
1799 ModestHeaderViewPrivate *priv = NULL;
1801 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1802 g_assert(observer != NULL);
1803 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1805 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1807 g_mutex_lock(priv->observer_list_lock);
1808 priv->observer_list = g_slist_prepend(priv->observer_list, observer);
1809 g_mutex_unlock(priv->observer_list_lock);
1812 void modest_header_view_remove_observer(
1813 ModestHeaderView *header_view,
1814 ModestHeaderViewObserver *observer)
1816 ModestHeaderViewPrivate *priv = NULL;
1818 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1819 g_assert(observer != NULL);
1820 g_assert(MODEST_IS_HEADER_VIEW_OBSERVER(observer));
1822 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1824 g_mutex_lock(priv->observer_list_lock);
1825 priv->observer_list = g_slist_remove(priv->observer_list, observer);
1826 g_mutex_unlock(priv->observer_list_lock);
1829 static void modest_header_view_notify_observers(
1830 ModestHeaderView *header_view,
1831 GtkTreeModel *model,
1832 const gchar *tny_folder_id)
1834 ModestHeaderViewPrivate *priv = NULL;
1836 ModestHeaderViewObserver *observer;
1838 g_assert(MODEST_IS_HEADER_VIEW(header_view));
1840 priv = MODEST_HEADER_VIEW_GET_PRIVATE(header_view);
1842 g_mutex_lock(priv->observer_list_lock);
1843 iter = priv->observer_list;
1844 while(iter != NULL){
1845 observer = MODEST_HEADER_VIEW_OBSERVER(iter->data);
1846 modest_header_view_observer_update(observer, model,
1848 iter = g_slist_next(iter);
1850 g_mutex_unlock(priv->observer_list_lock);