* Removed a duplicated save_state call
[modest] / src / widgets / modest-attachment-view.c
1 /* Copyright (c) 2007, 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 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif /*HAVE_CONFIG_H*/
33
34 #include <glib/gi18n.h>
35
36 #include <string.h>
37 #include <modest-attachment-view.h>
38 #include <modest-platform.h>
39 #include <modest-text-utils.h>
40 #include <tny-msg.h>
41 #include <tny-camel-mem-stream.h>
42 #include <modest-mail-operation.h>
43 #include <modest-mail-operation-queue.h>
44 #include <modest-runtime.h>
45
46 #define GET_SIZE_BUFFER_SIZE 128
47
48 static GObjectClass *parent_class = NULL;
49
50 typedef struct _ModestAttachmentViewPrivate ModestAttachmentViewPrivate;
51
52 struct _ModestAttachmentViewPrivate
53 {
54         TnyMimePart *mime_part;
55
56         GtkWidget *icon;
57         GtkWidget *filename_view;
58         GtkWidget *size_view;
59
60         guint get_size_idle_id;
61         TnyStream *get_size_stream;
62         guint64 size;
63
64         PangoLayout *layout_full_filename;
65         gboolean is_purged;
66
67 };
68
69 #define UNKNOWN_FILE_ICON "qgn_list_gene_unknown_file"
70
71 #define MODEST_ATTACHMENT_VIEW_GET_PRIVATE(o)   \
72         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_ATTACHMENT_VIEW, ModestAttachmentViewPrivate))
73
74 /* TnyMimePartView functions */
75 static TnyMimePart *modest_attachment_view_get_part (TnyMimePartView *self);
76 static TnyMimePart *modest_attachment_view_get_part_default (TnyMimePartView *self);
77 static void modest_attachment_view_set_part (TnyMimePartView *self, TnyMimePart *mime_part);
78 static void modest_attachment_view_set_part_default (TnyMimePartView *self, TnyMimePart *mime_part);
79 static void modest_attachment_view_clear (TnyMimePartView *self);
80 static void modest_attachment_view_clear_default (TnyMimePartView *self);
81
82 /* Gtk events */
83 static void size_allocate (GtkWidget *widget, GtkAllocation *allocation);
84
85 /* GObject and GInterface management */
86 static void modest_attachment_view_instance_init (GTypeInstance *instance, gpointer g_class);
87 static void modest_attachment_view_finalize (GObject *object);
88 static void modest_attachment_view_class_init (ModestAttachmentViewClass *klass);
89 static void tny_mime_part_view_init (gpointer g, gpointer iface_data);
90
91
92
93 static void update_filename_request (ModestAttachmentView *self);
94
95
96 static gboolean
97 idle_get_mime_part_size_cb (gpointer userdata)
98 {
99         ModestAttachmentView *view = (ModestAttachmentView *) userdata;
100         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (view);
101         gchar *size_str;
102         gchar *label_text;
103         gdk_threads_enter ();
104
105         if (GTK_WIDGET_VISIBLE (view)) {
106                 size_str = modest_text_utils_get_display_size (priv->size);
107                 label_text = g_strdup_printf (" (%s)", size_str);
108                 g_free (size_str);
109                 gtk_label_set_text (GTK_LABEL (priv->size_view), label_text);
110                 g_free (label_text);
111         }
112
113         gdk_threads_leave ();
114
115         g_object_unref (view);
116
117         return FALSE;
118 }
119
120 static gpointer
121 get_mime_part_size_thread (gpointer thr_user_data)
122 {
123         ModestAttachmentView *view =  (ModestAttachmentView *) thr_user_data;
124         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (view);
125         gchar read_buffer[GET_SIZE_BUFFER_SIZE];
126         TnyStream *stream;
127         gssize readed_size;
128         gssize total = 0;
129
130         stream = tny_camel_mem_stream_new ();
131         tny_mime_part_decode_to_stream (priv->mime_part, stream);
132         tny_stream_reset (stream);
133         if (tny_stream_is_eos (stream)) {
134                 tny_stream_close (stream);
135                 stream = tny_mime_part_get_stream (priv->mime_part);
136         }
137         
138         while (!tny_stream_is_eos (stream)) {
139                 readed_size = tny_stream_read (stream, read_buffer, GET_SIZE_BUFFER_SIZE);
140                 total += readed_size;
141         }
142
143         priv->size = total;
144
145         g_idle_add (idle_get_mime_part_size_cb, g_object_ref (view));
146
147         g_object_unref (stream);
148         g_object_unref (view);
149
150         return NULL;
151 }
152
153 static TnyMimePart *
154 modest_attachment_view_get_part (TnyMimePartView *self)
155 {
156         return MODEST_ATTACHMENT_VIEW_GET_CLASS (self)->get_part_func (self);
157 }
158
159 static TnyMimePart *
160 modest_attachment_view_get_part_default (TnyMimePartView *self)
161 {
162         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (self);
163
164         if (priv->mime_part)
165                 return TNY_MIME_PART (g_object_ref (priv->mime_part));
166         else
167                 return NULL;
168 }
169
170 static void
171 update_filename_request (ModestAttachmentView *self)
172 {
173         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (self);
174         /* gint width, height; */
175         
176         pango_layout_set_text (PANGO_LAYOUT (priv->layout_full_filename), 
177                                gtk_label_get_text (GTK_LABEL (priv->filename_view)), -1);
178
179
180 }
181
182 static void
183 modest_attachment_view_set_part (TnyMimePartView *self, TnyMimePart *mime_part)
184 {
185         MODEST_ATTACHMENT_VIEW_GET_CLASS (self)->set_part_func (self, mime_part);
186         return;
187 }
188
189
190 static void
191 modest_attachment_view_set_part_default (TnyMimePartView *self, TnyMimePart *mime_part)
192 {
193         ModestAttachmentViewPrivate *priv = NULL;
194         gchar *filename = NULL;
195         gchar *file_icon_name = NULL;
196         gboolean show_size = FALSE;
197         
198         g_return_if_fail (TNY_IS_MIME_PART_VIEW (self));
199         g_return_if_fail (TNY_IS_MIME_PART (mime_part));
200         priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (self);
201
202         if (priv->mime_part != NULL) {
203                 g_object_unref (priv->mime_part);
204         }
205
206         priv->mime_part = mime_part;
207
208         priv->size = 0;
209         priv->is_purged = tny_mime_part_is_purged (mime_part);
210
211         if (TNY_IS_MSG (mime_part)) {
212                 TnyHeader *header = tny_msg_get_header (TNY_MSG (mime_part));
213                 if (TNY_IS_HEADER (header)) {
214                         filename = g_strdup (tny_header_get_subject (header));
215                         if (filename == NULL || filename[0] == '\0')
216                                 filename = g_strdup (_("mail_va_no_subject"));
217                         if (priv->is_purged)
218                                 file_icon_name = modest_platform_get_file_icon_name (NULL, NULL, NULL);
219                         else
220                                 file_icon_name = 
221                                         modest_platform_get_file_icon_name (
222                                                 NULL, tny_mime_part_get_content_type (mime_part), NULL);
223                         g_object_unref (header);
224                 }
225         } else {
226                 filename = g_strdup (tny_mime_part_get_filename (mime_part));
227                 if (priv->is_purged) {
228                         file_icon_name = modest_platform_get_file_icon_name (NULL, NULL, NULL);
229                 } else {
230                         file_icon_name = modest_platform_get_file_icon_name (
231                                 filename, tny_mime_part_get_content_type (mime_part), NULL);
232                         show_size = TRUE;
233                 }
234         }
235
236         if (file_icon_name) {
237                 gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon), file_icon_name, GTK_ICON_SIZE_MENU);
238                 g_free (file_icon_name);
239         } else {
240                 gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon), UNKNOWN_FILE_ICON, GTK_ICON_SIZE_MENU);
241         }
242
243         if (priv->is_purged) {
244                 gchar * label_str = g_markup_printf_escaped(
245                         "<span style='italic' foreground='grey'>%s</span>",
246                         filename);
247                 gtk_label_set_markup (GTK_LABEL (priv->filename_view), label_str);
248                 g_free (label_str);
249         } else {
250                 gtk_label_set_text (GTK_LABEL (priv->filename_view), filename);
251         }
252         g_free (filename);
253         update_filename_request (MODEST_ATTACHMENT_VIEW (self));
254
255         gtk_label_set_text (GTK_LABEL (priv->size_view), "");
256
257         if (show_size) {
258                 tny_camel_mem_stream_get_type ();
259                 g_object_ref (self);
260                 g_thread_create (get_mime_part_size_thread, self, FALSE, NULL);
261         }
262
263         gtk_widget_queue_draw (GTK_WIDGET (self));
264 }
265
266 static void
267 modest_attachment_view_clear (TnyMimePartView *self)
268 {
269         MODEST_ATTACHMENT_VIEW_GET_CLASS (self)->clear_func (self);
270         return;
271 }
272
273 static void
274 modest_attachment_view_clear_default (TnyMimePartView *self)
275 {
276         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (self);
277
278         if (priv->mime_part != NULL) {
279                 g_object_unref (priv->mime_part);
280                 priv->mime_part = NULL;
281         }
282
283         if (priv->get_size_idle_id != 0) {
284                 g_source_remove (priv->get_size_idle_id);
285                 priv->get_size_idle_id = 0;
286         }
287
288         if (priv->get_size_stream != NULL) {
289                 g_object_unref (priv->get_size_stream);
290                 priv->get_size_stream = NULL;
291         }
292
293         priv->size = 0;
294
295         gtk_image_set_from_icon_name (GTK_IMAGE (priv->icon), 
296                                       UNKNOWN_FILE_ICON,
297                                       GTK_ICON_SIZE_MENU);
298         gtk_label_set_text (GTK_LABEL (priv->filename_view), "");
299         update_filename_request (MODEST_ATTACHMENT_VIEW(self));
300         gtk_label_set_text (GTK_LABEL (priv->size_view), " ");
301
302         gtk_widget_queue_draw (GTK_WIDGET (self));
303 }
304
305
306 /**
307  * modest_attachment_view_new:
308  * @mime_part: a #TnyMimePart
309  *
310  * Constructor for attachment view widget.
311  *
312  * Return value: a new #ModestAttachmentView instance implemented for Gtk+
313  **/
314 GtkWidget*
315 modest_attachment_view_new (TnyMimePart *mime_part)
316 {
317         ModestAttachmentView *self = g_object_new (MODEST_TYPE_ATTACHMENT_VIEW, 
318                                                    NULL);
319
320         modest_attachment_view_set_part (TNY_MIME_PART_VIEW (self), mime_part);
321
322         return GTK_WIDGET (self);
323 }
324
325 static void
326 modest_attachment_view_instance_init (GTypeInstance *instance, gpointer g_class)
327 {
328         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (instance);
329         PangoContext *context;
330         GtkWidget *box = NULL;
331
332         priv->mime_part = NULL;
333         priv->icon = gtk_image_new ();
334         priv->filename_view = gtk_label_new ("");
335         gtk_label_set_line_wrap (GTK_LABEL (priv->filename_view), FALSE);
336         gtk_label_set_ellipsize (GTK_LABEL (priv->filename_view), PANGO_ELLIPSIZE_END);
337         gtk_label_set_single_line_mode (GTK_LABEL (priv->filename_view), TRUE);
338         gtk_label_set_selectable (GTK_LABEL (priv->filename_view), FALSE);
339         priv->size_view = gtk_label_new (" ");
340         gtk_label_set_line_wrap (GTK_LABEL (priv->size_view), FALSE);
341         gtk_label_set_selectable (GTK_LABEL (priv->size_view), FALSE);
342         gtk_misc_set_alignment (GTK_MISC (priv->size_view), 0.0, 0.5);
343         gtk_misc_set_alignment (GTK_MISC (priv->filename_view), 0.0, 0.5);
344
345         priv->get_size_idle_id = 0;
346         priv->get_size_stream = NULL;
347         priv->size = 0;
348
349         box = gtk_hbox_new (FALSE, 0);
350         gtk_box_pack_start (GTK_BOX (box), priv->icon, FALSE, FALSE, 0);
351         gtk_box_pack_start (GTK_BOX (box), priv->filename_view, TRUE, TRUE, 0);
352         gtk_box_pack_start (GTK_BOX (box), priv->size_view, FALSE, FALSE, 0);
353         gtk_container_add (GTK_CONTAINER (instance), box);
354
355 /*      gtk_widget_get_style */
356 /*      gtk_widget_modify_bg (instance, GTK_STATE_SELECTED, selection_color); */
357
358         context = gtk_widget_get_pango_context (priv->filename_view);
359         priv->layout_full_filename = pango_layout_new (context);
360         
361         pango_layout_set_ellipsize (priv->layout_full_filename, PANGO_ELLIPSIZE_NONE);
362
363         gtk_event_box_set_above_child (GTK_EVENT_BOX (instance), FALSE);
364         gtk_event_box_set_visible_window (GTK_EVENT_BOX (instance), TRUE);
365         gtk_widget_set_events (GTK_WIDGET (instance), 0);
366
367         GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (instance), GTK_CAN_FOCUS);
368
369         return;
370 }
371
372 static void
373 modest_attachment_view_finalize (GObject *object)
374 {
375         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (object);
376
377         if (priv->get_size_idle_id) {
378                 g_source_remove (priv->get_size_idle_id);
379                 priv->get_size_idle_id = 0;
380         }
381
382         if (priv->get_size_stream != NULL) {
383                 g_object_unref (priv->get_size_stream);
384                 priv->get_size_stream = NULL;
385         }
386
387         if (G_LIKELY (priv->mime_part)) {
388                 g_object_unref (G_OBJECT (priv->mime_part));
389                 priv->mime_part = NULL;
390         }
391
392         (*parent_class->finalize) (object);
393
394         return;
395 }
396
397 static void
398 size_allocate (GtkWidget *widget, GtkAllocation *allocation)
399 {
400         ModestAttachmentViewPrivate *priv = MODEST_ATTACHMENT_VIEW_GET_PRIVATE (widget);
401         gint width, width_diff;
402
403         GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
404         pango_layout_set_font_description (priv->layout_full_filename, pango_context_get_font_description(pango_layout_get_context (gtk_label_get_layout (GTK_LABEL (priv->filename_view)))));
405
406         pango_layout_get_pixel_size (priv->layout_full_filename, &width, NULL);
407         width_diff = priv->filename_view->allocation.width - width;
408         if (width_diff > 0) {
409                 GtkAllocation filename_alloc, filesize_alloc;
410                 filename_alloc = priv->filename_view->allocation;
411                 filesize_alloc = priv->size_view->allocation;
412                 filename_alloc.width -= width_diff;
413                 filesize_alloc.width += width_diff;
414                 filesize_alloc.x -= width_diff;
415                 gtk_widget_size_allocate (priv->filename_view, &filename_alloc);
416                 gtk_widget_size_allocate (priv->size_view, &filesize_alloc);
417         }
418         
419 }
420
421
422 static void 
423 modest_attachment_view_class_init (ModestAttachmentViewClass *klass)
424 {
425         GObjectClass *object_class;
426         GtkWidgetClass *widget_class;
427
428         parent_class = g_type_class_peek_parent (klass);
429         object_class = (GObjectClass*) klass;
430         widget_class = GTK_WIDGET_CLASS (klass);
431
432         object_class->finalize = modest_attachment_view_finalize;
433
434         klass->get_part_func = modest_attachment_view_get_part_default;
435         klass->set_part_func = modest_attachment_view_set_part_default;
436         klass->clear_func = modest_attachment_view_clear_default;
437
438         widget_class->size_allocate = size_allocate;
439
440         g_type_class_add_private (object_class, sizeof (ModestAttachmentViewPrivate));
441
442         return;
443 }
444
445 static void
446 tny_mime_part_view_init (gpointer g, gpointer iface_data)
447 {
448         TnyMimePartViewIface *klass = (TnyMimePartViewIface *)g;
449
450         klass->get_part_func = modest_attachment_view_get_part;
451         klass->set_part_func = modest_attachment_view_set_part;
452         klass->clear_func = modest_attachment_view_clear;
453
454         return;
455 }
456
457 GType 
458 modest_attachment_view_get_type (void)
459 {
460         static GType type = 0;
461
462         if (G_UNLIKELY(type == 0))
463         {
464                 static const GTypeInfo info = 
465                 {
466                   sizeof (ModestAttachmentViewClass),
467                   NULL,   /* base_init */
468                   NULL,   /* base_finalize */
469                   (GClassInitFunc) modest_attachment_view_class_init,   /* class_init */
470                   NULL,   /* class_finalize */
471                   NULL,   /* class_data */
472                   sizeof (ModestAttachmentView),
473                   0,      /* n_preallocs */
474                   modest_attachment_view_instance_init    /* instance_init */
475                 };
476
477                 static const GInterfaceInfo tny_mime_part_view_info =
478                 {
479                         (GInterfaceInitFunc) tny_mime_part_view_init, /* interface_init */
480                         NULL,        /* interface_finalize */
481                         NULL         /* interface_data */
482                 };
483
484                 type = g_type_register_static (GTK_TYPE_EVENT_BOX,
485                         "ModestAttachmentView",
486                         &info, 0);
487
488                 g_type_add_interface_static (type, TNY_TYPE_MIME_PART_VIEW,
489                                              &tny_mime_part_view_info);
490
491         }
492
493         return type;
494 }