Modified webpage: now tinymail repository is in gitorious.
[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 <tny-simple-list.h>
31 #include <modest-header-view.h>
32 #include <modest-header-view-priv.h>
33 #include <modest-defs.h>
34 #include <modest-icon-names.h>
35 #include <modest-text-utils.h>
36 #include <modest-tny-account-store.h>
37 #include <modest-tny-send-queue.h>
38 #include <modest-tny-folder.h>
39 #include <modest-tny-account.h>
40 #include <modest-runtime.h>
41 #include <glib/gi18n.h>
42 #include <modest-platform.h>
43 #include <string.h>
44
45 #ifdef MODEST_TOOLKIT_HILDON2
46 #define SMALL_ICON_SIZE MODEST_ICON_SIZE_SMALL
47 #else
48 #define SMALL_ICON_SIZE MODEST_ICON_SIZE_SMALL
49 #endif
50
51 #define MODEST_HEADER_VIEW_MAX_TEXT_LENGTH 128
52
53 static const gchar *
54 get_status_string (ModestTnySendQueueStatus status)
55 {
56         switch (status) {
57         case MODEST_TNY_SEND_QUEUE_WAITING:
58                 return _("mcen_li_outbox_waiting");
59                 break;
60         case MODEST_TNY_SEND_QUEUE_SENDING:
61                 return _("mcen_li_outbox_sending");
62                 break;
63         case MODEST_TNY_SEND_QUEUE_SUSPENDED:
64                 return _("mcen_li_outbox_suspended");
65                 break;
66         case MODEST_TNY_SEND_QUEUE_FAILED:
67                 return _("mcen_li_outbox_failed");
68                 break;
69         default:
70                 return "";
71                 break;
72         }
73 }
74
75 static GdkPixbuf*
76 get_pixbuf_for_flag (TnyHeaderFlags flag, gboolean is_calendar)
77 {
78         /* optimization */
79         static GdkPixbuf *deleted_pixbuf          = NULL;
80         static GdkPixbuf *seen_pixbuf             = NULL;
81         static GdkPixbuf *unread_pixbuf           = NULL;
82         static GdkPixbuf *attachments_pixbuf      = NULL;
83         static GdkPixbuf *high_pixbuf             = NULL;
84         static GdkPixbuf *low_pixbuf             = NULL;
85         static GdkPixbuf *calendar_pixbuf             = NULL;
86
87         if (is_calendar) {
88                 if (G_UNLIKELY(!calendar_pixbuf))
89                         calendar_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_CALENDAR,
90                                                                     SMALL_ICON_SIZE);
91                 return calendar_pixbuf;
92         }
93         
94         switch (flag) {
95         case TNY_HEADER_FLAG_DELETED:
96                 if (G_UNLIKELY(!deleted_pixbuf))
97                         deleted_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_DELETED,
98                                                                    SMALL_ICON_SIZE);
99                 return deleted_pixbuf;
100         case TNY_HEADER_FLAG_SEEN:
101                 if (G_UNLIKELY(!seen_pixbuf))
102                         seen_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_READ,
103                                                                 SMALL_ICON_SIZE);
104                 return seen_pixbuf;
105         case TNY_HEADER_FLAG_ATTACHMENTS:
106                 if (G_UNLIKELY(!attachments_pixbuf))
107                         attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH,
108                                                                        SMALL_ICON_SIZE);
109                 return attachments_pixbuf;
110         case TNY_HEADER_FLAG_HIGH_PRIORITY:
111                 if (G_UNLIKELY(!high_pixbuf))
112                         high_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_HIGH,
113                                                                 SMALL_ICON_SIZE);
114                 return high_pixbuf;
115         case TNY_HEADER_FLAG_LOW_PRIORITY:
116                 if (G_UNLIKELY(!low_pixbuf))
117                         low_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_LOW,
118                                                                SMALL_ICON_SIZE);
119                 return low_pixbuf;
120         case TNY_HEADER_FLAG_NORMAL_PRIORITY:
121                 return NULL;
122         default:
123                 if (G_UNLIKELY(!unread_pixbuf))
124                         unread_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_UNREAD,
125                                                                   SMALL_ICON_SIZE);
126                 return unread_pixbuf;
127         }
128 }
129
130 static void
131 set_common_flags (GtkCellRenderer *renderer, TnyHeaderFlags flags)
132 {
133         g_object_set (G_OBJECT(renderer),
134                       "weight", (flags & TNY_HEADER_FLAG_SEEN) ? PANGO_WEIGHT_NORMAL: PANGO_WEIGHT_ULTRABOLD,
135                       "strikethrough",  (flags & TNY_HEADER_FLAG_DELETED) ?  TRUE:FALSE,
136                       NULL);    
137 }
138
139 static void
140 set_cell_text (GtkCellRenderer *renderer,
141                const gchar *text,
142                TnyHeaderFlags flags)
143 {
144         gboolean strikethrough;
145         gboolean bold_is_active_color;
146         GdkColor *color = NULL;
147         PangoWeight weight;
148         gchar *newtext = NULL;
149
150         /* We have to limit the size of the text. Otherwise Pango
151            could cause freezes trying to render too large texts. This
152            prevents DoS attacks with specially malformed emails */
153         if (g_utf8_validate(text, -1, NULL)) {
154                 if (g_utf8_strlen (text, -1) > MODEST_HEADER_VIEW_MAX_TEXT_LENGTH) {
155                         /* UTF-8 bytes are 4 bytes length in the worst case */
156                         newtext = g_malloc0 (MODEST_HEADER_VIEW_MAX_TEXT_LENGTH * 4);
157                         g_utf8_strncpy (newtext, text, MODEST_HEADER_VIEW_MAX_TEXT_LENGTH);
158                         text = newtext;
159                 }
160         } else {
161                 if (strlen (text) > MODEST_HEADER_VIEW_MAX_TEXT_LENGTH) {
162                         newtext = g_malloc0 (MODEST_HEADER_VIEW_MAX_TEXT_LENGTH);
163                         strncpy (newtext, text, MODEST_HEADER_VIEW_MAX_TEXT_LENGTH);
164                         text = newtext;
165                 }
166         }
167
168         bold_is_active_color = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (renderer), BOLD_IS_ACTIVE_COLOR));
169         if (bold_is_active_color) {
170                 color = g_object_get_data (G_OBJECT (renderer), ACTIVE_COLOR);
171         }
172
173 #ifdef MODEST_TOOLKIT_HILDON2
174         weight =  PANGO_WEIGHT_NORMAL;
175 #else
176         weight =  (bold_is_active_color || (flags & TNY_HEADER_FLAG_SEEN)) ? PANGO_WEIGHT_NORMAL: PANGO_WEIGHT_ULTRABOLD;
177 #endif
178         strikethrough = (flags & TNY_HEADER_FLAG_DELETED) ?  TRUE:FALSE;
179         g_object_freeze_notify (G_OBJECT (renderer));
180         g_object_set (G_OBJECT (renderer), 
181                       "text", text, 
182                       "weight", weight,
183                       "strikethrough", (flags &TNY_HEADER_FLAG_DELETED) ? TRUE : FALSE,
184                       NULL);
185         if (bold_is_active_color && color) {
186                 if (flags & TNY_HEADER_FLAG_SEEN) {
187                         g_object_set (G_OBJECT (renderer),
188                                       "foreground-set", FALSE,
189                                       NULL);
190                 } else {
191                         g_object_set (G_OBJECT (renderer),
192                                       "foreground-gdk", color,
193                                       "foreground-set", TRUE,
194                                       NULL);
195                 }
196         }
197
198         if (newtext)
199                 g_free (newtext);
200
201         g_object_thaw_notify (G_OBJECT (renderer));
202 }
203
204 void
205 _modest_header_view_attach_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
206                                       GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
207 {
208         TnyHeaderFlags flags;
209
210         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
211                             &flags, -1);
212
213         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
214                 g_object_set (G_OBJECT (renderer), "pixbuf",
215                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS, FALSE),
216                               NULL);
217 }
218
219 void
220 _modest_header_view_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
221                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
222 {
223         TnyHeaderFlags flags;
224         
225         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
226                             &flags, -1);
227         set_common_flags (renderer, flags);
228 }
229
230
231 void
232 _modest_header_view_date_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
233                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
234                                      gpointer user_data)
235 {
236         TnyHeaderFlags flags;
237         guint date, date_col;
238         gboolean received = GPOINTER_TO_INT(user_data);
239
240         if (received)
241                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
242         else
243                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
244
245         gtk_tree_model_get (tree_model, iter,
246                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
247                             date_col, &date,
248                             -1);
249
250 #if GTK_CHECK_VERSION (2, 12, 0)
251         ModestHeaderView *header_view;
252         header_view = MODEST_HEADER_VIEW (gtk_tree_view_column_get_tree_view (column));
253         set_cell_text (renderer,
254                        _modest_header_view_get_display_date (header_view, date),
255                        flags);
256 #else
257         set_cell_text (renderer, modest_text_utils_get_display_date (date),
258                        flags);
259 #endif
260 }
261
262 void
263 _modest_header_view_sender_receiver_cell_data  (GtkTreeViewColumn *column,  
264                                                 GtkCellRenderer *renderer,
265                                                 GtkTreeModel *tree_model,  
266                                                 GtkTreeIter *iter,  
267                                                 gboolean is_sender)
268 {
269         TnyHeaderFlags flags;
270         gchar *address;
271         gint sender_receiver_col;
272
273         if (is_sender)
274                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
275         else
276                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
277
278         gtk_tree_model_get (tree_model, iter,
279                             sender_receiver_col,  &address,
280                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
281                             -1);
282
283         modest_text_utils_get_display_address (address); /* string is changed in-place */
284
285         set_cell_text (renderer, (address && address[0] != '\0')?address:_("mail_va_no_to"),
286                        flags);
287         g_free (address);
288 }
289 /*
290  * this for both incoming and outgoing mail, depending on the the user_data
291  * parameter. in the incoming case, show 'From' and 'Received date', in the
292  * outgoing case, show 'To' and 'Sent date'
293  */
294 void
295 _modest_header_view_compact_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
296                                                GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
297 {
298         TnyHeaderFlags flags = 0;
299         gchar *recipients = NULL, *addresses;
300         gchar *subject = NULL;
301         time_t date;
302         GtkCellRenderer *recipient_cell, *date_or_status_cell, *subject_cell,
303                 *attach_cell, *priority_cell,
304                 *recipient_box, *subject_box = NULL;
305         TnyHeader *msg_header = NULL;
306         TnyHeaderFlags prio = 0;
307         gboolean is_calendar = FALSE;
308
309 #ifdef MAEMO_CHANGES
310 #ifdef HAVE_GTK_TREE_VIEW_COLUMN_GET_CELL_DATA_HINT
311         GtkTreeCellDataHint hint;
312 #endif
313 #endif
314
315         g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
316         g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
317         g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
318
319 #ifdef MAEMO_CHANGES
320 #ifdef HAVE_GTK_TREE_VIEW_COLUMN_GET_CELL_DATA_HINT
321         hint = gtk_tree_view_column_get_cell_data_hint (GTK_TREE_VIEW_COLUMN (column));
322
323         if (hint != GTK_TREE_CELL_DATA_HINT_ALL)
324                 return;
325 #endif
326 #endif
327
328         recipient_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "recpt-box-renderer"));
329         subject_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "subject-box-renderer"));
330         priority_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "priority-renderer"));
331         subject_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "subject-renderer"));
332         attach_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "attach-renderer"));
333         recipient_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "recipient-renderer"));
334         date_or_status_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "date-renderer"));
335
336         ModestHeaderViewCompactHeaderMode header_mode = GPOINTER_TO_INT (user_data); 
337
338         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN)
339                 gtk_tree_model_get (tree_model, iter,
340                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
341                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,  &recipients,
342                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
343                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN, &date,
344                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &msg_header,
345                                     -1);
346         else
347                 gtk_tree_model_get (tree_model, iter,
348                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
349                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,  &recipients,
350                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
351                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &date,
352                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &msg_header,
353                                     -1);        
354         /* flags */
355         /* FIXME: we might gain something by doing all the g_object_set's at once */
356         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
357                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
358                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS, FALSE),
359                               NULL);
360         else
361                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
362                               NULL, NULL);
363
364         is_calendar = tny_header_get_user_flag (msg_header, "calendar");
365         if (msg_header) {
366                 prio = tny_header_get_priority (msg_header);
367         }
368         
369         g_object_set (G_OBJECT (priority_cell), "pixbuf",
370                       get_pixbuf_for_flag (prio, is_calendar),
371                       NULL);
372
373         set_cell_text (subject_cell, (subject && subject[0] != 0)?subject:_("mail_va_no_subject"), 
374                        flags);
375         g_free (subject);
376
377         /* Show the list of senders/recipients */
378         addresses = modest_text_utils_get_display_addresses ((const gchar *) recipients);
379         set_cell_text (recipient_cell, (addresses) ? addresses : _("mail_va_no_to"), flags);
380         g_free (addresses);
381         g_free (recipients);
382
383         /* Show status (outbox folder) or sent date */
384         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX) {
385                 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
386                 const gchar *status_str = "";
387                 if (msg_header != NULL) {
388                         status = modest_tny_all_send_queues_get_msg_status (msg_header);
389                         if (status == MODEST_TNY_SEND_QUEUE_SUSPENDED) {
390                                 tny_header_set_flag (msg_header, TNY_HEADER_FLAG_SUSPENDED);
391                         }
392                 }
393
394                 status_str = get_status_string (status);
395                 set_cell_text (date_or_status_cell, status_str, flags);
396         } else {
397 #if GTK_CHECK_VERSION (2, 12, 0)
398                 ModestHeaderView *header_view;
399                 header_view = MODEST_HEADER_VIEW (gtk_tree_view_column_get_tree_view (column));
400                 set_cell_text (date_or_status_cell, 
401                                date ? _modest_header_view_get_display_date (header_view, date) : "",
402                                flags);
403 #else
404                 set_cell_text (date_or_status_cell, 
405                                date ? modest_text_utils_get_display_date (date) : "",
406                                flags);
407 #endif
408         }
409         if (msg_header != NULL)
410                 g_object_unref (msg_header);
411 }
412
413
414 void
415 _modest_header_view_size_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
416                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
417                                      gpointer user_data)
418 {
419         TnyHeaderFlags flags;
420         guint size;
421         gchar *size_str;
422         
423         gtk_tree_model_get (tree_model, iter,
424                            TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
425                            TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN, &size,
426                             -1);
427         
428         size_str = modest_text_utils_get_display_size (size);
429         
430         set_cell_text (renderer, size_str, flags);
431
432         g_free (size_str);
433  }
434
435 void
436 _modest_header_view_status_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
437                                        GtkTreeModel *tree_model,  GtkTreeIter *iter,
438                                        gpointer user_data)
439 {
440         TnyHeaderFlags flags;
441         //guint status;
442         gchar *status_str;
443         
444         gtk_tree_model_get (tree_model, iter,
445                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
446                             -1);
447
448        if (flags & TNY_HEADER_FLAG_SUSPENDED)
449                status_str = g_strdup(_("mcen_li_outbox_suspended"));
450        else            
451                status_str = g_strdup(_("mcen_li_outbox_waiting"));
452        
453         set_cell_text (renderer, status_str, flags);
454
455         g_free (status_str);
456  }
457