1 /* Copyright (c) 2007, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
34 #include <gdk/gdkkeysyms.h>
36 #include <tny-simple-list.h>
38 #include <modest-platform.h>
39 #include <modest-runtime.h>
40 #include <modest-attachment-view.h>
41 #include <modest-attachments-view.h>
42 #include <modest-tny-mime-part.h>
44 static GObjectClass *parent_class = NULL;
53 typedef struct _ModestAttachmentsViewPrivate ModestAttachmentsViewPrivate;
55 struct _ModestAttachmentsViewPrivate
60 GtkWidget *rubber_start;
63 #define MODEST_ATTACHMENTS_VIEW_GET_PRIVATE(o) \
64 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_ATTACHMENTS_VIEW, ModestAttachmentsViewPrivate))
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 gboolean focus (GtkWidget *widget, GtkDirectionType direction, ModestAttachmentsView *atts_view);
72 static GtkWidget *get_att_view_at_coords (ModestAttachmentsView *atts_view,
73 gdouble x, gdouble y);
74 static void unselect_all (ModestAttachmentsView *atts_view);
75 static void set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view);
76 static void select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2);
77 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
78 guint info, gpointer userdata);
79 static void own_clipboard (ModestAttachmentsView *atts_view);
81 static guint signals[LAST_SIGNAL] = {0};
84 * modest_attachments_view_new:
87 * Constructor for attachments view widget.
89 * Return value: a new #ModestAttachmentsView instance implemented for Gtk+
92 modest_attachments_view_new (TnyMsg *msg)
94 ModestAttachmentsView *self = g_object_new (MODEST_TYPE_ATTACHMENTS_VIEW,
95 "resize-mode", GTK_RESIZE_PARENT,
98 modest_attachments_view_set_message (self, msg);
100 return GTK_WIDGET (self);
105 modest_attachments_view_set_message (ModestAttachmentsView *attachments_view, TnyMsg *msg)
107 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
110 gchar *msg_content_type = NULL;
112 if (msg == priv->msg) return;
115 g_object_unref (priv->msg);
117 g_object_ref (G_OBJECT(msg));
121 g_list_free (priv->selected);
122 priv->selected = NULL;
124 gtk_container_foreach (GTK_CONTAINER (priv->box), (GtkCallback) gtk_widget_destroy, NULL);
126 if (priv->msg == NULL) {
130 /* If the top mime part is a multipart/related, we don't show the attachments, as they're
131 * embedded images in body */
132 msg_content_type = modest_tny_mime_part_get_content_type (TNY_MIME_PART (priv->msg));
133 if ((msg_content_type != NULL) && !strcasecmp (msg_content_type, "multipart/related")) {
134 gchar *header_content_type;
135 gboolean application_multipart = FALSE;
137 g_free (msg_content_type);
139 header_content_type = modest_tny_mime_part_get_headers_content_type (TNY_MIME_PART (priv->msg));
141 if ((header_content_type != NULL) &&
142 !strstr (header_content_type, "application/")) {
143 application_multipart = TRUE;
145 g_free (header_content_type);
147 if (application_multipart) {
148 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
152 gboolean direct_attach;
154 direct_attach = (!g_str_has_prefix (msg_content_type, "message/rfc822") &&
155 !g_str_has_prefix (msg_content_type, "multipart") &&
156 !g_str_has_prefix (msg_content_type, "text/"));
158 g_free (msg_content_type);
161 modest_attachments_view_add_attachment (attachments_view, TNY_MIME_PART (msg), TRUE, 0);
162 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
167 parts = TNY_LIST (tny_simple_list_new ());
168 tny_mime_part_get_parts (TNY_MIME_PART (priv->msg), parts);
169 iter = tny_list_create_iterator (parts);
171 while (!tny_iterator_is_done (iter)) {
174 part = TNY_MIME_PART (tny_iterator_get_current (iter));
176 if (part && (modest_tny_mime_part_is_attachment_for_modest (part)))
177 modest_attachments_view_add_attachment (attachments_view, part, TRUE, 0);
180 g_object_unref (part);
182 tny_iterator_next (iter);
184 g_object_unref (iter);
185 g_object_unref (parts);
188 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
193 modest_attachments_view_add_attachment (ModestAttachmentsView *attachments_view, TnyMimePart *part,
194 gboolean detect_size, guint64 size)
196 GtkWidget *att_view = NULL;
197 ModestAttachmentsViewPrivate *priv = NULL;
199 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (attachments_view));
200 g_return_if_fail (TNY_IS_MIME_PART (part));
202 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
204 att_view = modest_attachment_view_new (part, detect_size);
206 modest_attachment_view_set_size (MODEST_ATTACHMENT_VIEW (att_view), size);
207 gtk_box_pack_end (GTK_BOX (priv->box), att_view, FALSE, FALSE, 0);
208 gtk_widget_show_all (att_view);
212 modest_attachments_view_remove_attachment (ModestAttachmentsView *atts_view, TnyMimePart *mime_part)
214 ModestAttachmentsViewPrivate *priv = NULL;
215 GList *box_children = NULL, *node = NULL;
216 ModestAttachmentView *found_att_view = NULL;
218 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
219 g_return_if_fail (TNY_IS_MIME_PART (mime_part));
221 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
222 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
224 for (node = box_children; node != NULL; node = g_list_next (node)) {
225 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
226 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
228 if (mime_part == cur_mime_part)
229 found_att_view = att_view;
231 g_object_unref (cur_mime_part);
233 if (found_att_view != NULL)
237 if (found_att_view) {
239 GtkWidget *next_widget = NULL;
240 GList *box_children = NULL;
242 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
243 node = g_list_find (box_children, found_att_view);
245 next_widget = node->next->data;
247 g_list_free (box_children);
249 node = g_list_find (priv->selected, found_att_view);
251 priv->selected = g_list_delete_link (priv->selected, node);
252 gtk_widget_destroy (GTK_WIDGET (found_att_view));
253 if ((priv->selected == NULL) && (next_widget != NULL))
254 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view),
255 MODEST_ATTACHMENT_VIEW (next_widget));
257 own_clipboard (atts_view);
263 modest_attachments_view_remove_attachment_by_id (ModestAttachmentsView *atts_view, const gchar *att_id)
265 ModestAttachmentsViewPrivate *priv = NULL;
266 GList *box_children = NULL, *node = NULL;
268 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
269 g_return_if_fail (att_id != NULL);
271 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
272 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
274 for (node = box_children; node != NULL; node = g_list_next (node)) {
275 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
276 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
277 const gchar *mime_part_id = NULL;
279 mime_part_id = tny_mime_part_get_content_id (cur_mime_part);
280 if ((mime_part_id != NULL) && (strcmp (mime_part_id, att_id) == 0)) {
281 gtk_widget_destroy (GTK_WIDGET (att_view));
282 priv->selected = g_list_remove (priv->selected, att_view);
285 g_object_unref (cur_mime_part);
288 own_clipboard (atts_view);
293 modest_attachments_view_instance_init (GTypeInstance *instance, gpointer g_class)
295 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (instance);
298 priv->box = gtk_vbox_new (FALSE, 0);
299 priv->rubber_start = NULL;
300 priv->selected = NULL;
302 gtk_container_add (GTK_CONTAINER (instance), priv->box);
303 gtk_event_box_set_above_child (GTK_EVENT_BOX (instance), TRUE);
305 g_signal_connect (G_OBJECT (instance), "button-press-event", G_CALLBACK (button_press_event), instance);
306 g_signal_connect (G_OBJECT (instance), "button-release-event", G_CALLBACK (button_release_event), instance);
307 g_signal_connect (G_OBJECT (instance), "motion-notify-event", G_CALLBACK (motion_notify_event), instance);
308 g_signal_connect (G_OBJECT (instance), "key-press-event", G_CALLBACK (key_press_event), instance);
309 g_signal_connect (G_OBJECT (instance), "focus-out-event", G_CALLBACK (focus_out_event), instance);
310 g_signal_connect (G_OBJECT (instance), "focus", G_CALLBACK (focus), instance);
312 GTK_WIDGET_SET_FLAGS (instance, GTK_CAN_FOCUS);
318 modest_attachments_view_finalize (GObject *object)
320 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (object);
323 g_object_unref (priv->msg);
327 (*parent_class->finalize) (object);
333 modest_attachments_view_class_init (ModestAttachmentsViewClass *klass)
335 GObjectClass *object_class;
336 GtkWidgetClass *widget_class;
338 parent_class = g_type_class_peek_parent (klass);
339 object_class = (GObjectClass*) klass;
340 widget_class = GTK_WIDGET_CLASS (klass);
342 object_class->finalize = modest_attachments_view_finalize;
344 klass->activate = NULL;
346 g_type_class_add_private (object_class, sizeof (ModestAttachmentsViewPrivate));
348 signals[ACTIVATE_SIGNAL] =
349 g_signal_new ("activate",
350 G_TYPE_FROM_CLASS (object_class),
351 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
352 G_STRUCT_OFFSET(ModestAttachmentsViewClass, activate),
354 g_cclosure_marshal_VOID__OBJECT,
355 G_TYPE_NONE, 1, G_TYPE_OBJECT);
357 signals[DELETE_SIGNAL] =
358 g_signal_new ("delete",
359 G_TYPE_FROM_CLASS (object_class),
360 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
361 G_STRUCT_OFFSET(ModestAttachmentsViewClass, delete),
363 g_cclosure_marshal_VOID__VOID,
370 modest_attachments_view_get_type (void)
372 static GType type = 0;
374 if (G_UNLIKELY(type == 0))
376 static const GTypeInfo info =
378 sizeof (ModestAttachmentsViewClass),
379 NULL, /* base_init */
380 NULL, /* base_finalize */
381 (GClassInitFunc) modest_attachments_view_class_init, /* class_init */
382 NULL, /* class_finalize */
383 NULL, /* class_data */
384 sizeof (ModestAttachmentsView),
386 modest_attachments_view_instance_init /* instance_init */
389 type = g_type_register_static (GTK_TYPE_EVENT_BOX,
390 "ModestAttachmentsView",
398 /* buttons signal events */
400 button_press_event (GtkWidget *widget,
401 GdkEventButton *event,
402 ModestAttachmentsView *atts_view)
404 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
405 if (!GTK_WIDGET_HAS_FOCUS (widget))
406 gtk_widget_grab_focus (widget);
408 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
409 GtkWidget *att_view = NULL;
411 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
412 (gint) event->x_root, (gint) event->y_root);
414 if (att_view != NULL) {
415 if (GTK_WIDGET_STATE (att_view) == GTK_STATE_SELECTED && (g_list_length (priv->selected) < 2)) {
416 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
417 if (TNY_IS_MIME_PART (mime_part)) {
418 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
419 g_object_unref (mime_part);
422 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
424 /* Do not select purged attachments */
425 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
426 set_selected (MODEST_ATTACHMENTS_VIEW (widget), MODEST_ATTACHMENT_VIEW (att_view));
427 priv->rubber_start = att_view;
428 gtk_grab_add (widget);
430 g_object_unref (mime_part);
439 button_release_event (GtkWidget *widget,
440 GdkEventButton *event,
441 ModestAttachmentsView *atts_view)
443 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
444 if (widget == gtk_grab_get_current ()) {
445 GtkWidget *att_view = NULL;
447 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
448 (gint) event->x_root, (gint) event->y_root);
450 if (att_view != NULL) {
451 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
452 select_range (MODEST_ATTACHMENTS_VIEW (widget),
453 MODEST_ATTACHMENT_VIEW (priv->rubber_start),
454 MODEST_ATTACHMENT_VIEW (att_view));
456 priv->rubber_start = NULL;
457 gtk_grab_remove (widget);
463 motion_notify_event (GtkWidget *widget,
464 GdkEventMotion *event,
465 ModestAttachmentsView *atts_view)
467 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
468 if (gtk_grab_get_current () == widget) {
469 GtkWidget *att_view = NULL;
471 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
472 (gint) event->x_root, (gint) event->y_root);
474 if (att_view != NULL) {
475 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
476 select_range (MODEST_ATTACHMENTS_VIEW (widget),
477 MODEST_ATTACHMENT_VIEW (priv->rubber_start),
478 MODEST_ATTACHMENT_VIEW (att_view));
485 find_prev_or_next_not_purged (GList *list, gboolean prev, gboolean include_this)
492 tmp = g_list_previous (list);
494 tmp = g_list_next (list);
504 ModestAttachmentView *att_view = (ModestAttachmentView *) tmp->data;
505 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
507 /* Do not select purged attachments */
508 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
512 tmp = g_list_previous (tmp);
514 tmp = g_list_next (tmp);
517 g_object_unref (mime_part);
518 } while (!is_valid && tmp);
525 key_press_event (GtkWidget *widget,
527 ModestAttachmentsView *atts_view)
529 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
531 /* If grabbed (for example rubber banding), escape leaves the rubberbanding mode */
532 if (gtk_grab_get_current () == widget) {
533 if (event->keyval == GDK_Escape) {
534 set_selected (MODEST_ATTACHMENTS_VIEW (widget),
535 MODEST_ATTACHMENT_VIEW (priv->rubber_start));
536 priv->rubber_start = NULL;
537 gtk_grab_remove (widget);
543 if (event->keyval == GDK_Up) {
544 ModestAttachmentView *current_sel = NULL;
545 gboolean move_out = FALSE;
546 GList * box_children, *new_sel, *first_child;
548 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
549 if (box_children == NULL) {
552 first_child = box_children;
553 first_child = find_prev_or_next_not_purged (box_children, FALSE, TRUE);
554 if (priv->selected != NULL && first_child != NULL) {
555 if (priv->selected->data != first_child->data)
556 current_sel = (ModestAttachmentView *) priv->selected->data;
565 GtkWidget *toplevel = NULL;
566 /* move cursor outside */
567 toplevel = gtk_widget_get_toplevel (widget);
568 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
569 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_UP);
570 unselect_all (atts_view);
572 new_sel = g_list_find (box_children, (gpointer) current_sel);
573 new_sel = find_prev_or_next_not_purged (new_sel, TRUE, FALSE);
574 /* We assume that we detected properly that
575 there is a not purge attachment so we don't
576 need to check NULL */
577 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
579 g_list_free (box_children);
583 if (event->keyval == GDK_Down) {
584 ModestAttachmentView *current_sel = NULL;
585 gboolean move_out = FALSE;
586 GList * box_children, *new_sel, *last_child = NULL;
588 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
590 if (box_children == NULL) {
593 last_child = g_list_last (box_children);
594 last_child = find_prev_or_next_not_purged (last_child, TRUE, TRUE);
595 if (priv->selected != NULL && last_child != NULL) {
596 GList *last_selected = g_list_last (priv->selected);
597 if (last_selected->data != last_child->data)
598 current_sel = (ModestAttachmentView *) last_selected->data;
607 GtkWidget *toplevel = NULL;
608 /* move cursor outside */
609 toplevel = gtk_widget_get_toplevel (widget);
610 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
611 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_DOWN);
612 unselect_all (atts_view);
614 new_sel = g_list_find (box_children, (gpointer) current_sel);
615 new_sel = find_prev_or_next_not_purged (new_sel, FALSE, FALSE);
616 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
618 g_list_free (box_children);
622 if (event->keyval == GDK_BackSpace) {
623 g_signal_emit (G_OBJECT (widget), signals[DELETE_SIGNAL], 0);
627 /* Activates selected item */
628 if (g_list_length (priv->selected) == 1) {
629 ModestAttachmentView *att_view = (ModestAttachmentView *) priv->selected->data;
630 if ((event->keyval == GDK_Return)) {
631 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
632 if (TNY_IS_MIME_PART (mime_part)) {
633 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
634 g_object_unref (mime_part);
645 get_att_view_at_coords (ModestAttachmentsView *atts_view,
646 gdouble x, gdouble y)
648 ModestAttachmentsViewPrivate *priv = NULL;
649 GList *att_view_list, *node;
650 GtkWidget *result = NULL;
652 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
653 att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
655 for (node = att_view_list; node != NULL; node = g_list_next (node)) {
656 GtkWidget *att_view = (GtkWidget *) node->data;
657 gint pos_x, pos_y, w, h, int_x, int_y;
658 gint widget_x, widget_y;
660 gdk_window_get_origin (att_view->window, &widget_x, &widget_y);
664 w = att_view->allocation.width;
665 h = att_view->allocation.height;
667 int_x = (gint) x - GTK_WIDGET (atts_view)->allocation.x;
668 int_y = (gint) y - GTK_WIDGET (atts_view)->allocation.y;
670 if ((x >= pos_x) && (x <= (pos_x + w)) && (y >= pos_y) && (y <= (pos_y + h))) {
676 g_list_free (att_view_list);
681 unselect_all (ModestAttachmentsView *atts_view)
683 ModestAttachmentsViewPrivate *priv = NULL;
684 GList *att_view_list, *node;
686 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
687 att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
689 for (node = att_view_list; node != NULL; node = g_list_next (node)) {
690 GtkWidget *att_view = (GtkWidget *) node->data;
692 if (GTK_WIDGET_STATE (att_view) == GTK_STATE_SELECTED)
693 gtk_widget_set_state (att_view, GTK_STATE_NORMAL);
696 g_list_free (priv->selected);
697 priv->selected = NULL;
699 g_list_free (att_view_list);
703 set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view)
705 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
708 unselect_all (atts_view);
709 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
711 g_list_free (priv->selected);
712 priv->selected = NULL;
713 if (TNY_IS_MIME_PART (part) && !tny_mime_part_is_purged (part)) {
714 gtk_widget_set_state (GTK_WIDGET (att_view), GTK_STATE_SELECTED);
715 priv->selected = g_list_append (priv->selected, att_view);
718 g_object_unref (part);
720 own_clipboard (atts_view);
724 select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2)
726 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
727 GList *children = NULL;
729 gboolean selecting = FALSE;
732 unselect_all (atts_view);
735 set_selected (atts_view, att1);
739 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
740 g_list_free (priv->selected);
741 priv->selected = NULL;
744 for (node = children; node != NULL; node = g_list_next (node)) {
745 if ((node->data == att1) || (node->data == att2)) {
746 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (node->data));
747 if (!tny_mime_part_is_purged (part)) {
748 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
749 priv->selected = g_list_append (priv->selected, node->data);
751 g_object_unref (part);
752 selecting = !selecting;
753 } else if (selecting) {
754 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (node->data));
755 if (!tny_mime_part_is_purged (part)) {
756 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
757 priv->selected = g_list_append (priv->selected, node->data);
759 g_object_unref (part);
763 g_list_free (children);
765 own_clipboard (atts_view);
768 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
769 guint info, gpointer userdata)
771 ModestAttachmentsView *atts_view = (ModestAttachmentsView *) userdata;
772 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
774 if ((priv->selected != NULL)&&(priv->selected->next == NULL)) {
775 if (info == MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX) {
776 /* MODEST_ATTACHMENT requested. As the content id is not filled in all the case, we'll
777 * use an internal index. This index is simply the index of the attachment in the vbox */
778 GList *box_children = NULL;
780 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
781 index = g_list_index (box_children, priv->selected);
783 gchar *index_str = g_strdup_printf("%d", index);
784 gtk_selection_data_set_text (selection_data, index_str, -1);
792 modest_attachments_view_get_selection (ModestAttachmentsView *atts_view)
794 ModestAttachmentsViewPrivate *priv;
798 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
799 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
801 selection = tny_simple_list_new ();
802 for (node = priv->selected; node != NULL; node = g_list_next (node)) {
803 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
804 TnyMimePart *part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
805 tny_list_append (selection, (GObject *) part);
806 g_object_unref (part);
813 modest_attachments_view_get_attachments (ModestAttachmentsView *atts_view)
815 ModestAttachmentsViewPrivate *priv;
817 GList *children, *node= NULL;
819 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
820 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
822 att_list = TNY_LIST (tny_simple_list_new ());
824 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
825 for (node = children; node != NULL; node = g_list_next (node)) {
826 GtkWidget *att_view = GTK_WIDGET (node->data);
827 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
828 tny_list_append (att_list, (GObject *) mime_part);
829 g_object_unref (mime_part);
831 g_list_free (children);
837 modest_attachments_view_select_all (ModestAttachmentsView *atts_view)
839 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
840 GList *children = NULL;
843 unselect_all (atts_view);
845 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
846 g_list_free (priv->selected);
847 priv->selected = NULL;
849 for (node = children; node != NULL; node = g_list_next (node)) {
850 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
851 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
853 /* Do not select purged attachments */
854 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
855 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
856 priv->selected = g_list_append (priv->selected, node->data);
858 g_object_unref (mime_part);
860 g_list_free (children);
862 own_clipboard (atts_view);
866 modest_attachments_view_has_attachments (ModestAttachmentsView *atts_view)
868 ModestAttachmentsViewPrivate *priv;
872 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), FALSE);
873 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
875 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
876 result = (children != NULL);
877 g_list_free (children);
883 modest_attachments_view_get_sizes (ModestAttachmentsView *attachments_view,
884 gint *attachments_count,
885 guint64 *attachments_size)
887 ModestAttachmentsViewPrivate *priv;
888 GList *children, *node;
890 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (attachments_view));
891 g_return_if_fail (attachments_count != NULL && attachments_size != NULL);
893 *attachments_count = 0;
894 *attachments_size = 0;
896 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
898 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
899 for (node = children; node != NULL; node = g_list_next (node)) {
900 GtkWidget *att_view = (GtkWidget *) node->data;
901 TnyMimePart *part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
903 if (!tny_mime_part_is_purged (part)) {
905 (*attachments_count) ++;
906 size = modest_attachment_view_get_size (MODEST_ATTACHMENT_VIEW (att_view));
908 /* we do a random estimation of the size of an attachment */
911 *attachments_size += size;
914 g_object_unref (part);
916 g_list_free (children);
920 own_clipboard (ModestAttachmentsView *atts_view)
922 GtkTargetEntry targets[] = {
923 {MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE, 0, MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX},
926 gtk_clipboard_set_with_owner (gtk_widget_get_clipboard (GTK_WIDGET (atts_view), GDK_SELECTION_PRIMARY),
927 targets, G_N_ELEMENTS (targets),
928 clipboard_get, NULL, G_OBJECT(atts_view));
933 focus_out_event (GtkWidget *widget, GdkEventFocus *event, ModestAttachmentsView *atts_view)
935 if (!gtk_widget_is_focus (widget))
936 unselect_all (atts_view);
942 focus (GtkWidget *widget, GtkDirectionType direction, ModestAttachmentsView *atts_view)
944 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
945 GList *children = NULL;
946 GtkWidget *toplevel = NULL;
948 toplevel = gtk_widget_get_toplevel (widget);
949 if (!gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)))
952 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
953 if (children != NULL) {
954 set_selected (atts_view, MODEST_ATTACHMENT_VIEW (children->data));
956 g_list_free (children);