a8eecb4eb084ee5a10b4de59622be91f6dcab26a
[modest] / src / modest-tny-header-tree-view.c
1 /* modest-tny-header-tree-view.c 
2  *
3  * This file is part of modest.
4  *
5  * Copyright (C) 2006 Nokia Corporation
6  * 
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  */
22 #include <glib/gi18n.h>
23 #include "modest-tny-header-tree-view.h"
24 #include <tny-list-iface.h>
25
26
27 /* 'private'/'protected' functions */
28 static void modest_tny_header_tree_view_class_init  (ModestTnyHeaderTreeViewClass *klass);
29 static void modest_tny_header_tree_view_init        (ModestTnyHeaderTreeView *obj);
30 static void modest_tny_header_tree_view_finalize    (GObject *obj);
31
32 static void selection_changed (GtkTreeSelection *sel, gpointer user_data);
33 static void column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data);
34
35 #define PIXMAP_PREFIX PREFIX "/share/modest/pixmaps/"
36
37 enum {
38         MESSAGE_SELECTED_SIGNAL,
39         LAST_SIGNAL
40 };
41
42
43 enum {
44         HEADER_ICON_READ,
45         HEADER_ICON_UNREAD,
46         HEADER_ICON_ATTACH,
47         HEADER_ICON_NUM
48 };
49
50
51
52 typedef struct _ModestTnyHeaderTreeViewPrivate ModestTnyHeaderTreeViewPrivate;
53 struct _ModestTnyHeaderTreeViewPrivate {
54         TnyMsgFolderIface *tny_msg_folder;
55         TnyListIface      *headers;
56
57         GdkPixbuf *icons[HEADER_ICON_NUM];
58 };
59 #define MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
60                                                          MODEST_TYPE_TNY_HEADER_TREE_VIEW, \
61                                                          ModestTnyHeaderTreeViewPrivate))
62 /* globals */
63 static GObjectClass *parent_class = NULL;
64
65 /* uncomment the following if you have defined any signals */
66 static guint signals[LAST_SIGNAL] = {0};
67
68 GType
69 modest_tny_header_tree_view_get_type (void)
70 {
71         static GType my_type = 0;
72         if (!my_type) {
73                 static const GTypeInfo my_info = {
74                         sizeof(ModestTnyHeaderTreeViewClass),
75                         NULL,           /* base init */
76                         NULL,           /* base finalize */
77                         (GClassInitFunc) modest_tny_header_tree_view_class_init,
78                         NULL,           /* class finalize */
79                         NULL,           /* class data */
80                         sizeof(ModestTnyHeaderTreeView),
81                         1,              /* n_preallocs */
82                         (GInstanceInitFunc) modest_tny_header_tree_view_init,
83                 };
84                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
85                                                   "ModestTnyHeaderTreeView",
86                                                   &my_info, 0);
87         }
88         return my_type;
89 }
90
91 static void
92 modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass)
93 {
94         GObjectClass *gobject_class;
95         gobject_class = (GObjectClass*) klass;
96
97         parent_class            = g_type_class_peek_parent (klass);
98         gobject_class->finalize = modest_tny_header_tree_view_finalize;
99
100         g_type_class_add_private (gobject_class, sizeof(ModestTnyHeaderTreeViewPrivate));
101
102         signals[MESSAGE_SELECTED_SIGNAL] = 
103                 g_signal_new ("message_selected",
104                               G_TYPE_FROM_CLASS (gobject_class),
105                               G_SIGNAL_RUN_FIRST,
106                               G_STRUCT_OFFSET (ModestTnyHeaderTreeViewClass,message_selected),
107                               NULL, NULL,
108                               g_cclosure_marshal_VOID__POINTER,
109                               G_TYPE_NONE, 1, G_TYPE_POINTER);  
110 }
111
112
113
114 static void
115 flags_cell_data (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
116                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
117 {
118         TnyMsgHeaderFlags flags;
119         static gchar txt[10];
120         
121         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags, -1);
122         g_snprintf (txt, 10, "%d", flags);
123         g_object_set (G_OBJECT (renderer), "text", txt, NULL);
124 }
125
126
127
128 static void
129 msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
130                    GtkTreeModel *tree_model, GtkTreeIter *iter,
131                    GdkPixbuf *icons[HEADER_ICON_NUM])
132 {
133         TnyMsgHeaderFlags flags;
134         GdkPixbuf *pixbuf;
135
136         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
137                             &flags, -1);
138
139         if (flags & TNY_MSG_HEADER_FLAG_SEEN)
140                 pixbuf = icons[HEADER_ICON_READ];
141         else
142                 pixbuf = icons[HEADER_ICON_UNREAD];
143         
144         g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf,
145                       NULL);
146 }
147
148 static void
149 attach_cell_data (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
150                   GtkTreeModel *tree_model,  GtkTreeIter *iter, GdkPixbuf *icons[HEADER_ICON_NUM])
151 {
152         TnyMsgHeaderFlags flags;
153         GdkPixbuf *pixbuf;
154
155         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
156                             &flags, -1);
157         if (flags & TNY_MSG_HEADER_FLAG_ATTACHMENTS)
158                 pixbuf = icons[HEADER_ICON_ATTACH];
159         else
160                 pixbuf = NULL;
161         
162         g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
163 }
164
165
166
167 static void
168 header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
169                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
170 {
171         GObject *rendobj;
172         TnyMsgHeaderFlags flags;
173         
174         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
175                             &flags, -1);
176         rendobj = G_OBJECT(renderer);           
177         
178         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
179                 g_object_set (rendobj, "weight", 800, NULL);
180         else
181                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
182 }
183
184
185 static void
186 init_icons (GdkPixbuf *icons[HEADER_ICON_NUM])
187 {
188         icons[HEADER_ICON_READ] =
189                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "read.xpm",NULL);
190         icons[HEADER_ICON_UNREAD] =
191                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "unread.xpm",NULL);
192         icons[HEADER_ICON_ATTACH] =
193                 gdk_pixbuf_new_from_file (PIXMAP_PREFIX "clip.xpm",NULL);
194 }
195
196
197 static void
198 modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
199 {
200         GtkTreeViewColumn *column;
201         GtkCellRenderer *renderer_msgtype,
202                 *renderer_header,
203                 *renderer_attach;
204         
205         ModestTnyHeaderTreeViewPrivate *priv;
206         
207         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
208
209         init_icons (priv->icons);
210         
211         renderer_msgtype = gtk_cell_renderer_pixbuf_new ();
212         renderer_attach  = gtk_cell_renderer_pixbuf_new ();
213         renderer_header = gtk_cell_renderer_text_new (); 
214
215         priv->tny_msg_folder = NULL;
216         priv->headers        = NULL;
217
218         /* msgtype */
219         column =  gtk_tree_view_column_new_with_attributes(_("M"), renderer_msgtype, NULL);
220         gtk_tree_view_column_set_resizable (column, TRUE);
221         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN);
222         gtk_tree_view_column_set_sort_indicator (column, FALSE);
223         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
224         gtk_tree_view_column_set_cell_data_func(column, renderer_msgtype,
225                                                 (GtkTreeCellDataFunc)msgtype_cell_data,
226                                                 priv->icons, NULL);
227
228
229         /* attachment */
230         column =  gtk_tree_view_column_new_with_attributes(_("A"), renderer_attach, NULL);
231         gtk_tree_view_column_set_resizable (column, TRUE);
232         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN);
233         gtk_tree_view_column_set_sort_indicator (column, FALSE);
234         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
235         gtk_tree_view_column_set_cell_data_func(column, renderer_attach,
236                                                 (GtkTreeCellDataFunc)attach_cell_data,
237                                                 priv->icons, NULL);
238
239         /* date */
240         column =  gtk_tree_view_column_new_with_attributes(_("Date"), renderer_header,
241                                                            "text",
242                                                            TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
243                                                            NULL);
244         gtk_tree_view_column_set_resizable (column, TRUE);
245         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN);
246         gtk_tree_view_column_set_sort_indicator (column, TRUE);
247         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
248         gtk_tree_view_column_set_cell_data_func(column, renderer_header,
249                                                 (GtkTreeCellDataFunc)header_cell_data,
250                                                 NULL, NULL);
251         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
252
253         
254         /* from */
255         column =  gtk_tree_view_column_new_with_attributes(_("From"), renderer_header,
256                                                            "text",
257                                                            TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
258                                                            NULL);
259         gtk_tree_view_column_set_resizable (column, TRUE);
260         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN);
261         gtk_tree_view_column_set_sort_indicator (column, TRUE);
262         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
263         gtk_tree_view_column_set_cell_data_func(column, renderer_header,
264                                                 (GtkTreeCellDataFunc)header_cell_data,
265                                                 NULL, NULL);
266         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
267
268
269         /* subject */
270         column =  gtk_tree_view_column_new_with_attributes(_("Subject"), renderer_header,
271                                                            "text",
272                                                            TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
273                                                            NULL);
274         gtk_tree_view_column_set_resizable (column, TRUE);
275         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN);
276         gtk_tree_view_column_set_sort_indicator (column, TRUE);
277         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
278         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
279
280         
281         /* all cols */
282         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(obj), TRUE);
283         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
284         gtk_tree_view_column_set_cell_data_func(column, renderer_header,
285                                                 (GtkTreeCellDataFunc)header_cell_data,
286                                                 NULL, NULL);
287
288         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj), TRUE); /* alternating row colors */
289         
290 }
291
292 static void
293 modest_tny_header_tree_view_finalize (GObject *obj)
294 {
295         ModestTnyHeaderTreeView        *self;
296         ModestTnyHeaderTreeViewPrivate *priv;
297         int i;
298         
299         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
300         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
301
302         if (priv->headers)      
303                 g_object_unref (G_OBJECT(priv->headers));
304         
305         priv->headers = NULL;
306         priv->tny_msg_folder    = NULL;
307
308         /* cleanup our icons */
309         for (i = 0; i != HEADER_ICON_NUM; ++i) {
310                 if (priv->icons[i]) {
311                         g_object_unref (G_OBJECT(priv->icons[i]));
312                         priv->icons[i] = NULL;
313                 }
314         }
315 }
316
317 GtkWidget*
318 modest_tny_header_tree_view_new (TnyMsgFolderIface *folder)
319 {
320         GObject *obj;
321         GtkTreeSelection *sel;
322         ModestTnyHeaderTreeView *self;
323                 
324         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_HEADER_TREE_VIEW, NULL));
325         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
326
327         if (!modest_tny_header_tree_view_set_folder (self, NULL)) {
328                 g_warning ("could not set the folder");
329                 g_object_unref (obj);
330                 return NULL;
331         }
332         
333         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
334         g_signal_connect (sel, "changed",
335                           G_CALLBACK(selection_changed), self);
336
337         return GTK_WIDGET(self);
338 }
339
340
341 gboolean
342 modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
343                                         TnyMsgFolderIface *folder)
344 {
345         GtkTreeModel *oldsortable, *sortable, *oldmodel;
346         ModestTnyHeaderTreeViewPrivate *priv;
347         static GtkTreeModel *empty_model = NULL;
348         
349         g_return_val_if_fail (self, FALSE);
350
351         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
352         
353         if (folder) {
354                 priv->headers = TNY_LIST_IFACE(tny_msg_header_list_model_new ());
355                 tny_msg_folder_iface_get_headers (folder, priv->headers,
356                                                   FALSE);
357                 tny_msg_header_list_model_set_folder (TNY_MSG_HEADER_LIST_MODEL(priv->headers),
358                                                       folder);
359         } else {
360                 if (!empty_model) 
361                         empty_model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING));
362         }
363                 
364         oldsortable = gtk_tree_view_get_model(GTK_TREE_VIEW (self));
365         if (oldsortable && GTK_IS_TREE_MODEL_SORT(oldsortable)) {
366                 GtkTreeModel *oldmodel = gtk_tree_model_sort_get_model
367                         (GTK_TREE_MODEL_SORT(oldsortable));
368                 if (oldmodel)
369                         g_object_unref (G_OBJECT(oldmodel));
370                 g_object_unref (oldsortable);
371         }
372         
373         if (folder) 
374                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(priv->headers));
375         else
376                 sortable = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL(empty_model));
377
378         gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
379
380         return TRUE;
381 }
382
383
384 static void
385 selection_changed (GtkTreeSelection *sel, gpointer user_data)
386 {
387         GtkTreeModel            *model;
388         TnyMsgHeaderIface       *header;
389         GtkTreeIter             iter;
390         ModestTnyHeaderTreeView *tree_view;
391
392         g_return_if_fail (sel);
393         g_return_if_fail (user_data);
394         
395         if (!gtk_tree_selection_get_selected (sel, &model, &iter))
396                 return; /* msg was _un_selected */
397         
398         tree_view = MODEST_TNY_HEADER_TREE_VIEW (user_data);
399         
400         gtk_tree_model_get (model, &iter,
401                             TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
402                             &header, -1);
403         
404         if (header) {
405                 TnyMsgHeaderFlags flags;
406                 const TnyMsgIface *msg;
407                 const TnyMsgFolderIface *folder;
408                 
409                 folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
410                 if (!folder)
411                         g_message ("cannot find folder");
412                 else {
413                         msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
414                                                                 header);
415                         if (!msg) 
416                                 g_message ("cannot find msg");          
417                 }
418                                         
419                 g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
420                                msg);
421
422                 /* mark message as seen; _set_flags crashes, bug in tinymail? */
423                 flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
424                 //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
425         
426         }
427 }
428
429 static void
430 column_clicked (GtkTreeViewColumn *col, gpointer user_data)
431 {
432         GtkTreeView *treeview;
433         gint id;
434
435         treeview = GTK_TREE_VIEW (user_data);
436         id = gtk_tree_view_column_get_sort_column_id (col);
437         
438         gtk_tree_view_set_search_column (treeview, id);
439 }