1 /* modest-tny-header-tree-view.c
3 #include <glib/gi18n.h>
4 #include "modest-tny-header-tree-view.h"
5 #include <tny-list-iface.h>
8 /* 'private'/'protected' functions */
9 static void modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass);
10 static void modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj);
11 static void modest_tny_header_tree_view_finalize (GObject *obj);
13 static void selection_changed (GtkTreeSelection *sel, gpointer user_data);
14 static void column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data);
16 #define PIXMAP_PREFIX PREFIX "/share/modest/pixmaps/"
19 MESSAGE_SELECTED_SIGNAL,
44 typedef struct _ModestTnyHeaderTreeViewPrivate ModestTnyHeaderTreeViewPrivate;
45 struct _ModestTnyHeaderTreeViewPrivate {
46 TnyMsgFolderIface *tny_msg_folder;
47 TnyListIface *headers;
49 GdkPixbuf *icons[HEADER_ICON_NUM];
50 guint sort_columns[SORT_COLUMN_NUM];
52 #define MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
53 MODEST_TYPE_TNY_HEADER_TREE_VIEW, \
54 ModestTnyHeaderTreeViewPrivate))
56 static GObjectClass *parent_class = NULL;
58 /* uncomment the following if you have defined any signals */
59 static guint signals[LAST_SIGNAL] = {0};
62 modest_tny_header_tree_view_get_type (void)
64 static GType my_type = 0;
66 static const GTypeInfo my_info = {
67 sizeof(ModestTnyHeaderTreeViewClass),
69 NULL, /* base finalize */
70 (GClassInitFunc) modest_tny_header_tree_view_class_init,
71 NULL, /* class finalize */
72 NULL, /* class data */
73 sizeof(ModestTnyHeaderTreeView),
75 (GInstanceInitFunc) modest_tny_header_tree_view_init,
77 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
78 "ModestTnyHeaderTreeView",
85 modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass)
87 GObjectClass *gobject_class;
88 gobject_class = (GObjectClass*) klass;
90 parent_class = g_type_class_peek_parent (klass);
91 gobject_class->finalize = modest_tny_header_tree_view_finalize;
93 g_type_class_add_private (gobject_class, sizeof(ModestTnyHeaderTreeViewPrivate));
95 signals[MESSAGE_SELECTED_SIGNAL] =
96 g_signal_new ("message_selected",
97 G_TYPE_FROM_CLASS (gobject_class),
99 G_STRUCT_OFFSET (ModestTnyHeaderTreeViewClass,message_selected),
101 g_cclosure_marshal_VOID__POINTER,
102 G_TYPE_NONE, 1, G_TYPE_POINTER);
110 msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
111 GtkTreeModel *tree_model, GtkTreeIter *iter,
112 GdkPixbuf *icons[HEADER_ICON_NUM])
114 TnyMsgHeaderFlags flags;
117 gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
120 if (flags & TNY_MSG_HEADER_FLAG_SEEN)
121 pixbuf = icons[HEADER_ICON_READ];
123 pixbuf = icons[HEADER_ICON_UNREAD];
125 g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf,
130 attach_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
131 GtkTreeModel *tree_model, GtkTreeIter *iter, GdkPixbuf *icons[HEADER_ICON_NUM])
133 TnyMsgHeaderFlags flags;
136 gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
138 if (flags & TNY_MSG_HEADER_FLAG_ATTACHMENTS)
139 pixbuf = icons[HEADER_ICON_ATTACH];
143 g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
149 header_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
150 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
153 TnyMsgHeaderFlags flags;
155 gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
157 rendobj = G_OBJECT(renderer);
159 if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
160 g_object_set (rendobj, "weight", 800, NULL);
162 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
168 sender_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
169 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
172 TnyMsgHeaderFlags flags;
176 gtk_tree_model_get (tree_model, iter,
177 TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN, &from,
178 TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
180 rendobj = G_OBJECT(renderer);
182 /* simplistic --> remove <email@address> from display */
183 address = g_strstr_len (from, strlen(from), "<");
186 g_object_set (rendobj, "text", from, NULL);
190 if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
191 g_object_set (rendobj, "weight", 800, NULL);
193 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
198 init_icons (GdkPixbuf *icons[HEADER_ICON_NUM])
200 icons[HEADER_ICON_READ] =
201 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "read.xpm",NULL);
202 icons[HEADER_ICON_UNREAD] =
203 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "unread.xpm",NULL);
204 icons[HEADER_ICON_ATTACH] =
205 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "clip.xpm",NULL);
210 static GtkTreeViewColumn*
211 get_new_column (const gchar *name, GtkCellRenderer *renderer,
212 gboolean resizable, gint sort_col_id, gboolean show_as_text,
213 GtkTreeCellDataFunc cell_data_func, gpointer user_data)
215 GtkTreeViewColumn *column;
217 column = gtk_tree_view_column_new_with_attributes(name, renderer, NULL);
218 gtk_tree_view_column_set_resizable (column, resizable);
221 gtk_tree_view_column_add_attribute (column, renderer, "text",
223 if (sort_col_id >= 0)
224 gtk_tree_view_column_set_sort_column_id (column, sort_col_id);
226 gtk_tree_view_column_set_sort_indicator (column, FALSE);
227 gtk_tree_view_column_set_reorderable (column, TRUE);
230 gtk_tree_view_column_set_cell_data_func(column, renderer, cell_data_func,
233 /* g_signal_connect (G_OBJECT (column), "clicked", */
234 /* G_CALLBACK (column_clicked), obj); */
244 modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
246 GtkTreeViewColumn *column;
247 GtkCellRenderer *renderer_msgtype,
251 ModestTnyHeaderTreeViewPrivate *priv;
253 priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj);
255 init_icons (priv->icons);
257 renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
258 renderer_attach = gtk_cell_renderer_pixbuf_new ();
259 renderer_header = gtk_cell_renderer_text_new ();
261 priv->tny_msg_folder = NULL;
262 priv->headers = NULL;
264 for (i = 0; i != SORT_COLUMN_NUM; ++i)
265 priv->sort_columns[i] = -1;
268 column = get_new_column (_("M"), renderer_msgtype, FALSE, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
269 FALSE, (GtkTreeCellDataFunc)msgtype_cell_data, priv->icons);
270 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
271 priv->sort_columns[SORT_COLUMN_MSGTYPE] =
272 gtk_tree_view_column_get_sort_column_id (column);
273 g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
277 column = get_new_column (_("A"), renderer_attach, FALSE, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
278 FALSE, (GtkTreeCellDataFunc)attach_cell_data, priv->icons);
279 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
280 priv->sort_columns[SORT_COLUMN_ATTACH] =
281 gtk_tree_view_column_get_sort_column_id (column);
282 g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
285 column = get_new_column (_("Received"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
286 TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
287 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
288 priv->sort_columns[SORT_COLUMN_RECEIVED] =
289 gtk_tree_view_column_get_sort_column_id (column);
290 g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
294 column = get_new_column (_("From"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
295 TRUE, (GtkTreeCellDataFunc)sender_cell_data, NULL);
296 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
297 priv->sort_columns[SORT_COLUMN_FROM] =
298 gtk_tree_view_column_get_sort_column_id (column);
299 g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
303 column = get_new_column (_("Subject"), renderer_header, TRUE, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
304 TRUE, (GtkTreeCellDataFunc)header_cell_data, NULL);
305 gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
306 priv->sort_columns[SORT_COLUMN_SUBJECT] =
307 gtk_tree_view_column_get_sort_column_id (column);
308 g_signal_connect (G_OBJECT (column), "clicked",G_CALLBACK (column_clicked), obj);
312 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(obj), TRUE);
313 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
315 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj), TRUE); /* alternating row colors */
320 modest_tny_header_tree_view_finalize (GObject *obj)
322 ModestTnyHeaderTreeView *self;
323 ModestTnyHeaderTreeViewPrivate *priv;
326 self = MODEST_TNY_HEADER_TREE_VIEW(obj);
327 priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
330 g_object_unref (G_OBJECT(priv->headers));
332 priv->headers = NULL;
333 priv->tny_msg_folder = NULL;
335 /* cleanup our icons */
336 for (i = 0; i != HEADER_ICON_NUM; ++i) {
337 if (priv->icons[i]) {
338 g_object_unref (G_OBJECT(priv->icons[i]));
339 priv->icons[i] = NULL;
345 modest_tny_header_tree_view_new (TnyMsgFolderIface *folder)
348 GtkTreeSelection *sel;
349 ModestTnyHeaderTreeView *self;
351 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_HEADER_TREE_VIEW, NULL));
352 self = MODEST_TNY_HEADER_TREE_VIEW(obj);
354 if (!modest_tny_header_tree_view_set_folder (self, NULL)) {
355 g_warning ("could not set the folder");
356 g_object_unref (obj);
360 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
361 g_signal_connect (sel, "changed",
362 G_CALLBACK(selection_changed), self);
364 return GTK_WIDGET(self);
369 cmp_rows (GtkTreeModel *tree_model, GtkTreeIter *iter1, GtkTreeIter *iter2,
372 gint col_id = GPOINTER_TO_INT (user_data);
375 g_return_val_if_fail (GTK_IS_TREE_MODEL(tree_model), -1);
379 case SORT_COLUMN_RECEIVED:
380 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
382 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
385 g_message ("%d %d %d %d", col_id, val1, val2, val1 - val2);
389 case SORT_COLUMN_SENT:
390 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
392 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_DATE_SENT_COLUMN,
396 case SORT_COLUMN_ATTACH:
397 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
399 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
402 return (val1 & TNY_MSG_HEADER_FLAG_ATTACHMENTS) - (val2 & TNY_MSG_HEADER_FLAG_ATTACHMENTS);
405 case SORT_COLUMN_MSGTYPE:
406 gtk_tree_model_get (tree_model, iter1, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
408 gtk_tree_model_get (tree_model, iter2, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
411 return (val1 & TNY_MSG_HEADER_FLAG_SEEN) - (val2 & TNY_MSG_HEADER_FLAG_SEEN);
414 g_message ("%p %p", iter1, iter2);
415 return &iter1 - &iter2;
422 modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
423 TnyMsgFolderIface *folder)
426 GtkTreeModel *oldsortable, *sortable, *oldmodel;
427 ModestTnyHeaderTreeViewPrivate *priv;
429 g_return_val_if_fail (self, FALSE);
431 priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
434 priv->headers = TNY_LIST_IFACE(tny_msg_header_list_model_new ());
435 tny_msg_folder_iface_get_headers (folder, priv->headers,
437 tny_msg_header_list_model_set_folder (TNY_MSG_HEADER_LIST_MODEL(priv->headers),
440 oldsortable = gtk_tree_view_get_model(GTK_TREE_VIEW (self));
441 if (oldsortable && GTK_IS_TREE_MODEL_SORT(oldsortable)) {
442 GtkTreeModel *oldmodel = gtk_tree_model_sort_get_model
443 (GTK_TREE_MODEL_SORT(oldsortable));
445 g_object_unref (G_OBJECT(oldmodel));
446 g_object_unref (oldsortable);
449 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
451 /* set special sorting functions */
453 gtk_tree_model_sort_reset_default_sort_func (sortable);
455 for (i = 0; i != SORT_COLUMN_NUM; ++i) {
456 int col_id = priv->sort_columns[i];
458 g_message ("%d: %p: %p: %d", i, GTK_TREE_SORTABLE(sortable), sortable, col_id);
459 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE(sortable), col_id,
460 (GtkTreeIterCompareFunc)cmp_rows,
461 GINT_TO_POINTER(col_id), NULL);
465 gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
466 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), TRUE);
468 /* no need to unref sortable */
470 } else /* when there is no folder */
471 gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(self), FALSE);
478 selection_changed (GtkTreeSelection *sel, gpointer user_data)
481 TnyMsgHeaderIface *header;
483 ModestTnyHeaderTreeView *tree_view;
485 g_return_if_fail (sel);
486 g_return_if_fail (user_data);
488 if (!gtk_tree_selection_get_selected (sel, &model, &iter))
489 return; /* msg was _un_selected */
491 tree_view = MODEST_TNY_HEADER_TREE_VIEW (user_data);
493 gtk_tree_model_get (model, &iter,
494 TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
498 TnyMsgHeaderFlags flags;
499 const TnyMsgIface *msg;
500 const TnyMsgFolderIface *folder;
502 folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
504 g_message ("cannot find folder");
506 msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
509 g_message ("cannot find msg");
510 /* FIXME: update display */
514 g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
517 /* mark message as seen; _set_flags crashes, bug in tinymail? */
518 flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
519 //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
524 column_clicked (GtkTreeViewColumn *col, gpointer user_data)
526 GtkTreeView *treeview;
529 treeview = GTK_TREE_VIEW (user_data);
530 id = gtk_tree_view_column_get_sort_column_id (col);
532 gtk_tree_view_set_search_column (treeview, id);