* Add two new columns to show compact Sent/Received date. This change
[modest] / src / widgets / modest-header-view-render.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <modest-header-view.h>
31 #include <modest-header-view-priv.h>
32 #include <modest-icon-names.h>
33 #include <modest-text-utils.h>
34 #include <modest-runtime.h>
35 #include <glib/gi18n.h>
36 #include <modest-platform.h>
37
38
39 static gchar *
40 _pango_replace_string (const gchar *string, 
41                        const gchar *in,
42                        const gchar *out) 
43 {
44         gchar **tmp = NULL;     
45         gchar *new_string = NULL;
46         gchar *tmp_string = NULL;
47         guint i = 0;
48
49         /* Split data */
50         if (string == NULL) return g_strdup("");
51         tmp = g_strsplit(string, in, 0);        
52         if (tmp[0] == NULL) return g_strdup(string);
53
54         /* Replace and concat data*/
55         new_string = g_strdup(tmp[0]);
56         for (i = 1; tmp[i] != NULL; i++) {
57                 tmp_string = g_strconcat (new_string, out, tmp[i], NULL);
58                 g_free(new_string);
59                 new_string = g_strdup(tmp_string);
60                 g_free(tmp_string);
61         } 
62         
63         /* Free */
64         g_strfreev(tmp);
65
66         return new_string;
67 }
68
69 static gchar *
70 _pango_parse_string (const gchar *string) 
71 {
72         gchar *parsed_string = NULL;
73         gchar *tmp = NULL;
74         
75         if (string == NULL) return g_strdup("");
76         parsed_string = g_strdup(string);
77
78         /* Check for '&' special character */   
79         tmp = g_strdup(parsed_string);
80         g_free(parsed_string);
81         parsed_string = _pango_replace_string (tmp, "&", "&amp;");
82         g_free(tmp);
83
84         /* Check for '<' special character */
85         tmp = g_strdup(parsed_string);
86         g_free(parsed_string);
87         parsed_string = _pango_replace_string (tmp, "<", "&lt;");
88         g_free(tmp);
89
90         /* Check for '>' special character */
91         tmp = g_strdup(parsed_string);
92         g_free(parsed_string);
93         parsed_string = _pango_replace_string (tmp, ">", "&gt;");
94         g_free(tmp);
95
96         /* Check for ''' special character */
97         tmp = g_strdup(parsed_string);
98         g_free(parsed_string);
99         parsed_string = _pango_replace_string (tmp, "'", "&apos;");
100         g_free(tmp);
101
102         return parsed_string;
103 }
104
105
106 static GdkPixbuf*
107 get_pixbuf_for_flag (TnyHeaderFlags flag)
108 {
109         /* optimization */
110         static GdkPixbuf *deleted_pixbuf          = NULL;
111         static GdkPixbuf *seen_pixbuf             = NULL;
112         static GdkPixbuf *unread_pixbuf           = NULL;
113         static GdkPixbuf *attachments_pixbuf      = NULL;
114         
115         switch (flag) {
116         case TNY_HEADER_FLAG_DELETED:
117                 if (G_UNLIKELY(!deleted_pixbuf))
118                         deleted_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_DELETED);
119                 return deleted_pixbuf;
120         case TNY_HEADER_FLAG_SEEN:
121                 if (G_UNLIKELY(!seen_pixbuf))
122                         seen_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_READ);
123                 return seen_pixbuf;
124         case TNY_HEADER_FLAG_ATTACHMENTS:
125                 if (G_UNLIKELY(!attachments_pixbuf))
126                         attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH);
127                 return attachments_pixbuf;
128         default:
129                 if (G_UNLIKELY(!unread_pixbuf))
130                         unread_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_UNREAD);
131                 return unread_pixbuf;
132         }
133 }
134
135 static GdkPixbuf*
136 get_pixbuf_for_compact_flag (TnyHeaderFlags flags)
137 {
138         /* optimization */
139         static GdkPixbuf *high_attachments_pixbuf   = NULL;
140         static GdkPixbuf *normal_attachments_pixbuf = NULL;
141         static GdkPixbuf *low_attachments_pixbuf    = NULL;
142         static GdkPixbuf *high_pixbuf               = NULL;
143         static GdkPixbuf *low_pixbuf                = NULL;
144         
145         if (flags & TNY_HEADER_FLAG_ATTACHMENTS) {
146                 if (flags & TNY_HEADER_FLAG_HIGH_PRIORITY) {
147                         if (G_UNLIKELY(!high_attachments_pixbuf))
148                                 high_attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH_HIGH_PRIORITY);
149                         return high_attachments_pixbuf;
150                 }
151                 else if (flags & TNY_HEADER_FLAG_LOW_PRIORITY) {
152                         if (G_UNLIKELY(!low_attachments_pixbuf))
153                                 low_attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH_LOW_PRIORITY);
154                         return low_attachments_pixbuf;
155                 }
156                 else {
157                         if (G_UNLIKELY(!normal_attachments_pixbuf))
158                                 normal_attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH_NORM_PRIORITY);
159                         return normal_attachments_pixbuf;
160                 }
161         }
162         else if (flags & TNY_HEADER_FLAG_HIGH_PRIORITY) {
163                 if (G_UNLIKELY(!high_pixbuf))
164                         high_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_HIGH_PRIORITY);
165                 return high_pixbuf;
166         }
167         else if (flags & TNY_HEADER_FLAG_LOW_PRIORITY) {
168                 if (G_UNLIKELY(!low_pixbuf))
169                         low_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_LOW_PRIORITY);
170                 return low_pixbuf;
171         }
172
173         
174         return NULL;
175 }
176
177 static void
178 set_common_flags (GtkCellRenderer *renderer, TnyHeaderFlags flags)
179 {
180         g_object_set (G_OBJECT(renderer),
181                       "weight", (flags & TNY_HEADER_FLAG_SEEN) ? PANGO_WEIGHT_NORMAL: PANGO_WEIGHT_ULTRABOLD,
182                       "strikethrough",  (flags & TNY_HEADER_FLAG_DELETED) ?  TRUE:FALSE,
183                       NULL);    
184 }
185
186
187 void
188 _modest_header_view_msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
189                    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
190 {
191         TnyHeaderFlags flags;
192                 
193         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
194                             &flags, -1);
195
196         if (flags & TNY_HEADER_FLAG_DELETED)
197                 g_object_set (G_OBJECT (renderer), "pixbuf",
198                               get_pixbuf_for_flag (TNY_HEADER_FLAG_DELETED), NULL);           
199         else if (flags & TNY_HEADER_FLAG_SEEN)
200                 g_object_set (G_OBJECT (renderer), "pixbuf",
201                               get_pixbuf_for_flag (TNY_HEADER_FLAG_SEEN), NULL);              
202         else 
203                 g_object_set (G_OBJECT (renderer), "pixbuf",
204                               get_pixbuf_for_flag (0), NULL); /* ughh, FIXME */               
205 }
206
207 void
208 _modest_header_view_attach_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
209                                       GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
210 {
211         TnyHeaderFlags flags;
212
213         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
214                             &flags, -1);
215
216         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
217                 g_object_set (G_OBJECT (renderer), "pixbuf",
218                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS),
219                               NULL);
220 }
221
222 void
223 _modest_header_view_compact_flag_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
224                                             GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
225 {
226         TnyHeaderFlags flags;
227         GdkPixbuf* pixbuf = NULL;
228
229         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
230                             &flags, -1);
231         
232         pixbuf = get_pixbuf_for_compact_flag (flags);
233         if (pixbuf != NULL)     
234                 g_object_set (G_OBJECT (renderer), "pixbuf", pixbuf, NULL);
235         
236 }
237
238 void
239 _modest_header_view_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
240                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
241 {
242         TnyHeaderFlags flags;
243         
244         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
245                             &flags, -1);
246         set_common_flags (renderer, flags);
247 }
248
249
250 void
251 _modest_header_view_date_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
252                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
253                                      gpointer user_data)
254 {
255         TnyHeaderFlags flags;
256         guint date, date_col;
257         gchar *display_date = NULL;
258         gboolean received = GPOINTER_TO_INT(user_data);
259
260         if (received)
261                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
262         else
263                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
264
265         gtk_tree_model_get (tree_model, iter,
266                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
267                             date_col, &date,
268                             -1);
269
270         display_date = modest_text_utils_get_display_date (date);
271         g_object_set (G_OBJECT(renderer), "text", display_date, NULL);  
272
273         set_common_flags (renderer, flags);
274         g_free (display_date);
275 }
276
277 void
278 _modest_header_view_compact_date_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
279                                              GtkTreeModel *tree_model,  GtkTreeIter *iter,
280                                              gpointer user_data)
281 {
282         TnyHeaderFlags flags;
283         guint date, date_col;
284         gchar *display_date = NULL, *tmp_date = NULL;
285         gboolean received = GPOINTER_TO_INT(user_data);
286
287         if (received)
288                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
289         else
290                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
291
292         gtk_tree_model_get (tree_model, iter,
293                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
294                             date_col, &date,
295                             -1);
296
297         tmp_date = modest_text_utils_get_display_date (date);
298         display_date = g_strdup_printf ("\n<small>%s</small>", tmp_date);
299         g_object_set (G_OBJECT(renderer), "markup", display_date, NULL);        
300
301         set_common_flags (renderer, flags);
302         g_free (tmp_date);
303         g_free (display_date);
304 }
305
306 void
307 _modest_header_view_sender_receiver_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
308                             GtkTreeModel *tree_model,  GtkTreeIter *iter,  gboolean is_sender)
309 {
310         TnyHeaderFlags flags;
311         gchar *address;
312         gint sender_receiver_col;
313
314         if (is_sender)
315                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
316         else
317                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
318                 
319         gtk_tree_model_get (tree_model, iter,
320                             sender_receiver_col,  &address,
321                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
322                             -1);
323         
324         g_object_set (G_OBJECT(renderer),
325                       "text",
326                       modest_text_utils_get_display_address (address),
327                       NULL);
328         g_free (address);
329         set_common_flags (renderer, flags);
330 }
331 /*
332  * this for both incoming and outgoing mail, depending on the the user_data
333  * parameter. in the incoming case, show 'From' and 'Received date', in the
334  * outgoing case, show 'To' and 'Sent date'
335  */
336 void
337 _modest_header_view_compact_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
338                                                GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
339 {
340         GObject *rendobj;
341         TnyHeaderFlags flags;
342         gchar *address, *subject, *header, *display_address;
343         gchar *parsed_address, *parsed_subject;
344         time_t date;
345         gboolean is_incoming;
346
347         is_incoming = GPOINTER_TO_INT(user_data); /* GPOINTER_TO_BOOLEAN is not available
348                                                    * in older versions of glib...*/
349
350         if (is_incoming)
351                 gtk_tree_model_get (tree_model, iter,
352                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
353                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,  &address,
354                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
355                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN, &date,   
356                                     -1);
357         else
358                 gtk_tree_model_get (tree_model, iter,
359                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
360                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,  &address,
361                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
362                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &date,   
363                                     -1);
364         
365         rendobj = G_OBJECT(renderer);
366
367         /* deal with empty subjects */
368         if (!subject)
369                 subject = g_strdup (_("mail_va_no_subject"));
370         
371         /* Escape special characteres to allow pango makup`*/
372         display_address = modest_text_utils_get_display_address (address);
373         parsed_address = _pango_parse_string (display_address);
374         parsed_subject = _pango_parse_string (subject);
375
376         header = g_strdup_printf ("%s\n<small>%s</small>",
377                                   parsed_subject,
378                                   parsed_address);
379         g_free (address);
380         g_free (subject);
381         g_free(parsed_subject);
382         g_free(parsed_address);
383
384         g_object_set (rendobj, "markup", header, NULL); 
385         set_common_flags (renderer, flags);
386         
387         g_free (header);
388 }
389
390
391 void
392 _modest_header_view_size_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
393                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
394                                      gpointer user_data)
395 {
396         TnyHeaderFlags flags;
397         guint size;
398         gchar *size_str;
399         
400         gtk_tree_model_get (tree_model, iter,
401                            TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
402                            TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN, &size,
403                             -1);
404         
405         size_str = modest_text_utils_get_display_size (size);
406         
407         g_object_set (G_OBJECT(renderer), "text", size_str, NULL);
408         set_common_flags (renderer, flags);
409
410         g_free (size_str);
411  }
412
413 void
414 _modest_header_view_status_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
415                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
416                                      gpointer user_data)
417 {
418         TnyHeaderFlags flags;
419         guint status;
420         gchar *status_str;
421         
422         gtk_tree_model_get (tree_model, iter,
423                            TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
424                            TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN, &status,
425                             -1);
426         
427 /*      size_str = modest_text_utils_get_display_size (size); */
428         status_str = g_strdup(_("mcen_li_outbox_waiting"));
429         
430         g_object_set (G_OBJECT(renderer), "text", status_str, NULL);
431         set_common_flags (renderer, flags);
432
433         g_free (status_str);
434  }
435