* modest-tny-folder-tree-view.c:
[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 /* 'private'/'protected' functions */
26 static void modest_tny_header_tree_view_class_init  (ModestTnyHeaderTreeViewClass *klass);
27 static void modest_tny_header_tree_view_init        (ModestTnyHeaderTreeView *obj);
28 static void modest_tny_header_tree_view_finalize    (GObject *obj);
29
30 static void selection_changed (GtkTreeSelection *sel, gpointer user_data);
31 static void column_clicked (GtkTreeViewColumn *treeviewcolumn, gpointer user_data);
32         
33 /* list my signals */
34 enum {
35         /* MY_SIGNAL_1, */
36         MESSAGE_SELECTED_SIGNAL,
37         LAST_SIGNAL
38 };
39
40 typedef struct _ModestTnyHeaderTreeViewPrivate ModestTnyHeaderTreeViewPrivate;
41 struct _ModestTnyHeaderTreeViewPrivate {
42         TnyMsgFolderIface *tny_msg_folder;
43         GtkTreeModel *header_tree_model;
44 };
45 #define MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
46                                                          MODEST_TYPE_TNY_HEADER_TREE_VIEW, \
47                                                          ModestTnyHeaderTreeViewPrivate))
48 /* globals */
49 static GObjectClass *parent_class = NULL;
50
51 /* uncomment the following if you have defined any signals */
52 static guint signals[LAST_SIGNAL] = {0};
53
54 GType
55 modest_tny_header_tree_view_get_type (void)
56 {
57         static GType my_type = 0;
58         if (!my_type) {
59                 static const GTypeInfo my_info = {
60                         sizeof(ModestTnyHeaderTreeViewClass),
61                         NULL,           /* base init */
62                         NULL,           /* base finalize */
63                         (GClassInitFunc) modest_tny_header_tree_view_class_init,
64                         NULL,           /* class finalize */
65                         NULL,           /* class data */
66                         sizeof(ModestTnyHeaderTreeView),
67                         1,              /* n_preallocs */
68                         (GInstanceInitFunc) modest_tny_header_tree_view_init,
69                 };
70                 my_type = g_type_register_static (GTK_TYPE_TREE_VIEW,
71                                                   "ModestTnyHeaderTreeView",
72                                                   &my_info, 0);
73         }
74         return my_type;
75 }
76
77 static void
78 modest_tny_header_tree_view_class_init (ModestTnyHeaderTreeViewClass *klass)
79 {
80         GObjectClass *gobject_class;
81         gobject_class = (GObjectClass*) klass;
82
83         parent_class            = g_type_class_peek_parent (klass);
84         gobject_class->finalize = modest_tny_header_tree_view_finalize;
85
86         g_type_class_add_private (gobject_class, sizeof(ModestTnyHeaderTreeViewPrivate));
87
88         signals[MESSAGE_SELECTED_SIGNAL] = 
89                 g_signal_new ("message_selected",
90                               G_TYPE_FROM_CLASS (gobject_class),
91                               G_SIGNAL_RUN_FIRST,
92                               G_STRUCT_OFFSET (ModestTnyHeaderTreeViewClass,message_selected),
93                               NULL, NULL,
94                               g_cclosure_marshal_VOID__POINTER,
95                               G_TYPE_NONE, 1, G_TYPE_POINTER);  
96 }
97
98 static void
99 map_header_icon (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
100            GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
101 {
102         TnyMsgHeaderFlags flags;
103         static gchar txt[10];
104         
105         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags, -1);
106         g_snprintf (txt, 10, "%d", flags);
107         g_object_set (G_OBJECT (renderer), "text", txt, NULL);
108 }
109
110
111 static void
112 map_header_text  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
113            GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer data)
114 {
115         GObject *rendobj;
116         TnyMsgHeaderFlags flags;
117
118         gtk_tree_model_get (tree_model, iter, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN,
119                             &flags, -1);
120         rendobj = G_OBJECT(renderer);
121         
122         if (!(flags & TNY_MSG_HEADER_FLAG_SEEN))
123                 g_object_set (rendobj, "weight", 800, NULL);
124         else
125                 g_object_set (rendobj, "weight", 400, NULL); /* default, non-bold */
126 }
127
128
129 static void
130 modest_tny_header_tree_view_init (ModestTnyHeaderTreeView *obj)
131 {
132         GtkTreeViewColumn *column;
133         GtkCellRenderer *renderer_icon, *renderer_header;
134
135         ModestTnyHeaderTreeViewPrivate *priv;
136         
137         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(obj); 
138
139         renderer_icon   = gtk_cell_renderer_text_new (); /* TODO */
140         renderer_header = gtk_cell_renderer_text_new (); 
141
142         priv->tny_msg_folder = NULL;
143         priv->header_tree_model = NULL;
144
145         /* flags */
146         column =  gtk_tree_view_column_new_with_attributes(_("F"), renderer_icon, NULL);
147         gtk_tree_view_column_set_resizable (column, TRUE);
148         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FLAGS_COLUMN);
149         gtk_tree_view_column_set_sort_indicator (column, FALSE);
150         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
151         gtk_tree_view_column_set_cell_data_func(column, renderer_icon, map_header_icon, NULL, NULL);
152
153         /* date */
154         column =  gtk_tree_view_column_new_with_attributes(_("Date"), renderer_header,
155                                                            "text",
156                                                            TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN,
157                                                            NULL);
158         gtk_tree_view_column_set_resizable (column, TRUE);
159         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_DATE_RECEIVED_COLUMN);
160         gtk_tree_view_column_set_sort_indicator (column, TRUE);
161         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
162         gtk_tree_view_column_set_cell_data_func(column, renderer_header, map_header_text, NULL, NULL);
163         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
164
165         
166         /* from */
167         column =  gtk_tree_view_column_new_with_attributes(_("From"), renderer_header,
168                                                            "text",
169                                                            TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN,
170                                                            NULL);
171         gtk_tree_view_column_set_resizable (column, TRUE);
172         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_FROM_COLUMN);
173         gtk_tree_view_column_set_sort_indicator (column, TRUE);
174         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
175         gtk_tree_view_column_set_cell_data_func(column, renderer_header, map_header_text, NULL, NULL);
176         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
177
178
179         /* subject */
180         column =  gtk_tree_view_column_new_with_attributes(_("Subject"), renderer_header,
181                                                            "text",
182                                                            TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN,
183                                                            NULL);
184         gtk_tree_view_column_set_resizable (column, TRUE);
185         gtk_tree_view_column_set_sort_column_id (column, TNY_MSG_HEADER_LIST_MODEL_SUBJECT_COLUMN);
186         gtk_tree_view_column_set_sort_indicator (column, TRUE);
187         gtk_tree_view_append_column (GTK_TREE_VIEW(obj), column);
188         g_signal_connect (G_OBJECT (column), "clicked", G_CALLBACK (column_clicked), obj);
189
190         
191         /* all cols */
192         gtk_tree_view_set_headers_visible   (GTK_TREE_VIEW(obj), TRUE);
193         gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW(obj), TRUE);
194         gtk_tree_view_column_set_cell_data_func(column, renderer_header, map_header_text, NULL, NULL);
195
196         
197         gtk_tree_view_set_rules_hint (GTK_TREE_VIEW(obj), TRUE); /* alternating row colors */
198         
199 }
200
201 static void
202 modest_tny_header_tree_view_finalize (GObject *obj)
203 {
204         ModestTnyHeaderTreeView        *self;
205         ModestTnyHeaderTreeViewPrivate *priv;
206
207         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
208         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
209
210         if (priv->header_tree_model)    
211                 g_object_unref (G_OBJECT(priv->header_tree_model));
212
213         priv->header_tree_model = NULL;
214         priv->tny_msg_folder    = NULL;
215 }
216
217 GtkWidget*
218 modest_tny_header_tree_view_new (TnyMsgFolderIface *folder)
219 {
220         GObject *obj;
221         GtkTreeSelection *sel;
222         ModestTnyHeaderTreeView *self;
223                 
224         obj  = G_OBJECT(g_object_new(MODEST_TYPE_TNY_HEADER_TREE_VIEW, NULL));
225         self = MODEST_TNY_HEADER_TREE_VIEW(obj);
226
227         if (!modest_tny_header_tree_view_set_folder (self, NULL)) {
228                 g_warning ("could not set the folder");
229                 g_object_unref (obj);
230                 return NULL;
231         }
232                 
233         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(self));
234         g_signal_connect (sel, "changed",
235                           G_CALLBACK(selection_changed), self);
236
237         return GTK_WIDGET(self);
238 }
239
240
241 gboolean
242 modest_tny_header_tree_view_set_folder (ModestTnyHeaderTreeView *self,
243                                         TnyMsgFolderIface *folder)
244 {
245         GtkTreeModel *sortable;
246         ModestTnyHeaderTreeViewPrivate *priv;
247
248         g_return_val_if_fail (self, FALSE);
249
250         priv = MODEST_TNY_HEADER_TREE_VIEW_GET_PRIVATE(self);
251
252         /* clean up old stuff */
253         if (priv->header_tree_model)
254                 g_object_unref (G_OBJECT(priv->header_tree_model));
255         priv->header_tree_model = NULL;
256
257         priv->header_tree_model = GTK_TREE_MODEL (tny_msg_header_list_model_new());
258         if (folder) {
259                 tny_msg_header_list_model_set_folder (
260                         TNY_MSG_HEADER_LIST_MODEL(priv->header_tree_model),
261                         folder, TRUE); /* FIXME: refresh?*/
262                 
263                 sortable = gtk_tree_model_sort_new_with_model (priv->header_tree_model);
264                 
265         } else {
266                 static GtkTreeModel *empty_model = NULL;
267                 if (!empty_model)
268                         empty_model = GTK_TREE_MODEL(gtk_list_store_new(1, G_TYPE_STRING));
269
270                 sortable = empty_model;
271         }
272
273         gtk_tree_view_set_model (GTK_TREE_VIEW (self), sortable);
274
275         if (sortable)
276                 g_object_unref (G_OBJECT(sortable));
277         
278         return TRUE;
279 }
280
281
282 static void
283 selection_changed (GtkTreeSelection *sel, gpointer user_data)
284 {
285         GtkTreeModel            *model;
286         TnyMsgHeaderIface       *header;
287         GtkTreeIter             iter;
288         ModestTnyHeaderTreeView *tree_view;
289
290         g_return_if_fail (sel);
291         g_return_if_fail (user_data);
292         
293         if (!gtk_tree_selection_get_selected (sel, &model, &iter))
294                 return; /* msg was _un_selected */
295         
296         tree_view = MODEST_TNY_HEADER_TREE_VIEW (user_data);
297         
298         gtk_tree_model_get (model, &iter,
299                             TNY_MSG_HEADER_LIST_MODEL_INSTANCE_COLUMN,
300                             &header, -1);
301         
302         if (header) {
303                 TnyMsgHeaderFlags flags;
304                 const TnyMsgIface *msg;
305                 const TnyMsgFolderIface *folder;
306                 
307                 folder = tny_msg_header_iface_get_folder (TNY_MSG_HEADER_IFACE(header));
308                 if (!folder)
309                         g_message ("cannot find folder");
310                 else {
311                         msg = tny_msg_folder_iface_get_message (TNY_MSG_FOLDER_IFACE(folder),
312                                                                 header);
313                         if (!msg) 
314                                 g_message ("cannot find msg");          
315                 }
316
317                 /* mark message as seen; _set_flags crashes, bug in tinymail? */
318                 //flags = tny_msg_header_iface_get_flags (TNY_MSG_HEADER_IFACE(header));
319                 //tny_msg_header_iface_set_flags (header, flags | TNY_MSG_HEADER_FLAG_SEEN);
320                                                 
321                 g_signal_emit (G_OBJECT(tree_view), signals[MESSAGE_SELECTED_SIGNAL], 0,
322                                msg); 
323         }
324 }
325
326 static void
327 column_clicked (GtkTreeViewColumn *col, gpointer user_data)
328 {
329         GtkTreeView *treeview;
330         gint id;
331
332         treeview = GTK_TREE_VIEW (user_data);
333         id = gtk_tree_view_column_get_sort_column_id (col);
334         
335         gtk_tree_view_set_search_column (treeview, id);
336 }