* let the dbus-invoked msgs use the same uids as other messages,
[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_PRIORITY);
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_PRIORITY);
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         TnyHeaderFlags flags, prior_flags;
263         gchar *address, *subject, *header;
264         time_t date;
265         ModestHeaderViewCompactHeaderMode header_mode;
266         GtkCellRenderer *recipient_cell, *date_or_status_cell, *subject_cell,
267                 *attach_cell, *priority_cell,
268                 *recipient_box, *subject_box;
269         TnyHeader *msg_header;
270         gchar *display_date = NULL, *tmp_date = NULL;
271
272         recipient_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "recpt-box-renderer"));
273         subject_box = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (renderer), "subject-box-renderer"));
274         priority_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "priority-renderer"));
275         subject_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (subject_box), "subject-renderer"));
276         attach_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "attach-renderer"));
277         recipient_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "recipient-renderer"));
278         date_or_status_cell = GTK_CELL_RENDERER (g_object_get_data (G_OBJECT (recipient_box), "date-renderer"));
279
280         header_mode = GPOINTER_TO_INT (user_data); 
281
282         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_IN)
283                 gtk_tree_model_get (tree_model, iter,
284                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
285                                     TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN,  &address,
286                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
287                                     TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN, &date,   
288                                     -1);
289         else
290                 gtk_tree_model_get (tree_model, iter,
291                                     TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
292                                     TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN,  &address,
293                                     TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN, &subject,
294                                     TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN, &date,
295                                     TNY_GTK_HEADER_LIST_MODEL_INSTANCE_COLUMN, &msg_header,
296                                     -1);
297
298         /* flags */
299         prior_flags = flags & TNY_HEADER_FLAG_PRIORITY;
300         if (flags & TNY_HEADER_FLAG_ATTACHMENTS)
301                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
302                               get_pixbuf_for_flag (TNY_HEADER_FLAG_ATTACHMENTS),
303                               NULL);
304         else
305                 g_object_set (G_OBJECT (attach_cell), "pixbuf",
306                               NULL, NULL);
307         if (flags & TNY_HEADER_FLAG_PRIORITY)
308                 g_object_set (G_OBJECT (priority_cell), "pixbuf",
309                               get_pixbuf_for_flag (prior_flags),
310 /*                            get_pixbuf_for_flag (flags & TNY_HEADER_FLAG_PRIORITY), */
311                               NULL);
312         else
313                 g_object_set (G_OBJECT (priority_cell), "pixbuf",
314                               NULL, NULL);
315         header = g_markup_printf_escaped ("%s", (subject && strlen (subject)) ? subject : _("mail_va_no_subject"));
316         g_free (subject);
317         g_object_set (G_OBJECT (subject_cell), "markup", header, NULL);
318         g_free (header);
319         set_common_flags (subject_cell, flags);
320
321
322         /* fixme: we hardcode the color to #666666; instead we should use SecundaryTextColour from the
323          * theme (gtkrc file) */
324         
325         header = g_markup_printf_escaped ("<span size='small' foreground='#666666'>%s</span>", modest_text_utils_get_display_address (address));
326         g_free (address);
327         g_object_set (G_OBJECT (recipient_cell),
328                       "markup", header,
329                       NULL);
330         g_free (header);
331         set_common_flags (recipient_cell, flags);
332
333         if (header_mode == MODEST_HEADER_VIEW_COMPACT_HEADER_MODE_OUTBOX) {
334                 ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_WAITING;
335                 const gchar *status_str = "";
336                 if (msg_header != NULL) {
337                         status = get_status_of_uid (msg_header);
338                         if (status == MODEST_TNY_SEND_QUEUE_SUSPENDED) {
339                                 tny_header_unset_flags (msg_header, TNY_HEADER_FLAG_PRIORITY);
340                                 tny_header_set_flags (msg_header, TNY_HEADER_FLAG_SUSPENDED_PRIORITY);
341                         }
342 /*                      if (prior_flags == TNY_HEADER_FLAG_SUSPENDED_PRIORITY) */
343 /*                              status = MODEST_TNY_SEND_QUEUE_SUSPENDED; */
344                 }
345                 status_str = get_status_string (status);
346                 display_date = g_strdup_printf("<span size='small' foreground='#666666'>%s</span>", status_str);
347                 g_object_set (G_OBJECT (date_or_status_cell),
348                               "markup", display_date,
349                               NULL);
350                 g_free (display_date);
351         } else {
352                 /* in some rare cases, mail might have no Date: field. it case,
353                  * don't show the date, instead of bogus 1/1/1970
354                  */
355                 if (date)
356                         tmp_date = modest_text_utils_get_display_date (date);
357                 else
358                         tmp_date = g_strdup ("");
359                 
360                 display_date = g_strdup_printf ("<span size='small' foreground='#666666'>%s</span>", tmp_date);
361                 g_object_set (G_OBJECT (date_or_status_cell),
362                               "markup", display_date,
363                               NULL);
364                 g_free (tmp_date);
365                 g_free (display_date);
366         }
367         set_common_flags (date_or_status_cell, flags);
368 }
369
370
371 void
372 _modest_header_view_size_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
373                                      GtkTreeModel *tree_model,  GtkTreeIter *iter,
374                                      gpointer user_data)
375 {
376         TnyHeaderFlags flags;
377         guint size;
378         gchar *size_str;
379         
380         gtk_tree_model_get (tree_model, iter,
381                            TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
382                            TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN, &size,
383                             -1);
384         
385         size_str = modest_text_utils_get_display_size (size);
386         
387         g_object_set (G_OBJECT(renderer), "text", size_str, NULL);
388         set_common_flags (renderer, flags);
389
390         g_free (size_str);
391  }
392
393 void
394 _modest_header_view_status_cell_data  (GtkTreeViewColumn *column,  GtkCellRenderer *renderer,
395                                        GtkTreeModel *tree_model,  GtkTreeIter *iter,
396                                        gpointer user_data)
397 {
398         TnyHeaderFlags flags, prior_flags;
399         //guint status;
400         gchar *status_str;
401         
402         gtk_tree_model_get (tree_model, iter,
403                             TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN, &flags,
404                             -1);
405         
406        prior_flags = flags & TNY_HEADER_FLAG_PRIORITY;
407        if (prior_flags == TNY_HEADER_FLAG_SUSPENDED_PRIORITY)
408                status_str = g_strdup(_("mcen_li_outbox_suspended"));
409        else            
410                status_str = g_strdup(_("mcen_li_outbox_waiting"));
411        
412         g_object_set (G_OBJECT(renderer), "text", status_str, NULL);
413         set_common_flags (renderer, flags);
414
415         g_free (status_str);
416  }
417