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