890dd07edf65929be2310ca50ff323aad0e08031
[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-tny-send-queue.h>
35 #include <modest-tny-folder.h>
36 #include <modest-tny-account.h>
37 #include <modest-runtime.h>
38 #include <glib/gi18n.h>
39 #include <modest-platform.h>
40 #include <string.h>
41
42
43 void 
44 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
45 {
46         GSList **send_queues = (GSList **) userdata;
47         *send_queues = g_slist_prepend (*send_queues, value);
48 }
49
50 static ModestTnySendQueueStatus
51 get_status_of_uid (TnyHeader *header)
52 {
53         ModestCacheMgr *cache_mgr;
54         GHashTable     *send_queue_cache;
55         GSList *send_queues = NULL, *node;
56         /* get_msg_status returns suspended by default, so we want to detect changes */
57         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
58         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
59         gchar *msg_uid = NULL;
60         
61         msg_uid = modest_tny_send_queue_get_msg_id (header);
62         cache_mgr = modest_runtime_get_cache_mgr ();
63         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
64                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
65         
66         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
67         
68         for (node = send_queues; node != NULL; node = g_slist_next (node)) {
69                 ModestTnySendQueue *send_queue = MODEST_TNY_SEND_QUEUE (node->data);
70
71                 queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
72                 if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNONW) {
73                         status = queue_status;
74                         break;
75                 }
76         }
77
78         g_free(msg_uid);
79         g_slist_free (send_queues);
80         return status;
81 }
82
83 static const gchar *
84 get_status_string (ModestTnySendQueueStatus status)
85 {
86         switch (status) {
87         case MODEST_TNY_SEND_QUEUE_WAITING:
88                 return _("mcen_li_outbox_waiting");
89                 break;
90         case MODEST_TNY_SEND_QUEUE_SENDING:
91                 return _("mcen_li_outbox_sending");
92                 break;
93         case MODEST_TNY_SEND_QUEUE_SUSPENDED:
94                 return _("mcen_li_outbox_suspended");
95                 break;
96         case MODEST_TNY_SEND_QUEUE_FAILED:
97                 return _("mcen_li_outbox_failed");
98                 break;
99         default:
100                 return "";
101                 break;
102         }
103 }
104
105 static GdkPixbuf*
106 get_pixbuf_for_flag (TnyHeaderFlags flag)
107 {
108         /* optimization */
109         static GdkPixbuf *deleted_pixbuf          = NULL;
110         static GdkPixbuf *seen_pixbuf             = NULL;
111         static GdkPixbuf *unread_pixbuf           = NULL;
112         static GdkPixbuf *attachments_pixbuf      = NULL;
113         static GdkPixbuf *high_pixbuf             = NULL;
114         static GdkPixbuf *low_pixbuf             = NULL;
115         
116         switch (flag) {
117         case TNY_HEADER_FLAG_DELETED:
118                 if (G_UNLIKELY(!deleted_pixbuf))
119                         deleted_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_DELETED);
120                 return deleted_pixbuf;
121         case TNY_HEADER_FLAG_SEEN:
122                 if (G_UNLIKELY(!seen_pixbuf))
123                         seen_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_READ);
124                 return seen_pixbuf;
125         case TNY_HEADER_FLAG_ATTACHMENTS:
126                 if (G_UNLIKELY(!attachments_pixbuf))
127                         attachments_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_ATTACH);
128                 return attachments_pixbuf;
129         case TNY_HEADER_FLAG_HIGH_PRIORITY:
130                 if (G_UNLIKELY(!high_pixbuf))
131                         high_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_HIGH);
132                 return high_pixbuf;
133         case TNY_HEADER_FLAG_LOW_PRIORITY:
134                 if (G_UNLIKELY(!low_pixbuf))
135                         low_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_LOW);
136                 return low_pixbuf;
137         default:
138                 if (G_UNLIKELY(!unread_pixbuf))
139                         unread_pixbuf = modest_platform_get_icon (MODEST_HEADER_ICON_UNREAD);
140                 return unread_pixbuf;
141         }
142 }
143
144 static void
145 set_common_flags (GtkCellRenderer *renderer, TnyHeaderFlags flags)
146 {
147         g_object_set (G_OBJECT(renderer),
148                       "weight", (flags & TNY_HEADER_FLAG_SEEN) ? PANGO_WEIGHT_NORMAL: PANGO_WEIGHT_ULTRABOLD,
149                       "strikethrough",  (flags & TNY_HEADER_FLAG_DELETED) ?  TRUE:FALSE,
150                       NULL);    
151 }
152
153
154 void
155 _modest_header_view_msgtype_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
156                    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
157 {
158         TnyHeaderFlags flags;
159                 
160         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
161                             &flags, -1);
162
163         if (flags & TNY_HEADER_FLAG_DELETED)
164                 g_object_set (G_OBJECT (renderer), "pixbuf",
165                               get_pixbuf_for_flag (TNY_HEADER_FLAG_DELETED), NULL);           
166         else if (flags & TNY_HEADER_FLAG_SEEN)
167                 g_object_set (G_OBJECT (renderer), "pixbuf",
168                               get_pixbuf_for_flag (TNY_HEADER_FLAG_SEEN), NULL);              
169         else 
170                 g_object_set (G_OBJECT (renderer), "pixbuf",
171                               get_pixbuf_for_flag (0), NULL); /* ughh, FIXME */               
172 }
173
174 void
175 _modest_header_view_attach_cell_data (GtkTreeViewColumn *column, GtkCellRenderer *renderer,
176                                       GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer user_data)
177 {
178         TnyHeaderFlags flags;
179
180         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
181                             &flags, -1);
182
183         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
184                 g_object_set (G_OBJECT (renderer), "pixbuf",
185                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS),
186                               NULL);
187 }
188
189 void
190 _modest_header_view_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
191                   GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
192 {
193         TnyHeaderFlags flags;
194         
195         gtk_tree_model_get (tree_model, iter, TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN,
196                             &flags, -1);
197         set_common_flags (renderer, flags);
198 }
199
200
201 void
202 _modest_header_view_date_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
203                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
204                                      gpointer user_data)
205 {
206         TnyHeaderFlags flags;
207         guint date, date_col;
208         gchar *display_date = NULL;
209         gboolean received = GPOINTER_TO_INT(user_data);
210
211         if (received)
212                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
213         else
214                 date_col = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
215
216         gtk_tree_model_get (tree_model, iter,
217                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
218                             date_col, &date,
219                             -1);
220
221         display_date = modest_text_utils_get_display_date (date);
222         g_object_set (G_OBJECT(renderer), "text", display_date, NULL);  
223
224         set_common_flags (renderer, flags);
225         g_free (display_date);
226 }
227
228 void
229 _modest_header_view_sender_receiver_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
230                             GtkTreeModel *tree_model,  GtkTreeIter *iter,  gboolean is_sender)
231 {
232         TnyHeaderFlags flags;
233         gchar *address;
234         gint sender_receiver_col;
235
236         if (is_sender)
237                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
238         else
239                 sender_receiver_col = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
240                 
241         gtk_tree_model_get (tree_model, iter,
242                             sender_receiver_col,  &address,
243                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
244                             -1);
245         
246         g_object_set (G_OBJECT(renderer),
247                       "text",
248                       modest_text_utils_get_display_address (address),
249                       NULL);
250         g_free (address);
251         set_common_flags (renderer, flags);
252 }
253 /*
254  * this for both incoming and outgoing mail, depending on the the user_data
255  * parameter. in the incoming case, show 'From' and 'Received date', in the
256  * outgoing case, show 'To' and 'Sent date'
257  */
258 void
259 _modest_header_view_compact_header_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
260                                                GtkTreeModel *tree_model,  GtkTreeIter *iter,  gpointer user_data)
261 {
262         g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column));
263         g_return_if_fail (GTK_IS_CELL_RENDERER (renderer));
264         g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
265         
266         /* Note that GtkTreeModel is a GtkTreeModelFilter. */
267         
268         /* printf ("DEBUG: %s: tree_model gtype=%s\n", __FUNCTION__, G_OBJECT_TYPE_NAME (tree_model)); */
269         
270         TnyHeaderFlags flags = 0;
271         TnyHeaderFlags prior_flags = 0;
272         gchar *address = NULL;
273         gchar *subject = NULL;
274         gchar *header = NULL;
275         time_t date = 0;
276         
277         GtkCellRenderer *recipient_cell, *date_or_status_cell, *subject_cell,
278                 *attach_cell, *priority_cell,
279                 *recipient_box, *subject_box = NULL;
280         TnyHeader *msg_header = NULL;
281         gchar *display_date = NULL, *tmp_date = NULL;
282
283         recipient_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "recpt-box-renderer"));
284         subject_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "subject-box-renderer"));
285         priority_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "priority-renderer"));
286         subject_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "subject-renderer"));
287         attach_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "attach-renderer"));
288         recipient_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "recipient-renderer"));
289         date_or_status_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "date-renderer"));
290
291         ModestHeaderViewCompactHeaderMode header_mode = GPOINTER_TO_INT (user_data); 
292
293         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN)
294                 gtk_tree_model_get (tree_model, iter,
295                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
296                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,  &address,
297                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
298                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN, &date,   
299                                     -1);
300         else
301                 gtk_tree_model_get (tree_model, iter,
302                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
303                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,  &address,
304                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
305                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &date,
306                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &msg_header,
307                                     -1);
308
309         /* flags */
310         prior_flags = flags & TNY_HEADER_FLAG_PRIORITY;
311         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
312                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
313                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS),
314                               NULL);
315         else
316                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
317                               NULL, NULL);
318         if (flags & TNY_HEADER_FLAG_PRIORITY)
319                 g_object_set (G_OBJECT (priority_cell), "pixbuf",
320                               get_pixbuf_for_flag (prior_flags),
321 /*                            get_pixbuf_for_flag (flags & TNY_HEADER_FLAG_PRIORITY), */
322                               NULL);
323         else
324                 g_object_set (G_OBJECT (priority_cell), "pixbuf",
325                               NULL, NULL);
326         header = g_markup_printf_escaped ("%s", (subject && strlen (subject)) ? subject : _("mail_va_no_subject"));
327         g_free (subject);
328         g_object_set (G_OBJECT (subject_cell), "markup", header, NULL);
329         g_free (header);
330         set_common_flags (subject_cell, flags);
331
332
333         /* fixme: we hardcode the color to #666666; instead we should use SecundaryTextColour from the
334          * theme (gtkrc file) */
335         
336         header = g_markup_printf_escaped ("<span size='small' foreground='#666666'>%s</span>", modest_text_utils_get_display_address (address));
337         g_free (address);
338         g_object_set (G_OBJECT (recipient_cell),
339                       "markup", header,
340                       NULL);
341         g_free (header);
342         set_common_flags (recipient_cell, flags);
343
344         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX) {
345                 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_WAITING;
346                 const gchar *status_str = "";
347                 if (msg_header != NULL) {
348                         status = get_status_of_uid (msg_header);
349                         if (status == MODEST_TNY_SEND_QUEUE_SUSPENDED) {
350                                 tny_header_unset_flags (msg_header, TNY_HEADER_FLAG_PRIORITY);
351                                 tny_header_set_flags (msg_header, TNY_HEADER_FLAG_SUSPENDED_PRIORITY);
352                         }
353 /*                      if (prior_flags == TNY_HEADER_FLAG_SUSPENDED_PRIORITY) */
354 /*                              status = MODEST_TNY_SEND_QUEUE_SUSPENDED; */
355                 }
356                 status_str = get_status_string (status);
357                 display_date = g_strdup_printf("<span size='small' foreground='#666666'>%s</span>", status_str);
358                 g_object_set (G_OBJECT (date_or_status_cell),
359                               "markup", display_date,
360                               NULL);
361                 g_free (display_date);
362         } else {
363                 /* in some rare cases, mail might have no Date: field. it case,
364                  * don't show the date, instead of bogus 1/1/1970
365                  */
366                 if (date)
367                         tmp_date = modest_text_utils_get_display_date (date);
368                 else
369                         tmp_date = g_strdup ("");
370                 
371                 display_date = g_strdup_printf ("<span size='small' foreground='#666666'>%s</span>", tmp_date);
372                 g_object_set (G_OBJECT (date_or_status_cell),
373                               "markup", display_date,
374                               NULL);
375                 g_free (tmp_date);
376                 g_free (display_date);
377         }
378         if (msg_header != NULL)
379                 g_object_unref (msg_header);
380         set_common_flags (date_or_status_cell, flags);
381 }
382
383
384 void
385 _modest_header_view_size_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
386                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
387                                      gpointer user_data)
388 {
389         TnyHeaderFlags flags;
390         guint size;
391         gchar *size_str;
392         
393         gtk_tree_model_get (tree_model, iter,
394                            TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
395                            TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN, &size,
396                             -1);
397         
398         size_str = modest_text_utils_get_display_size (size);
399         
400         g_object_set (G_OBJECT(renderer), "text", size_str, NULL);
401         set_common_flags (renderer, flags);
402
403         g_free (size_str);
404  }
405
406 void
407 _modest_header_view_status_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
408                                        GtkTreeModel *tree_model,  GtkTreeIter *iter,
409                                        gpointer user_data)
410 {
411         TnyHeaderFlags flags, prior_flags;
412         //guint status;
413         gchar *status_str;
414         
415         gtk_tree_model_get (tree_model, iter,
416                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
417                             -1);
418         
419        prior_flags = flags & TNY_HEADER_FLAG_PRIORITY;
420        if (prior_flags == TNY_HEADER_FLAG_SUSPENDED_PRIORITY)
421                status_str = g_strdup(_("mcen_li_outbox_suspended"));
422        else            
423                status_str = g_strdup(_("mcen_li_outbox_waiting"));
424        
425         g_object_set (G_OBJECT(renderer), "text", status_str, NULL);
426         set_common_flags (renderer, flags);
427
428         g_free (status_str);
429  }
430