root/trunk/libtinymailui-gtk/tny-gtk-msg-view.c

Revision 3666 (checked in by jdapena, 7 months ago)

* Use GOnce registering all types in tinymail to make

registering thread-safe.

Line 
1 /* libtinymailui-gtk - The Tiny Mail UI library for Gtk+
2  * Copyright (C) 2006-2007 Philip Van Hoof <pvanhoof@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with self library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * TnyGtkMsgView:
22  *
23  * a #TnyMsgView for showing a message in Gtk+. It's recommended to wrap instances
24  * of this type into a #GtkScrolledWindow.
25  *
26  * free-function: g_object_unref
27  **/
28
29
30 #include <config.h>
31
32 #include <glib/gi18n-lib.h>
33
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #include <string.h>
42 #include <gtk/gtk.h>
43
44 #include <tny-list.h>
45 #include <tny-simple-list.h>
46 #include <tny-iterator.h>
47
48 #include <tny-gtk-msg-view.h>
49 #include <tny-gtk-text-buffer-stream.h>
50 #include <tny-gtk-attach-list-model.h>
51 #include <tny-header-view.h>
52 #include <tny-gtk-header-view.h>
53 #include <tny-gtk-image-mime-part-view.h>
54 #include <tny-gtk-text-mime-part-view.h>
55 #include <tny-gtk-attachment-mime-part-view.h>
56 #include <tny-gtk-expander-mime-part-view.h>
57 #include <tny-mime-part-saver.h>
58
59 #ifdef GNOME
60 #include <tny-vfs-stream.h>
61 #include <libgnomevfs/gnome-vfs.h>
62 #include <libgnomevfs/gnome-vfs-utils.h>
63 #else
64 #include <tny-fs-stream.h>
65 #endif
66
67 #include "tny-gtk-attach-list-model-priv.h"
68
69 static GObjectClass *parent_class = NULL;
70
71 typedef struct _TnyGtkMsgViewPriv TnyGtkMsgViewPriv;
72
73 struct _TnyGtkMsgViewPriv
74 {
75         TnyMimePart *part;
76         TnyHeaderView *headerview;
77         GtkIconView *attachview;
78         GtkWidget *attachview_sw;
79         GList *unattached_views;
80         gboolean display_html;
81         gboolean display_plain;
82         gboolean display_attachments;
83         gboolean display_rfc822;
84         gboolean first_attachment;
85         GtkBox *kid; gboolean in_expander, parented;
86         TnyStatusCallback status_callback;
87         gpointer status_user_data;
88 };
89
90 typedef struct
91 {
92         gulong signal;
93         TnyMimePart *part;
94 } RealizePriv;
95
96 #define TNY_GTK_MSG_VIEW_GET_PRIVATE(o) \
97         (G_TYPE_INSTANCE_GET_PRIVATE ((o), TNY_TYPE_GTK_MSG_VIEW, TnyGtkMsgViewPriv))
98
99
100 static void tny_gtk_msg_view_display_parts (TnyMsgView *self, TnyList *parts, gboolean alternatives, const gchar *desc);
101 static void remove_mime_part_viewer (TnyMimePartView *mpview, GtkContainer *mpviewers);
102 static gboolean tny_gtk_msg_view_display_part (TnyMsgView *self, TnyMimePart *part, const gchar *desc);
103
104
105 /**
106  * tny_gtk_msg_view_get_display_html:
107  * @self: a #TnyGtkMsgView
108  *
109  * Get whether or not to display text/html mime parts
110  *
111  * returns: whether or not to display text/html mime parts
112  * since: 1.0
113  * audience: application-developer
114  **/
115 gboolean
116 tny_gtk_msg_view_get_display_html (TnyGtkMsgView *self)
117 {
118         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
119         return priv->display_html;
120 }
121
122 /**
123  * tny_gtk_msg_view_get_display_rfc822:
124  * @self: a #TnyGtkMsgView
125  *
126  * Get whether or not to display message/rfc822 mime parts
127  *
128  * returns: whether or not to display message/rfc822 mime parts
129  * since: 1.0
130  * audience: application-developer
131  **/
132 gboolean
133 tny_gtk_msg_view_get_display_rfc822 (TnyGtkMsgView *self)
134 {
135         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
136         return priv->display_rfc822;
137 }
138
139 /**
140  * tny_gtk_msg_view_get_display_attachments:
141  * @self: a #TnyGtkMsgView
142  *
143  * Get whether or not to display attachments
144  *
145  * returns: whether or not to display attachments
146  * since: 1.0
147  * audience: application-developer
148  **/
149 gboolean
150 tny_gtk_msg_view_get_display_attachments (TnyGtkMsgView *self)
151 {
152         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
153         return priv->display_attachments;
154 }
155
156 /**
157  * tny_gtk_msg_view_get_display_plain:
158  * @self: a #TnyGtkMsgView
159  *
160  * Get whether or not to display text/plain mime parts
161  *
162  * returns: whether or not to display text/plain mime parts
163  * since: 1.0
164  * audience: application-developer
165  **/
166 gboolean
167 tny_gtk_msg_view_get_display_plain (TnyGtkMsgView *self)
168 {
169         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
170         return priv->display_plain;
171 }
172
173
174 /**
175  * tny_gtk_msg_view_set_display_html:
176  * @self: a #TnyGtkMsgView
177  * @setting: whether or not to display text/html mime parts
178  *
179  * With this setting will the default implementation of #TnyGtkMsgView display
180  * the HTML source code of text/html mime parts. Default is FALSE.
181  *
182  * Note that these settings only affect the instance in case an overridden
183  * implementation of tny_msg_view_create_mime_part_view_for() doesn't handle
184  * creating a viewer for a mime part.
185  *
186  * So for example in case a more advanced implementation that inherits this
187  * type implements viewing a text/html mime part, and will therefore not call
188  * this types original tny_msg_view_create_mime_part_view_for() method for
189  * the mime part anymore, the setting isn't used.
190  *
191  * The effect, by default, of this setting is showing the HTML source code.
192  *
193  * since: 1.0
194  * audience: application-developer
195  **/
196 void 
197 tny_gtk_msg_view_set_display_html (TnyGtkMsgView *self, gboolean setting)
198 {
199         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
200         priv->display_html = setting;
201         return;
202 }
203
204 /**
205  * tny_gtk_msg_view_set_display_rfc822:
206  * @self: a #TnyGtkMsgView
207  * @setting: whether or not to display message/rfc822 mime parts
208  *
209  * With this setting will the default implementation of #TnyGtkMsgView display
210  * RFC822 inline message mime parts (forwards). Default is FALSE.
211  *
212  * Note that these settings only affect the instance in case an overridden
213  * implementation of tny_msg_view_create_mime_part_view_for() doesn't handle
214  * creating a viewer for a mime part.
215  *
216  * So for example in case a more advanced implementation that inherits this
217  * type implements viewing a text/html mime part, and will therefore not call
218  * this types original tny_msg_view_create_mime_part_view_for() method for
219  * the mime part anymore, the setting isn't used.
220  *
221  * since: 1.0
222  * audience: application-developer
223  **/
224 void 
225 tny_gtk_msg_view_set_display_rfc822 (TnyGtkMsgView *self, gboolean setting)
226 {
227         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
228         priv->display_rfc822 = setting;
229         return;
230 }
231
232
233 /**
234  * tny_gtk_msg_view_set_display_attachments:
235  * @self: a #TnyGtkMsgView
236  * @setting: whether or not to display attachment mime parts
237  *
238  * With this setting will the default implementation of #TnyGtkMsgView display
239  * attachments using a #GtkIconList and the #TnyGtkAttachListModel at the bottom
240  * of the #TnyGtkMsgView's scrollwindow. Default is TRUE.
241  *
242  * Note that these settings only affect the instance in case an overridden
243  * implementation of tny_msg_view_create_mime_part_view_for() doesn't handle
244  * creating a viewer for a mime part.
245  *
246  * So for example in case a more advanced implementation that inherits this
247  * type implements viewing a text/html mime part, and will therefore not call
248  * this types original tny_msg_view_create_mime_part_view_for() method for
249  * the mime part anymore, the setting isn't used.
250  *
251  * since: 1.0
252  * audience: application-developer
253  **/
254 void 
255 tny_gtk_msg_view_set_display_attachments (TnyGtkMsgView *self, gboolean setting)
256 {
257         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
258         priv->display_attachments = setting;
259         return;
260 }
261
262 /**
263  * tny_gtk_msg_view_set_display_plain:
264  * @self: A #TnyGtkMsgView instance
265  * @setting: whether or not to display text/plain mime parts
266  *
267  * With this setting will the default implementation of #TnyGtkMsgView display
268  * text/plain mime parts. Default is TRUE.
269  *
270  * Note that these settings only affect the instance in case an overridden
271  * implementation of tny_msg_view_create_mime_part_view_for() doesn't handle
272  * creating a viewer for a mime part.
273  *
274  * So for example in case a more advanced implementation that inherits this
275  * type implements viewing a text/html mime part, and will therefore not call
276  * this types original tny_msg_view_create_mime_part_view_for() method for
277  * the mime part anymore, the setting isn't used.
278  *
279  * since: 1.0
280  * audience: application-developer
281  **/
282 void 
283 tny_gtk_msg_view_set_display_plain (TnyGtkMsgView *self, gboolean setting)
284 {
285         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
286         priv->display_plain = setting;
287         return;
288 }
289
290
291
292 static void
293 tny_gtk_msg_view_set_unavailable (TnyMsgView *self)
294 {
295         TNY_GTK_MSG_VIEW_GET_CLASS (self)->set_unavailable(self);
296 }
297
298 static void
299 tny_gtk_msg_view_set_unavailable_default (TnyMsgView *self)
300 {
301         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
302
303         tny_msg_view_clear (self);
304
305         tny_header_view_clear (priv->headerview);
306         gtk_widget_hide (GTK_WIDGET (priv->headerview));
307
308         return;
309 }
310
311
312 static TnyMsg*
313 tny_gtk_msg_view_get_msg (TnyMsgView *self)
314 {
315         return TNY_GTK_MSG_VIEW_GET_CLASS (self)->get_msg(self);
316 }
317
318 static TnyMsg*
319 tny_gtk_msg_view_get_msg_default (TnyMsgView *self)
320 {
321         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
322
323         return (priv->part && TNY_IS_MSG (priv->part))?TNY_MSG (g_object_ref (priv->part)):NULL;
324 }
325
326
327 static TnyMsgView*
328 tny_gtk_msg_view_create_new_inline_viewer (TnyMsgView *self)
329 {
330         return TNY_GTK_MSG_VIEW_GET_CLASS (self)->create_new_inline_viewer(self);
331 }
332
333 /**
334  * tny_gtk_msg_view_set_parented:
335  * @self: a #TnyGtkMsgView
336  * @parented: parented or not
337  *
338  * Set @self as parented. Usually internally used.
339  *
340  * since: 1.0
341  * audience: type-implementer, tinymail-developer
342  **/
343 void 
344 tny_gtk_msg_view_set_parented (TnyGtkMsgView *self, gboolean parented)
345 {
346         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
347         priv->parented = TRUE;
348         return;
349 }
350
351 /**
352  * tny_gtk_msg_view_set_status_callback:
353  * @self: a #TnyGtkMsgView
354  * @status_callback: (null-ok): a #TnyStatusCallback or NULL
355  * @status_user_data: (null-ok): user data for @status_callback
356  *
357  * Set the status callback info. This callback can be NULL and will be called
358  * when status information happens. You can for example set a progress bar's
359  * position here (for for example when downloading of a message takes place).
360  *
361  * since: 1.0
362  * audience: application-developer, type-implementer, tinymail-developer
363  **/
364 void 
365 tny_gtk_msg_view_set_status_callback (TnyGtkMsgView *self, TnyStatusCallback status_callback, gpointer status_user_data)
366 {
367         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
368         priv->status_callback = status_callback;
369         priv->status_user_data = status_user_data;
370         return;
371 }
372
373 /**
374  * tny_gtk_msg_view_get_status_callback:
375  * @self: a #TnyGtkMsgView
376  * @status_callback: (out): byref a #TnyStatusCallback
377  * @status_user_data: (out): byref user data for @status_callback
378  *
379  * Get the status callback info. Usually internally used.
380  *
381  * since: 1.0
382  * audience: type-implementer, tinymail-developer
383  **/
384 void 
385 tny_gtk_msg_view_get_status_callback (TnyGtkMsgView *self, TnyStatusCallback *status_callback, gpointer *status_user_data)
386 {
387         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
388         *status_callback = priv->status_callback;
389         *status_user_data = priv->status_user_data;
390 }
391
392 static TnyMsgView*
393 tny_gtk_msg_view_create_new_inline_viewer_default (TnyMsgView *self)
394 {
395         TnyMsgView *retval = tny_gtk_msg_view_new ();
396         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
397
398         tny_gtk_msg_view_set_parented (TNY_GTK_MSG_VIEW (retval), TRUE);
399         tny_gtk_msg_view_set_status_callback (TNY_GTK_MSG_VIEW (retval),
400                 priv->status_callback, priv->status_user_data);
401
402         return retval;
403 }
404
405 static TnyMimePartView*
406 tny_gtk_msg_view_create_mime_part_view_for (TnyMsgView *self, TnyMimePart *part)
407 {
408         return TNY_GTK_MSG_VIEW_GET_CLASS (self)->create_mime_part_view_for(self, part);
409 }
410
411
412 /**
413  * tny_gtk_msg_view_create_mime_part_view_for_default:
414  * @self: a #TnyGtkMsgView
415  * @part: a #TnyMimePart
416  *
417  * This is non-public API documentation
418  *
419  * Default implementation for tny_msg_view_create_mime_part_view_for. You will
420  * usually call this method from overridden implementations that inherit this
421  * type. A.k.a. calling the super method.
422  *
423  * The text/plain and message/rfc822 content types and attachment mime parts
424  * are typically handled by this implementation. The text/html one is typically
425  * handled by an implementation that inherits this implementation. However, if
426  * this implementation handles the text/html mime parts, then the HTML source
427  * code will be displayed to the user.
428  *
429  * ps. A #TnyMimePartView implementation that displays the HTML source code as
430  * plain text would be welcomed for this situation. For example links or
431  * lynx - like.
432  *
433  * This is non-public API documentation
434  **/
435 static TnyMimePartView*
436 tny_gtk_msg_view_create_mime_part_view_for_default (TnyMsgView *self, TnyMimePart *part)
437 {
438         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
439         TnyMimePartView *retval = NULL;
440
441         g_assert (TNY_IS_MIME_PART (part));
442
443         /* PLAIN mime part */
444         if (priv->display_plain && tny_mime_part_content_type_is (part, "text/plain"))
445         {
446                 retval = tny_gtk_text_mime_part_view_new (priv->status_callback, priv->status_user_data);
447
448         /* HTML mime part (shows HTML source code) (should be overridden in case there's
449            support for a HTML TnyMsgView (like the TnyMozEmbedMsgView) */
450         } else if (priv->display_html && tny_mime_part_content_type_is (part, "text/html"))
451         {
452                 retval = tny_gtk_text_mime_part_view_new (priv->status_callback, priv->status_user_data);
453
454         /* Inline message RFC822 */
455         } else if (tny_mime_part_content_type_is (part, "image/*"))
456         {
457                 gboolean nf = FALSE;
458                 TnyMimePartView *image_view = tny_gtk_image_mime_part_view_new (priv->status_callback, priv->status_user_data);
459                 gchar *desc = (gchar *) tny_mime_part_get_description (part);
460
461                 retval = tny_gtk_expander_mime_part_view_new (image_view);
462                 if (!desc) {
463                         const gchar *filen = tny_mime_part_get_filename (part);
464                         if (filen) {
465                                 desc = g_strdup_printf (_("Attached image: %s"), filen);
466                                 nf = TRUE;
467                         } else
468                                 desc = _("Attached image");
469                 }
470                 gtk_expander_set_label (GTK_EXPANDER (retval), desc);
471                 if (nf)
472                         g_free (desc);
473         } else if (tny_mime_part_content_type_is (part, "multipart/*"))
474         {
475                 retval = TNY_MIME_PART_VIEW (tny_msg_view_create_new_inline_viewer (self));
476
477         /* Attachments */
478         } else if ((priv->display_attachments && tny_mime_part_is_attachment (part)) ||
479                 (priv->display_rfc822 && (tny_mime_part_content_type_is (part, "message/rfc822"))))
480         {
481                 GtkTreeModel *model;
482
483                 gtk_widget_show (priv->attachview_sw);
484                 gtk_widget_show (GTK_WIDGET (priv->attachview));
485
486                 if (priv->first_attachment)
487                 {
488                         model = tny_gtk_attach_list_model_new ();
489                         gtk_icon_view_set_model (priv->attachview, model);
490                         priv->first_attachment = FALSE;
491                 } else {
492
493                         model = gtk_icon_view_get_model (priv->attachview);
494                         if (!model || !TNY_IS_LIST (model))
495                         {
496                                 model = tny_gtk_attach_list_model_new ();
497                                 gtk_icon_view_set_model (priv->attachview, model);
498                         }
499                 }
500
501                 retval = tny_gtk_attachment_mime_part_view_new (TNY_GTK_ATTACH_LIST_MODEL (model));
502         }
503
504         return retval;
505 }
506
507
508
509 static void
510 on_mpview_realize (GtkWidget *widget, gpointer user_data)
511 {
512         RealizePriv *prv = user_data;
513
514         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (widget), prv->part);
515         g_signal_handler_disconnect (widget, prv->signal);
516         g_object_unref (prv->part);
517         g_slice_free (RealizePriv, prv);
518 }
519
520
521 /**
522  * tny_gtk_msg_view_display_part:
523  * @self: a #TnyGtkMsgView
524  * @part: a #TnyMimePart
525  *
526  * This is non-public API documentation
527  *
528  * This method will display one mime part.
529  *
530  * The method will use the tny_msg_view_create_mime_part_view_for on self to get
531  * a suitable #TnyMimePartView for part. It will attach it to the viewers GtkBox
532  * and it will show it in case it's a widget. In case the widget isn't realized,
533  * it will delay setting the mime part on the viewer until the widget is
534  * effectively realized (using the realize signal).
535  *
536  * In case it's not a widget nor a known attachment mime part viewer, the
537  * instance is added to a list of unattached views. Upon finalization of self,
538  * will that list be unreferenced.
539  **/
540 static gboolean
541 tny_gtk_msg_view_display_part (TnyMsgView *self, TnyMimePart *part, const gchar *desc)
542 {
543         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
544         TnyMimePartView *mpview = NULL;
545
546         mpview = tny_msg_view_create_mime_part_view_for (TNY_MSG_VIEW (self), part);
547
548         if (mpview)
549         {
550                 if (GTK_IS_WIDGET (mpview))
551                 {
552                         if (TNY_IS_GTK_MSG_VIEW (mpview) && !GTK_IS_WINDOW (mpview) && !priv->in_expander)
553                         {
554                                 TnyGtkMsgViewPriv *mppriv = TNY_GTK_MSG_VIEW_GET_PRIVATE (mpview);
555                                 const gchar *label = tny_mime_part_get_description (part);
556                                 GtkWidget *expander;
557
558                                 mppriv->in_expander = TRUE;
559                                 if (label == NULL || strlen (label) <= 0)
560                                         label = _("Email message attachment");
561                                 expander = gtk_expander_new (label);
562                                 gtk_expander_set_expanded (GTK_EXPANDER (expander),
563                                         tny_mime_part_content_type_is (part, "text/*"));
564                                 gtk_expander_set_spacing (GTK_EXPANDER (expander), 7);
565                                 gtk_container_add (GTK_CONTAINER (expander), GTK_WIDGET (mpview));
566                                 gtk_widget_show (GTK_WIDGET (expander));
567                                 gtk_box_pack_end (GTK_BOX (TNY_GTK_MSG_VIEW (self)->viewers),
568                                         GTK_WIDGET (expander), TRUE, TRUE, 0);
569                         } else if (!GTK_IS_WINDOW (mpview))
570                                 gtk_box_pack_end (GTK_BOX (TNY_GTK_MSG_VIEW (self)->viewers),
571                                                 GTK_WIDGET (mpview), TRUE, TRUE, 0);
572
573                         /* If it's a GtkWindow, we don't pack it (but this is unusual and
574                          probably a mistake by the developer who created the mime part view) */
575
576                         gtk_widget_show (GTK_WIDGET (mpview));
577
578                         if (!GTK_WIDGET_REALIZED (mpview))
579                         {
580                                 RealizePriv *prv = g_slice_new (RealizePriv);
581                                 prv->part = g_object_ref (part);
582                                 prv->signal = g_signal_connect (G_OBJECT (mpview),
583                                         "realize", G_CALLBACK (on_mpview_realize), prv);
584                         } else
585                                 tny_mime_part_view_set_part (mpview, part);
586
587                 } else if (TNY_IS_GTK_ATTACHMENT_MIME_PART_VIEW (mpview))
588                         tny_mime_part_view_set_part (mpview, part);
589                 else if (!TNY_IS_GTK_ATTACHMENT_MIME_PART_VIEW (mpview))
590                 {
591                         priv->unattached_views = g_list_prepend (priv->unattached_views, mpview);
592                         tny_mime_part_view_set_part (mpview, part);
593                 }
594         } else if (!tny_mime_part_content_type_is (part, "multipart/*") &&
595                 !tny_mime_part_content_type_is (part, "message/*"))
596         {
597                 g_warning (_("I don't have a mime part viewer for %s\n"),
598                         tny_mime_part_get_content_type (part));
599
600                 return FALSE;
601         }
602
603         return TRUE;
604 }
605
606
607
608 /**
609  * tny_gtk_msg_view_display_parts:
610  * @self: a #TnyGtkMsgView
611  * @parts: a #TnyList instance containing #TnyMimePart instances
612  * @desc: description of the parent part (if any)
613  *
614  * This is non-public API documentation
615  *
616  * Walks all items in parts and performs tny_gtk_msg_view_display_part on each.
617  **/
618 static void
619 tny_gtk_msg_view_display_parts (TnyMsgView *self, TnyList *parts, gboolean alternatives, const gchar *desc)
620 {
621         TnyIterator *iterator = tny_list_create_iterator (parts);
622
623         while (!tny_iterator_is_done (iterator))
624         {
625                 TnyMimePart *part = (TnyMimePart*)tny_iterator_get_current (iterator);
626                 /* gboolean displayed =  */
627
628                 tny_gtk_msg_view_display_part (self, part, desc);
629
630                 g_object_unref (G_OBJECT (part));
631
632                 /* TNY TODO: partial message retrieval: temporarily disabled
633                         alternatives detection */
634
635                 /* if (alternatives && displayed)
636                         break; */
637                 tny_iterator_next (iterator);
638         }
639
640         g_object_unref (iterator);
641
642 }
643
644 static void
645 tny_gtk_msg_view_set_msg (TnyMsgView *self, TnyMsg *msg)
646 {
647         TNY_GTK_MSG_VIEW_GET_CLASS (self)->set_msg(self, msg);
648 }
649
650 static void
651 tny_gtk_msg_view_set_msg_default (TnyMsgView *self, TnyMsg *msg)
652 {
653
654         tny_mime_part_view_set_part (TNY_MIME_PART_VIEW (self), TNY_MIME_PART (msg));
655
656         return;
657 }
658
659 static void
660 remove_mime_part_viewer (TnyMimePartView *mpview, GtkContainer *mpviewers)
661 {
662         gtk_container_remove (mpviewers, GTK_WIDGET (mpview));
663         return;
664 }
665
666 static void
667 tny_gtk_msg_view_clear (TnyMsgView *self)
668 {
669         TNY_GTK_MSG_VIEW_GET_CLASS (self)->clear(self);
670 }
671
672 static void
673 clear_prv (TnyGtkMsgViewPriv *priv)
674 {
675         g_list_foreach (priv->unattached_views, (GFunc)g_object_unref, NULL);
676         g_list_free (priv->unattached_views);
677         priv->unattached_views = NULL;
678
679         if (G_LIKELY (priv->part))
680                 g_object_unref (G_OBJECT (priv->part));
681         priv->part = NULL;
682 }
683
684 static void
685 tny_gtk_msg_view_clear_default (TnyMsgView *self)
686 {
687         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
688         GtkContainer *viewers = GTK_CONTAINER (TNY_GTK_MSG_VIEW (self)->viewers);
689         GList *kids = gtk_container_get_children (viewers);
690
691         g_list_foreach (kids, (GFunc)remove_mime_part_viewer, viewers);
692         g_list_free (kids);
693
694         clear_prv (priv);
695
696         gtk_icon_view_set_model (priv->attachview, tny_gtk_attach_list_model_new ());
697         gtk_widget_hide (priv->attachview_sw);
698         tny_header_view_set_header (priv->headerview, NULL);
699         gtk_widget_hide (GTK_WIDGET (priv->headerview));
700
701         return;
702 }
703
704
705
706 static void
707 tny_gtk_msg_view_mp_set_part (TnyMimePartView *self, TnyMimePart *part)
708 {
709         TNY_GTK_MSG_VIEW_GET_CLASS (self)->set_part(self, part);
710         return;
711 }
712
713 /**
714  * tny_gtk_msg_view_mp_set_part_default:
715  * @self: a #TnyGtkMsgView
716  * @part: a #TnyMimePart
717  *
718  * This is non-public API documentation
719  *
720  * In case part is a TnyMsg, first show the header, then the part itself
721  * (#TnyMsg inherits from #TnyMimePart) and then dance on the
722  * tny_gtk_msg_view_display_parts song for the parts that are in the message.
723  **/
724 static void
725 tny_gtk_msg_view_mp_set_part_default (TnyMimePartView *self, TnyMimePart *part)
726 {
727         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
728
729         tny_msg_view_clear (TNY_MSG_VIEW (self));
730
731         if (part)
732         {
733                 g_assert (TNY_IS_MIME_PART (part));
734
735                 if (TNY_IS_MSG (part))
736                 {
737                         TnyHeader *header = (TnyHeader*) tny_msg_get_header (TNY_MSG (part));
738                         if (header && TNY_IS_HEADER (header))
739                         {
740                                 tny_header_view_set_header (priv->headerview, header);
741                                 g_object_unref (G_OBJECT (header));
742                                 gtk_widget_show (GTK_WIDGET (priv->headerview));
743                         }
744                 }
745
746                 priv->part = g_object_ref (G_OBJECT (part));
747
748                 if (!tny_mime_part_content_type_is (part, "multipart/*")) {
749
750                         tny_gtk_msg_view_display_part (TNY_MSG_VIEW (self), part, NULL);
751
752                 } else {
753                         TnyList *list;
754                         list = tny_simple_list_new ();
755
756                         tny_mime_part_get_parts (part, list);
757                         tny_gtk_msg_view_display_parts (TNY_MSG_VIEW (self), list,
758                                 tny_mime_part_content_type_is (part, "multipart/alternative"),
759                                 tny_mime_part_get_description (part));
760                         g_object_unref (G_OBJECT (list));
761                 }
762         }
763
764         return;
765 }
766
767
768 static TnyMimePart*
769 tny_gtk_msg_view_mp_get_part (TnyMimePartView *self)
770 {
771         return TNY_GTK_MSG_VIEW_GET_CLASS (self)->get_part(self);
772 }
773
774
775 static TnyMimePart*
776 tny_gtk_msg_view_mp_get_part_default (TnyMimePartView *self)
777 {
778         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
779         return TNY_MIME_PART (g_object_ref (priv->part));
780 }
781
782 static void
783 tny_gtk_msg_view_mp_clear (TnyMimePartView *self)
784 {
785         tny_msg_view_clear (TNY_MSG_VIEW (self));
786 }
787
788
789 /**
790  * tny_gtk_msg_view_new:
791  *
792  * Create a new #TnyMsgView
793  *
794  * returns: (caller-owns): a new #TnyMsgView
795  * since: 1.0
796  * audience: application-developer
797  **/
798 TnyMsgView*
799 tny_gtk_msg_view_new (void)
800 {
801         TnyGtkMsgView *self = g_object_new (TNY_TYPE_GTK_MSG_VIEW, NULL);
802         return TNY_MSG_VIEW (self);
803 }
804
805
806 static TnyHeaderView *
807 tny_gtk_msg_view_create_header_view_default (TnyGtkMsgView *self)
808 {
809         return tny_gtk_header_view_new ();
810 }
811
812 static void
813 tny_gtk_msg_view_instance_init (GTypeInstance *instance, gpointer g_class)
814 {
815         TnyGtkMsgView *self = (TnyGtkMsgView *)instance;
816         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
817         GtkBox *vbox;
818
819         priv->kid = GTK_BOX (gtk_vbox_new (FALSE, 0));
820         vbox = priv->kid;
821
822         priv->parented = FALSE;
823         priv->in_expander = FALSE;
824
825         /* Defaults */
826         priv->display_html = FALSE;
827         priv->display_plain = TRUE;
828         priv->display_attachments = TRUE;
829         priv->display_rfc822 = TRUE;
830         priv->first_attachment = TRUE;
831         priv->unattached_views = NULL;
832         priv->part = NULL;
833
834
835         priv->headerview = TNY_GTK_MSG_VIEW_GET_CLASS (self)->create_header_view(self);
836
837         gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (priv->headerview), FALSE, FALSE, 0);
838
839         TNY_GTK_MSG_VIEW (self)->viewers = GTK_CONTAINER (gtk_vbox_new (FALSE, 0));
840         gtk_box_pack_start (GTK_BOX (vbox),
841                 GTK_WIDGET (TNY_GTK_MSG_VIEW (self)->viewers), FALSE, FALSE, 0);
842         gtk_widget_show (GTK_WIDGET (TNY_GTK_MSG_VIEW (self)->viewers));
843
844         priv->attachview_sw = gtk_scrolled_window_new (NULL, NULL);
845         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->attachview_sw),
846                                         GTK_SHADOW_NONE);
847         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->attachview_sw),
848                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
849         priv->attachview = GTK_ICON_VIEW (gtk_icon_view_new ());
850         gtk_icon_view_set_selection_mode (priv->attachview, GTK_SELECTION_SINGLE);
851         gtk_icon_view_set_text_column (priv->attachview,
852                         TNY_GTK_ATTACH_LIST_MODEL_FILENAME_COLUMN);
853         gtk_icon_view_set_pixbuf_column (priv->attachview,
854                         TNY_GTK_ATTACH_LIST_MODEL_PIXBUF_COLUMN);
855         gtk_icon_view_set_columns (priv->attachview, -1);
856         gtk_icon_view_set_item_width (priv->attachview, 100);
857         gtk_icon_view_set_column_spacing (priv->attachview, 10);
858
859         gtk_box_pack_start (GTK_BOX (vbox), priv->attachview_sw, FALSE, TRUE, 0);
860         gtk_container_add (GTK_CONTAINER (priv->attachview_sw), GTK_WIDGET (priv->attachview));
861
862         /* Default is a non-online viewer */
863         gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (vbox));
864
865         gtk_widget_show (GTK_WIDGET (vbox));
866         gtk_widget_hide (GTK_WIDGET (priv->headerview));
867         gtk_widget_show (GTK_WIDGET (priv->attachview));
868
869         return;
870 }
871
872 static void
873 widget_size_request (GtkWidget *widget, GtkRequisition *requisition)
874 {
875         if (((GtkBin *) widget)->child)
876                 gtk_widget_size_request (((GtkBin *) widget)->child, requisition);
877 }
878
879 static void
880 widget_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
881 {
882         widget->allocation = *allocation;
883
884         if (((GtkBin *) widget)->child)
885                 gtk_widget_size_allocate (((GtkBin *) widget)->child, allocation);
886 }
887
888
889 static void
890 tny_gtk_msg_view_finalize (GObject *object)
891 {
892         TnyGtkMsgView *self = (TnyGtkMsgView *)object; 
893         TnyGtkMsgViewPriv *priv = TNY_GTK_MSG_VIEW_GET_PRIVATE (self);
894
895         clear_prv (priv);
896
897         if (G_LIKELY (priv->part))
898                 g_object_unref (G_OBJECT (priv->part));
899
900         (*parent_class->finalize) (object);
901
902         return;
903 }
904
905 static void
906 tny_msg_view_init (gpointer g, gpointer iface_data)
907 {
908         TnyMsgViewIface *klass = (TnyMsgViewIface *)g;
909
910         klass->get_msg= tny_gtk_msg_view_get_msg;
911         klass->set_msg= tny_gtk_msg_view_set_msg;
912         klass->set_unavailable= tny_gtk_msg_view_set_unavailable;
913         klass->clear= tny_gtk_msg_view_clear;
914         klass->create_mime_part_view_for= tny_gtk_msg_view_create_mime_part_view_for;
915         klass->create_new_inline_viewer= tny_gtk_msg_view_create_new_inline_viewer;
916
917         return;
918 }
919
920
921 static void
922 tny_mime_part_view_init (gpointer g, gpointer iface_data)
923 {
924         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
925
926         klass->get_part= tny_gtk_msg_view_mp_get_part;
927         klass->set_part= tny_gtk_msg_view_mp_set_part;
928         klass->clear= tny_gtk_msg_view_mp_clear;
929
930         return;
931 }
932
933
934 static void
935 tny_gtk_msg_view_class_init (TnyGtkMsgViewClass *class)
936 {
937         GObjectClass *object_class;
938         GtkWidgetClass *widget_class;
939
940         parent_class = g_type_class_peek_parent (class);
941         object_class = (GObjectClass*) class;
942
943         widget_class = (GtkWidgetClass *) class;
944         widget_class->size_request  = widget_size_request;
945         widget_class->size_allocate = widget_size_allocate;
946
947         object_class->finalize = tny_gtk_msg_view_finalize;
948
949         class->get_part= tny_gtk_msg_view_mp_get_part_default;
950         class->set_part= tny_gtk_msg_view_mp_set_part_default;
951         class->get_msg= tny_gtk_msg_view_get_msg_default;
952         class->set_msg= tny_gtk_msg_view_set_msg_default;
953         class->set_unavailable= tny_gtk_msg_view_set_unavailable_default;
954         class->clear= tny_gtk_msg_view_clear_default;
955         class->create_mime_part_view_for= tny_gtk_msg_view_create_mime_part_view_for_default;
956         class->create_new_inline_viewer= tny_gtk_msg_view_create_new_inline_viewer_default;
957
958         class->create_header_view= tny_gtk_msg_view_create_header_view_default;
959
960         g_type_class_add_private (object_class, sizeof (TnyGtkMsgViewPriv));
961
962
963         return;
964 }
965
966 static gpointer
967 tny_gtk_msg_view_register_type (gpointer notused)
968 {
969         GType type = 0;
970
971         static const GTypeInfo info =
972                 {
973                   sizeof (TnyGtkMsgViewClass),
974                   NULL,   /* base_init */
975                   NULL,   /* base_finalize */
976                   (GClassInitFunc) tny_gtk_msg_view_class_init,   /* class_init */