* src/widgets/modest-attachments-view.c:
[modest] / src / widgets / modest-attachments-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 #include <config.h>
31
32 //#include <glib/gi18n-lib.h>
33
34 #include <string.h>
35 #include <gtk/gtk.h>
36 #include <gdk/gdkkeysyms.h>
37 #include <tny-list.h>
38 #include <tny-simple-list.h>
39
40 #include <modest-platform.h>
41 #include <modest-runtime.h>
42 #include <modest-attachment-view.h>
43 #include <modest-attachments-view.h>
44
45 static GObjectClass *parent_class = NULL;
46
47 /* signals */
48 enum {
49         ACTIVATE_SIGNAL,
50         LAST_SIGNAL
51 };
52
53 typedef struct _ModestAttachmentsViewPrivate ModestAttachmentsViewPrivate;
54
55 struct _ModestAttachmentsViewPrivate
56 {
57         TnyMsg *msg;
58         GtkWidget *box;
59         GList *selected;
60         GtkWidget *rubber_start;
61 };
62
63 #define MODEST_ATTACHMENTS_VIEW_GET_PRIVATE(o)  \
64         (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_ATTACHMENTS_VIEW, ModestAttachmentsViewPrivate))
65
66 static gboolean button_press_event (GtkWidget *widget, GdkEventButton *event, ModestAttachmentsView *atts_view);
67 static gboolean motion_notify_event (GtkWidget *widget, GdkEventMotion *event, ModestAttachmentsView *atts_view);
68 static gboolean button_release_event (GtkWidget *widget, GdkEventButton *event, ModestAttachmentsView *atts_view);
69 static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event, ModestAttachmentsView *atts_view);
70 static gboolean focus_out_event (GtkWidget *widget, GdkEventFocus *event, ModestAttachmentsView *atts_view);
71 static GtkWidget *get_att_view_at_coords (ModestAttachmentsView *atts_view,
72                                           gdouble x, gdouble y);
73 static void unselect_all (ModestAttachmentsView *atts_view);
74 static void set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view);
75 static void select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2);
76 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
77                            guint info, gpointer userdata);
78 static void clipboard_clear (GtkClipboard *clipboard, gpointer userdata);
79 static void own_clipboard (ModestAttachmentsView *atts_view);
80
81 static guint signals[LAST_SIGNAL] = {0};
82
83 /**
84  * modest_attachments_view_new:
85  * @msg: a #TnyMsg
86  *
87  * Constructor for attachments view widget.
88  *
89  * Return value: a new #ModestAttachmentsView instance implemented for Gtk+
90  **/
91 GtkWidget*
92 modest_attachments_view_new (TnyMsg *msg)
93 {
94         ModestAttachmentsView *self = g_object_new (MODEST_TYPE_ATTACHMENTS_VIEW, 
95                                                     "resize-mode", GTK_RESIZE_PARENT,
96                                                     NULL);
97
98         modest_attachments_view_set_message (self, msg);
99
100         return GTK_WIDGET (self);
101 }
102
103 void
104 modest_attachments_view_set_message (ModestAttachmentsView *attachments_view, TnyMsg *msg)
105 {
106         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
107         TnyList *parts;
108         TnyIterator *iter;
109         
110         if (msg == priv->msg) return;
111
112         if (priv->msg)
113                 g_object_unref (priv->msg);
114         if (msg)
115                 g_object_ref (G_OBJECT(msg));
116         
117         priv->msg = msg;
118
119         g_list_free (priv->selected);
120         priv->selected = NULL;
121
122         gtk_container_foreach (GTK_CONTAINER (priv->box), (GtkCallback) gtk_widget_destroy, NULL);
123         
124         if (priv->msg == NULL) {
125                 return;
126         }
127
128         parts = TNY_LIST (tny_simple_list_new ());
129         tny_mime_part_get_parts (TNY_MIME_PART (priv->msg), parts);
130         iter = tny_list_create_iterator (parts);
131
132         while (!tny_iterator_is_done (iter)) {
133                 TnyMimePart *part;
134
135                 part = TNY_MIME_PART (tny_iterator_get_current (iter));
136                 if (part && (tny_mime_part_is_attachment (part) || TNY_IS_MSG (part))) {
137                         modest_attachments_view_add_attachment (attachments_view, part);
138                 }
139
140                 if (part)
141                         g_object_unref (part);
142
143                 tny_iterator_next (iter);
144         }
145
146         gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
147
148 }
149
150 void
151 modest_attachments_view_add_attachment (ModestAttachmentsView *attachments_view, TnyMimePart *part)
152 {
153         GtkWidget *att_view = NULL;
154         ModestAttachmentsViewPrivate *priv = NULL;
155
156         g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (attachments_view));
157         g_return_if_fail (TNY_IS_MIME_PART (part));
158
159         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
160
161         att_view = modest_attachment_view_new (part);
162         gtk_box_pack_end (GTK_BOX (priv->box), att_view, FALSE, FALSE, 0);
163         gtk_widget_show_all (att_view);
164 }
165
166 void
167 modest_attachments_view_remove_attachment (ModestAttachmentsView *atts_view, TnyMimePart *mime_part)
168 {
169         ModestAttachmentsViewPrivate *priv = NULL;
170         GList *box_children = NULL, *node = NULL;
171         ModestAttachmentView *found_att_view = NULL;
172
173         g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
174         g_return_if_fail (TNY_IS_MIME_PART (mime_part));
175
176         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
177         box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
178
179         for (node = box_children; node != NULL; node = g_list_next (node)) {
180                 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
181                 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
182
183                 if (mime_part == cur_mime_part)
184                         found_att_view = att_view;
185
186                 g_object_unref (cur_mime_part);
187
188                 if (found_att_view != NULL)
189                         break;
190         }
191
192         if (found_att_view) {
193                 GList *node = NULL;
194                 GtkWidget *next_widget = NULL;
195                 GList *box_children = NULL;
196
197                 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
198                 node = g_list_find (box_children, found_att_view);
199                 if (node->next)
200                         next_widget = node->next->data;
201
202                 g_list_free (box_children);
203
204                 node = g_list_find (priv->selected, found_att_view);
205                 if (node != NULL) {
206                         priv->selected = g_list_delete_link (priv->selected, node);
207                         gtk_widget_destroy (GTK_WIDGET (found_att_view));
208                         if ((priv->selected == NULL) && (next_widget != NULL))
209                                 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), 
210                                               MODEST_ATTACHMENT_VIEW (next_widget));
211                 }
212                 own_clipboard (atts_view);
213         }
214
215 }
216
217 void
218 modest_attachments_view_remove_attachment_by_id (ModestAttachmentsView *atts_view, const gchar *att_id)
219 {
220         ModestAttachmentsViewPrivate *priv = NULL;
221         GList *box_children = NULL, *node = NULL;
222
223         g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
224         g_return_if_fail (att_id != NULL);
225
226         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
227         box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
228
229         for (node = box_children; node != NULL; node = g_list_next (node)) {
230                 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
231                 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
232                 const gchar *mime_part_id = NULL;
233
234                 mime_part_id = tny_mime_part_get_content_id (cur_mime_part);
235                 if ((mime_part_id != NULL) && (strcmp (mime_part_id, att_id) == 0)) {
236                         gtk_widget_destroy (GTK_WIDGET (att_view));
237                         priv->selected = g_list_remove (priv->selected, att_view);
238                 }
239
240                 g_object_unref (cur_mime_part);
241         }
242
243         own_clipboard (atts_view);
244
245 }
246
247 static void
248 modest_attachments_view_instance_init (GTypeInstance *instance, gpointer g_class)
249 {
250         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (instance);
251
252         priv->msg = NULL;
253         priv->box = gtk_vbox_new (FALSE, 0);
254         priv->rubber_start = NULL;
255         priv->selected = NULL;
256
257         gtk_container_add (GTK_CONTAINER (instance), priv->box);
258         gtk_event_box_set_above_child (GTK_EVENT_BOX (instance), TRUE);
259
260         g_signal_connect (G_OBJECT (instance), "button-press-event", G_CALLBACK (button_press_event), instance);
261         g_signal_connect (G_OBJECT (instance), "button-release-event", G_CALLBACK (button_release_event), instance);
262         g_signal_connect (G_OBJECT (instance), "motion-notify-event", G_CALLBACK (motion_notify_event), instance);
263         g_signal_connect (G_OBJECT (instance), "key-press-event", G_CALLBACK (key_press_event), instance);
264         g_signal_connect (G_OBJECT (instance), "focus-out-event", G_CALLBACK (focus_out_event), instance);
265
266         GTK_WIDGET_SET_FLAGS (instance, GTK_CAN_FOCUS);
267
268         return;
269 }
270
271 static void
272 modest_attachments_view_finalize (GObject *object)
273 {
274         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (object);
275
276         if (priv->msg) {
277                 g_object_unref (priv->msg);
278                 priv->msg = NULL;
279         }
280
281         (*parent_class->finalize) (object);
282
283         return;
284 }
285
286 static void 
287 modest_attachments_view_class_init (ModestAttachmentsViewClass *klass)
288 {
289         GObjectClass *object_class;
290         GtkWidgetClass *widget_class;
291
292         parent_class = g_type_class_peek_parent (klass);
293         object_class = (GObjectClass*) klass;
294         widget_class = GTK_WIDGET_CLASS (klass);
295
296         object_class->finalize = modest_attachments_view_finalize;
297
298         klass->activate = NULL;
299
300         g_type_class_add_private (object_class, sizeof (ModestAttachmentsViewPrivate));
301
302         signals[ACTIVATE_SIGNAL] =
303                 g_signal_new ("activate",
304                               G_TYPE_FROM_CLASS (object_class),
305                               G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
306                               G_STRUCT_OFFSET(ModestAttachmentsViewClass, activate),
307                               NULL, NULL,
308                               g_cclosure_marshal_VOID__OBJECT,
309                               G_TYPE_NONE, 1, G_TYPE_OBJECT);
310         
311         return;
312 }
313
314 GType 
315 modest_attachments_view_get_type (void)
316 {
317         static GType type = 0;
318
319         if (G_UNLIKELY(type == 0))
320         {
321                 static const GTypeInfo info = 
322                 {
323                   sizeof (ModestAttachmentsViewClass),
324                   NULL,   /* base_init */
325                   NULL,   /* base_finalize */
326                   (GClassInitFunc) modest_attachments_view_class_init,   /* class_init */
327                   NULL,   /* class_finalize */
328                   NULL,   /* class_data */
329                   sizeof (ModestAttachmentsView),
330                   0,      /* n_preallocs */
331                   modest_attachments_view_instance_init    /* instance_init */
332                 };
333
334                 type = g_type_register_static (GTK_TYPE_EVENT_BOX,
335                         "ModestAttachmentsView",
336                         &info, 0);
337
338         }
339
340         return type;
341 }
342
343 /* buttons signal events */
344 static gboolean
345 button_press_event (GtkWidget *widget, 
346                     GdkEventButton *event,
347                     ModestAttachmentsView *atts_view)
348 {
349         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
350         if (!GTK_WIDGET_HAS_FOCUS (widget))
351                 gtk_widget_grab_focus (widget);
352
353         if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
354                 GtkWidget *att_view = NULL;
355
356                 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget), 
357                                                    (gint) event->x_root, (gint) event->y_root);
358
359                 if (att_view != NULL) {
360                         if (GTK_WIDGET_STATE (att_view) == GTK_STATE_SELECTED && (g_list_length (priv->selected) < 2)) {
361                                 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
362                                 if (TNY_IS_MIME_PART (mime_part)) {
363                                         g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
364                                         g_object_unref (mime_part);
365                                 }
366                         } else {
367                                 set_selected (MODEST_ATTACHMENTS_VIEW (widget), MODEST_ATTACHMENT_VIEW (att_view));
368                                 priv->rubber_start = att_view;
369                                 gtk_grab_add (widget);
370                         }
371                 }
372         }
373         return TRUE;
374
375 }
376
377 static gboolean
378 button_release_event (GtkWidget *widget,
379                       GdkEventButton *event,
380                       ModestAttachmentsView *atts_view)
381 {
382         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
383         if (widget == gtk_grab_get_current ()) {
384                 GtkWidget *att_view = NULL;
385
386                 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget), 
387                                                    (gint) event->x_root, (gint) event->y_root);
388
389                 if (att_view != NULL) {
390                         unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
391                         select_range (MODEST_ATTACHMENTS_VIEW (widget), 
392                                       MODEST_ATTACHMENT_VIEW (priv->rubber_start), 
393                                       MODEST_ATTACHMENT_VIEW (att_view));
394                 }
395                 priv->rubber_start = NULL;
396                 gtk_grab_remove (widget);
397         }
398         return TRUE;
399 }
400
401 static gboolean
402 motion_notify_event (GtkWidget *widget,
403                      GdkEventMotion *event,
404                      ModestAttachmentsView *atts_view)
405 {
406         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
407         if (gtk_grab_get_current () == widget) {
408                 GtkWidget *att_view = NULL;
409
410                 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget), 
411                                                    (gint) event->x_root, (gint) event->y_root);
412
413                 if (att_view != NULL) {
414                         unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
415                         select_range (MODEST_ATTACHMENTS_VIEW (widget), 
416                                       MODEST_ATTACHMENT_VIEW (priv->rubber_start), 
417                                       MODEST_ATTACHMENT_VIEW (att_view));
418                 }
419         }
420         return TRUE;
421 }
422
423 static gboolean
424 key_press_event (GtkWidget *widget,
425                  GdkEventKey *event,
426                  ModestAttachmentsView *atts_view)
427 {
428         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
429
430         /* If grabbed (for example rubber banding), escape leaves the rubberbanding mode */
431         if (gtk_grab_get_current () == widget) {
432                 if (event->keyval == GDK_Escape) {
433                         set_selected (MODEST_ATTACHMENTS_VIEW (widget),
434                                       MODEST_ATTACHMENT_VIEW (priv->rubber_start));
435                         priv->rubber_start = NULL;
436                         gtk_grab_remove (widget);
437                         return TRUE;
438                 } 
439                 return FALSE;
440         }
441
442         if (event->keyval == GDK_Up) {
443                 ModestAttachmentView *current_sel = NULL;
444                 gboolean move_out = FALSE;
445                 GList * box_children, *new_sel;
446
447                 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
448                 if (box_children == NULL)
449                         move_out = TRUE;
450                 else if ((priv->selected != NULL)&&(priv->selected->data != box_children->data))
451                         current_sel = (ModestAttachmentView *) priv->selected->data;
452                 else
453                         move_out = TRUE;
454
455                 if (move_out) {
456                         GtkWidget *toplevel = NULL;
457                         /* move cursor outside */
458                         toplevel = gtk_widget_get_toplevel (widget);
459                         if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
460                                 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_UP);
461                         unselect_all (atts_view);
462                 } else {
463                         new_sel = g_list_find (box_children, (gpointer) current_sel);
464                         new_sel = g_list_previous (new_sel);
465                         set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
466                 }
467                 g_list_free (box_children);
468                 return TRUE;
469         }
470
471         if (event->keyval == GDK_Down) {
472                 ModestAttachmentView *current_sel = NULL;
473                 gboolean move_out = FALSE;
474                 GList * box_children, *new_sel, *last_child = NULL;
475
476                 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
477
478                 if (box_children == NULL) {
479                         move_out = TRUE;
480                 } else {
481                         last_child = g_list_last (box_children);
482                         if (priv->selected != NULL) {
483                                 GList *last_selected = g_list_last (priv->selected);
484                                 if (last_selected->data != last_child->data)
485                                         current_sel = (ModestAttachmentView *) last_selected->data;
486                                 else
487                                         move_out = TRUE;
488                         } else {
489                                 move_out = TRUE;
490                         }
491                 }
492
493                 if (move_out) {
494                         GtkWidget *toplevel = NULL;
495                         /* move cursor outside */
496                         toplevel = gtk_widget_get_toplevel (widget);
497                         if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
498                                 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_DOWN);
499                         unselect_all (atts_view);
500                 } else {
501                         new_sel = g_list_find (box_children, (gpointer) current_sel);
502                         new_sel = g_list_next (new_sel);
503                         set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
504                 }
505                 g_list_free (box_children);
506                 return TRUE;
507         }
508
509         /* Activates selected item */
510         if (g_list_length (priv->selected) == 1) {
511                 ModestAttachmentView *att_view = (ModestAttachmentView *) priv->selected->data;
512                 if ((event->keyval == GDK_Return)) {
513                         TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
514                         if (TNY_IS_MIME_PART (mime_part)) {
515                                 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
516                                 g_object_unref (mime_part);
517                         }
518                         return TRUE;
519                 }
520         }
521
522         return FALSE;
523 }
524
525
526 static GtkWidget *
527 get_att_view_at_coords (ModestAttachmentsView *atts_view,
528                         gdouble x, gdouble y)
529 {
530         ModestAttachmentsViewPrivate *priv = NULL;
531         GList *att_view_list, *node;
532         GtkWidget *result = NULL;
533
534         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
535         att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
536         
537         for (node = att_view_list; node != NULL; node = g_list_next (node)) {
538                 GtkWidget *att_view = (GtkWidget *) node->data;
539                 gint pos_x, pos_y, w, h, int_x, int_y;
540                 gint widget_x, widget_y;
541
542                 gdk_window_get_origin (att_view->window, &widget_x, &widget_y);
543
544                 pos_x = widget_x;
545                 pos_y = widget_y;
546                 w = att_view->allocation.width;
547                 h = att_view->allocation.height;
548
549                 int_x = (gint) x - GTK_WIDGET (atts_view)->allocation.x;
550                 int_y = (gint) y - GTK_WIDGET (atts_view)->allocation.y;
551
552                 if ((x >= pos_x) && (x <= (pos_x + w)) && (y >= pos_y) && (y <= (pos_y + h))) {
553                         result = att_view;
554                         break;
555                 }
556         }
557
558         g_list_free (att_view_list);
559         return result;
560 }
561
562 static void
563 unselect_all (ModestAttachmentsView *atts_view)
564 {
565         ModestAttachmentsViewPrivate *priv = NULL;
566         GList *att_view_list, *node;
567
568         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
569         att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
570         
571         for (node = att_view_list; node != NULL; node = g_list_next (node)) {
572                 GtkWidget *att_view = (GtkWidget *) node->data;
573
574                 if (GTK_WIDGET_STATE (att_view) == GTK_STATE_SELECTED)
575                         gtk_widget_set_state (att_view, GTK_STATE_NORMAL);
576         }
577
578         g_list_free (priv->selected);
579         priv->selected = NULL;
580
581         g_list_free (att_view_list);
582 }
583
584 static void 
585 set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view)
586 {
587         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
588
589         unselect_all (atts_view);
590         gtk_widget_set_state (GTK_WIDGET (att_view), GTK_STATE_SELECTED);
591         g_list_free (priv->selected);
592         priv->selected = NULL;
593         priv->selected = g_list_append (priv->selected, att_view);
594         
595         own_clipboard (atts_view);
596 }
597
598 static void 
599 select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2)
600 {
601         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
602         GList *children = NULL;
603         GList *node = NULL;
604         gboolean selecting = FALSE;
605
606         unselect_all (atts_view);
607
608         if (att1 == att2) {
609                 set_selected (atts_view, att1);
610                 return;
611         }
612
613         children = gtk_container_get_children (GTK_CONTAINER (priv->box));
614         g_list_free (priv->selected);
615         priv->selected = NULL;
616
617
618         for (node = children; node != NULL; node = g_list_next (node)) {
619                 if ((node->data == att1) || (node->data == att2)) {
620                         gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
621                         priv->selected = g_list_append (priv->selected, node->data);
622                         selecting = !selecting;
623                 } else if (selecting) {
624                         gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
625                         priv->selected = g_list_append (priv->selected, node->data);
626                 }
627                         
628         }
629         g_list_free (children);
630         
631         own_clipboard (atts_view);
632 }
633
634 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
635                            guint info, gpointer userdata)
636 {
637         ModestAttachmentsView *atts_view = (ModestAttachmentsView *) userdata;
638         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
639
640         if ((priv->selected != NULL)&&(priv->selected->next == NULL)) {
641                 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (priv->selected->data));
642                 if (info != MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX) {
643                         if (TNY_IS_MSG (mime_part)) {
644                                 TnyHeader *header = tny_msg_get_header (TNY_MSG (mime_part));
645                                 if (TNY_IS_HEADER (header)) {
646                                         const gchar *subject = NULL;
647                                         subject = tny_header_get_subject (header);
648                                         if ((subject == NULL) || (subject[0] == '\0'))
649                                                 subject = _("mail_va_no_subject");
650                                         gtk_selection_data_set_text (selection_data, subject, -1);
651                                         g_object_unref (header);
652                                 }
653                         } else {
654                                 gtk_selection_data_set_text (selection_data, tny_mime_part_get_filename (mime_part), -1);
655                         }
656                 } else {
657                         /* MODEST_ATTACHMENT requested. As the content id is not filled in all the case, we'll
658                          * use an internal index. This index is simply the index of the attachment in the vbox */
659                         GList *box_children = NULL;
660                         gint index;
661                         box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
662                         index = g_list_index (box_children, priv->selected);
663                         if (index >= 0) {
664                                 gchar *index_str = g_strdup_printf("%d", index);
665                                 gtk_selection_data_set_text (selection_data, index_str, -1);
666                                 g_free (index_str);
667                         }
668                 }
669         }
670 }
671
672 static void clipboard_clear (GtkClipboard *clipboard, gpointer userdata)
673 {
674         ModestAttachmentsView *atts_view = (ModestAttachmentsView *) userdata;
675
676         unselect_all (atts_view);
677 }
678
679 GList *
680 modest_attachments_view_get_selection (ModestAttachmentsView *atts_view)
681 {
682         ModestAttachmentsViewPrivate *priv;
683         GList *selection, *node;
684
685         g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
686         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
687
688         selection = NULL;
689         for (node = priv->selected; node != NULL; node = g_list_next (node)) {
690                 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
691                 TnyMimePart *part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
692                 selection = g_list_append (selection, part);
693         }
694         
695         return selection;
696 }
697
698 GList *
699 modest_attachments_view_get_attachments (ModestAttachmentsView *atts_view)
700 {
701         ModestAttachmentsViewPrivate *priv;
702         GList *children, *node, *att_list = NULL;
703
704         g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
705         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
706
707         children = gtk_container_get_children (GTK_CONTAINER (priv->box));
708         for (node = children; node != NULL; node = g_list_next (node)) {
709                 GtkWidget *att_view = GTK_WIDGET (node->data);
710                 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
711                 att_list = g_list_prepend (att_list, mime_part);
712         }
713         g_list_free (children);
714         att_list = g_list_reverse (att_list);
715         return att_list;
716
717 }
718
719 void
720 modest_attachments_view_select_all (ModestAttachmentsView *atts_view)
721 {
722         ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
723         GList *children = NULL;
724         GList *node = NULL;
725
726         unselect_all (atts_view);
727
728         children = gtk_container_get_children (GTK_CONTAINER (priv->box));
729         g_list_free (priv->selected);
730         priv->selected = NULL;
731
732
733         for (node = children; node != NULL; node = g_list_next (node)) {
734                 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
735                 priv->selected = g_list_append (priv->selected, node->data);
736         }
737         g_list_free (children);
738
739         own_clipboard (atts_view);
740 }
741
742 gboolean
743 modest_attachments_view_has_attachments (ModestAttachmentsView *atts_view)
744 {
745         ModestAttachmentsViewPrivate *priv;
746         GList *children;
747         gboolean result;
748
749         g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), FALSE);
750         priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
751
752         children = gtk_container_get_children (GTK_CONTAINER (priv->box));
753         result = (children != NULL);
754         g_list_free (children);
755
756         return result;
757 }
758
759 static void
760 own_clipboard (ModestAttachmentsView *atts_view)
761 {
762         GtkTargetEntry targets[] = {
763                 {"TEXT", 0, 0},
764                 {"UTF8_STRING", 0, 1},
765                 {"COMPOUND_TEXT", 0, 2},
766                 {"STRING", 0, 3},
767                 {MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE, 0, MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX},
768         };
769
770         gtk_clipboard_set_with_owner (gtk_widget_get_clipboard (GTK_WIDGET (atts_view), GDK_SELECTION_PRIMARY),
771                                       targets, G_N_ELEMENTS (targets),
772                                       clipboard_get, clipboard_clear, G_OBJECT(atts_view));
773                               
774 }
775
776 static gboolean 
777 focus_out_event (GtkWidget *widget, GdkEventFocus *event, ModestAttachmentsView *atts_view)
778 {
779         if (!gtk_widget_is_focus (widget))
780                 unselect_all (atts_view);
781
782         return FALSE;
783 }