1 /* Copyright (c) 2007, 2009, 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>
43 #include <modest-tny-msg.h>
45 static GObjectClass *parent_class = NULL;
54 typedef struct _ModestAttachmentsViewPrivate ModestAttachmentsViewPrivate;
56 struct _ModestAttachmentsViewPrivate
61 GtkWidget *rubber_start;
62 GtkWidget *press_att_view;
63 GtkWidget *previous_selection;
64 ModestAttachmentsViewStyle style;
67 #define MODEST_ATTACHMENTS_VIEW_GET_PRIVATE(o) \
68 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_ATTACHMENTS_VIEW, ModestAttachmentsViewPrivate))
70 static gboolean button_press_event (GtkWidget *widget, GdkEventButton *event, ModestAttachmentsView *atts_view);
71 static gboolean motion_notify_event (GtkWidget *widget, GdkEventMotion *event, ModestAttachmentsView *atts_view);
72 static gboolean button_release_event (GtkWidget *widget, GdkEventButton *event, ModestAttachmentsView *atts_view);
73 static gboolean key_press_event (GtkWidget *widget, GdkEventKey *event, ModestAttachmentsView *atts_view);
74 static gboolean focus_out_event (GtkWidget *widget, GdkEventFocus *event, ModestAttachmentsView *atts_view);
75 static gboolean focus (GtkWidget *widget, GtkDirectionType direction, ModestAttachmentsView *atts_view);
76 static GtkWidget *get_att_view_at_coords (ModestAttachmentsView *atts_view,
77 gdouble x, gdouble y);
78 static void unselect_all (ModestAttachmentsView *atts_view);
79 static void set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view);
80 static void select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2);
81 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
82 guint info, gpointer userdata);
83 static void own_clipboard (ModestAttachmentsView *atts_view);
85 static guint signals[LAST_SIGNAL] = {0};
88 * modest_attachments_view_new:
91 * Constructor for attachments view widget.
93 * Return value: a new #ModestAttachmentsView instance implemented for Gtk+
96 modest_attachments_view_new (TnyMsg *msg)
98 ModestAttachmentsView *self = g_object_new (MODEST_TYPE_ATTACHMENTS_VIEW,
99 "resize-mode", GTK_RESIZE_PARENT,
102 modest_attachments_view_set_message (self, msg);
104 return GTK_WIDGET (self);
108 add_digest_attachments (ModestAttachmentsView *attachments_view, TnyMimePart *part)
113 parts = TNY_LIST (tny_simple_list_new());
114 tny_mime_part_get_parts (TNY_MIME_PART (part), parts);
116 for (iter = tny_list_create_iterator(parts);
117 !tny_iterator_is_done (iter);
118 tny_iterator_next (iter)) {
119 TnyMimePart *cur_part = TNY_MIME_PART (tny_iterator_get_current (iter));
120 modest_attachments_view_add_attachment (attachments_view, cur_part, TRUE, 0);
121 g_object_unref (cur_part);
123 g_object_unref (iter);
124 g_object_unref (parts);
130 modest_attachments_view_set_message (ModestAttachmentsView *attachments_view, TnyMsg *msg)
132 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
135 gchar *msg_content_type = NULL;
136 TnyMimePart *part_to_check;
138 gboolean is_alternate;
140 if (msg == priv->msg) return;
143 g_object_unref (priv->msg);
145 g_object_ref (G_OBJECT(msg));
149 g_list_free (priv->selected);
150 priv->selected = NULL;
152 gtk_container_foreach (GTK_CONTAINER (priv->box), (GtkCallback) gtk_widget_destroy, NULL);
154 if (priv->msg == NULL) {
158 part_to_check = modest_tny_msg_get_attachments_parent (TNY_MSG (msg));
161 msg_content_type = modest_tny_mime_part_get_content_type (TNY_MIME_PART (part_to_check));
162 is_alternate = !strcasecmp (msg_content_type, "multipart/alternative");
164 /* If we couldn't find parent, just go through fallback */
165 msg_content_type = NULL;
166 is_alternate = FALSE;
169 /* If the top mime part is a multipart/related, we don't show the attachments, as they're
170 * embedded images in body */
171 if ((msg_content_type != NULL) && !strcasecmp (msg_content_type, "multipart/related")) {
172 gchar *header_content_type;
173 gboolean application_multipart = FALSE;
175 g_free (msg_content_type);
177 header_content_type = modest_tny_mime_part_get_headers_content_type (TNY_MIME_PART (part_to_check));
179 if ((header_content_type != NULL) &&
180 !strstr (header_content_type, "application/")) {
181 application_multipart = TRUE;
183 g_free (header_content_type);
185 if (application_multipart) {
186 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
187 g_object_unref (part_to_check);
191 gboolean direct_attach;
193 direct_attach = (!g_str_has_prefix (msg_content_type, "message/rfc822") &&
194 !g_str_has_prefix (msg_content_type, "multipart") &&
195 !g_str_has_prefix (msg_content_type, "text/"));
197 g_free (msg_content_type);
200 modest_attachments_view_add_attachment (attachments_view, TNY_MIME_PART (part_to_check), TRUE, 0);
201 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
202 g_object_unref (part_to_check);
207 parts = TNY_LIST (tny_simple_list_new ());
208 tny_mime_part_get_parts (TNY_MIME_PART (part_to_check), parts);
209 iter = tny_list_create_iterator (parts);
212 while (!tny_iterator_is_done (iter)) {
216 part = TNY_MIME_PART (tny_iterator_get_current (iter));
218 if (part && (modest_tny_mime_part_is_attachment_for_modest (part))) {
219 modest_attachments_view_add_attachment (attachments_view, part, TRUE, 0);
221 } else if (part && !is_alternate) {
222 content_type = g_ascii_strdown (tny_mime_part_get_content_type (part), -1);
223 g_strstrip (content_type);
225 if (g_str_has_prefix (content_type, "multipart/digest")) {
226 add_digest_attachments (attachments_view, part);
227 } else if (body_found && g_str_has_prefix (content_type, "text/")) {
228 modest_attachments_view_add_attachment (attachments_view, part, TRUE, 0);
229 } else if (g_str_has_prefix (content_type, "multipart/") ||
230 g_str_has_prefix (content_type, "text/")) {
237 g_object_unref (part);
239 tny_iterator_next (iter);
241 g_object_unref (iter);
242 g_object_unref (parts);
243 g_object_unref (part_to_check);
246 gtk_widget_queue_draw (GTK_WIDGET (attachments_view));
251 modest_attachments_view_add_attachment (ModestAttachmentsView *attachments_view, TnyMimePart *part,
252 gboolean detect_size, guint64 size)
254 GtkWidget *att_view = NULL;
255 ModestAttachmentsViewPrivate *priv = NULL;
257 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (attachments_view));
258 g_return_if_fail (TNY_IS_MIME_PART (part));
260 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
262 att_view = modest_attachment_view_new (part, detect_size);
264 modest_attachment_view_set_size (MODEST_ATTACHMENT_VIEW (att_view), size);
265 gtk_box_pack_start (GTK_BOX (priv->box), att_view, FALSE, FALSE, 0);
266 gtk_widget_show_all (att_view);
267 gtk_widget_queue_resize (GTK_WIDGET (attachments_view));
271 modest_attachments_view_remove_attachment (ModestAttachmentsView *atts_view, TnyMimePart *mime_part)
273 ModestAttachmentsViewPrivate *priv = NULL;
274 GList *box_children = NULL, *node = NULL;
275 ModestAttachmentView *found_att_view = NULL;
277 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
278 g_return_if_fail (TNY_IS_MIME_PART (mime_part));
280 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
281 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
283 for (node = box_children; node != NULL; node = g_list_next (node)) {
284 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
285 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
287 if (mime_part == cur_mime_part)
288 found_att_view = att_view;
290 g_object_unref (cur_mime_part);
292 if (found_att_view != NULL)
296 if (found_att_view) {
298 GtkWidget *next_widget = NULL;
299 GList *box_children = NULL;
301 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
302 node = g_list_find (box_children, found_att_view);
303 if (node && node->next)
304 next_widget = node->next->data;
306 g_list_free (box_children);
307 gtk_widget_destroy (GTK_WIDGET (found_att_view));
309 node = g_list_find (priv->selected, found_att_view);
311 priv->selected = g_list_delete_link (priv->selected, node);
312 if ((priv->selected == NULL) && (next_widget != NULL))
313 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view),
314 MODEST_ATTACHMENT_VIEW (next_widget));
316 own_clipboard (atts_view);
320 gtk_widget_queue_resize (GTK_WIDGET (atts_view));
324 modest_attachments_view_remove_attachment_by_id (ModestAttachmentsView *atts_view, const gchar *att_id)
326 ModestAttachmentsViewPrivate *priv = NULL;
327 GList *box_children = NULL, *node = NULL;
329 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view));
330 g_return_if_fail (att_id != NULL);
332 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
333 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
335 for (node = box_children; node != NULL; node = g_list_next (node)) {
336 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
337 TnyMimePart *cur_mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
338 const gchar *mime_part_id = NULL;
340 mime_part_id = tny_mime_part_get_content_id (cur_mime_part);
341 if ((mime_part_id != NULL) && (strcmp (mime_part_id, att_id) == 0)) {
342 gtk_widget_destroy (GTK_WIDGET (att_view));
343 priv->selected = g_list_remove (priv->selected, att_view);
346 g_object_unref (cur_mime_part);
349 own_clipboard (atts_view);
351 gtk_widget_queue_resize (GTK_WIDGET (atts_view));
355 modest_attachments_view_instance_init (GTypeInstance *instance, gpointer g_class)
357 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (instance);
360 priv->box = gtk_vbox_new (FALSE, 0);
361 priv->rubber_start = NULL;
362 priv->press_att_view = NULL;
363 priv->selected = NULL;
364 priv->style = MODEST_ATTACHMENTS_VIEW_STYLE_SELECTABLE;
366 gtk_container_add (GTK_CONTAINER (instance), priv->box);
367 gtk_event_box_set_above_child (GTK_EVENT_BOX (instance), TRUE);
369 g_signal_connect (G_OBJECT (instance), "button-press-event", G_CALLBACK (button_press_event), instance);
370 g_signal_connect (G_OBJECT (instance), "button-release-event", G_CALLBACK (button_release_event), instance);
371 g_signal_connect (G_OBJECT (instance), "motion-notify-event", G_CALLBACK (motion_notify_event), instance);
372 g_signal_connect (G_OBJECT (instance), "key-press-event", G_CALLBACK (key_press_event), instance);
373 g_signal_connect (G_OBJECT (instance), "focus-out-event", G_CALLBACK (focus_out_event), instance);
374 g_signal_connect (G_OBJECT (instance), "focus", G_CALLBACK (focus), instance);
376 GTK_WIDGET_SET_FLAGS (instance, GTK_CAN_FOCUS);
382 modest_attachments_view_finalize (GObject *object)
384 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (object);
387 g_object_unref (priv->msg);
391 (*parent_class->finalize) (object);
397 modest_attachments_view_class_init (ModestAttachmentsViewClass *klass)
399 GObjectClass *object_class;
400 GtkWidgetClass *widget_class;
402 parent_class = g_type_class_peek_parent (klass);
403 object_class = (GObjectClass*) klass;
404 widget_class = GTK_WIDGET_CLASS (klass);
406 object_class->finalize = modest_attachments_view_finalize;
408 klass->activate = NULL;
410 g_type_class_add_private (object_class, sizeof (ModestAttachmentsViewPrivate));
412 signals[ACTIVATE_SIGNAL] =
413 g_signal_new ("activate",
414 G_TYPE_FROM_CLASS (object_class),
415 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
416 G_STRUCT_OFFSET(ModestAttachmentsViewClass, activate),
418 g_cclosure_marshal_VOID__OBJECT,
419 G_TYPE_NONE, 1, G_TYPE_OBJECT);
421 signals[DELETE_SIGNAL] =
422 g_signal_new ("delete",
423 G_TYPE_FROM_CLASS (object_class),
424 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
425 G_STRUCT_OFFSET(ModestAttachmentsViewClass, delete),
427 g_cclosure_marshal_VOID__VOID,
434 modest_attachments_view_get_type (void)
436 static GType type = 0;
438 if (G_UNLIKELY(type == 0))
440 static const GTypeInfo info =
442 sizeof (ModestAttachmentsViewClass),
443 NULL, /* base_init */
444 NULL, /* base_finalize */
445 (GClassInitFunc) modest_attachments_view_class_init, /* class_init */
446 NULL, /* class_finalize */
447 NULL, /* class_data */
448 sizeof (ModestAttachmentsView),
450 modest_attachments_view_instance_init /* instance_init */
453 type = g_type_register_static (GTK_TYPE_EVENT_BOX,
454 "ModestAttachmentsView",
462 /* buttons signal events */
464 button_press_event (GtkWidget *widget,
465 GdkEventButton *event,
466 ModestAttachmentsView *atts_view)
468 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
469 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_SELECTABLE &&
470 !GTK_WIDGET_HAS_FOCUS (widget))
471 gtk_widget_grab_focus (widget);
473 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
474 GtkWidget *att_view = NULL;
476 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
477 (gint) event->x_root, (gint) event->y_root);
479 if (att_view != NULL) {
480 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS) {
481 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
482 } else if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_LINKS) {
483 priv->press_att_view = att_view;
484 set_selected (MODEST_ATTACHMENTS_VIEW (widget), MODEST_ATTACHMENT_VIEW (att_view));
485 gtk_grab_add (widget);
487 if (g_list_length (priv->selected) == 1) {
488 priv->previous_selection = GTK_WIDGET (priv->selected->data);
490 priv->previous_selection = NULL;
492 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
494 /* Do not select purged attachments */
495 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
496 set_selected (MODEST_ATTACHMENTS_VIEW (widget), MODEST_ATTACHMENT_VIEW (att_view));
497 priv->rubber_start = att_view;
498 gtk_grab_add (widget);
500 g_object_unref (mime_part);
509 button_release_event (GtkWidget *widget,
510 GdkEventButton *event,
511 ModestAttachmentsView *atts_view)
513 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
514 if (widget == gtk_grab_get_current ()) {
515 GtkWidget *att_view = NULL;
517 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
518 (gint) event->x_root, (gint) event->y_root);
520 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_LINKS) {
521 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
522 if (att_view == priv->press_att_view) {
523 TnyMimePart *mime_part;
524 mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
525 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
526 g_object_unref (mime_part);
528 priv->press_att_view = NULL;
531 if (priv->style != MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS &&
532 priv->rubber_start == att_view &&
533 priv->previous_selection == att_view) {
534 TnyMimePart *mime_part;
535 mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
536 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
537 g_object_unref (mime_part);
538 } else if (att_view != NULL) {
539 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
540 select_range (MODEST_ATTACHMENTS_VIEW (widget),
541 MODEST_ATTACHMENT_VIEW (priv->rubber_start),
542 MODEST_ATTACHMENT_VIEW (att_view));
544 priv->rubber_start = NULL;
546 gtk_grab_remove (widget);
552 motion_notify_event (GtkWidget *widget,
553 GdkEventMotion *event,
554 ModestAttachmentsView *atts_view)
556 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
557 if (gtk_grab_get_current () == widget) {
558 GtkWidget *att_view = NULL;
560 att_view = get_att_view_at_coords (MODEST_ATTACHMENTS_VIEW (widget),
561 (gint) event->x_root, (gint) event->y_root);
562 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_LINKS) {
563 if (att_view == priv->press_att_view) {
564 if (priv->selected == NULL)
565 set_selected (MODEST_ATTACHMENTS_VIEW (widget), MODEST_ATTACHMENT_VIEW (att_view));
567 if (priv->selected) {
568 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
573 if (att_view != NULL) {
574 unselect_all (MODEST_ATTACHMENTS_VIEW (widget));
575 select_range (MODEST_ATTACHMENTS_VIEW (widget),
576 MODEST_ATTACHMENT_VIEW (priv->rubber_start),
577 MODEST_ATTACHMENT_VIEW (att_view));
580 gdk_event_request_motions (event);
586 find_prev_or_next_not_purged (GList *list, gboolean prev, gboolean include_this)
593 tmp = g_list_previous (list);
595 tmp = g_list_next (list);
605 ModestAttachmentView *att_view = (ModestAttachmentView *) tmp->data;
606 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
608 /* Do not select purged attachments */
609 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
613 tmp = g_list_previous (tmp);
615 tmp = g_list_next (tmp);
618 g_object_unref (mime_part);
619 } while (!is_valid && tmp);
626 key_press_event (GtkWidget *widget,
628 ModestAttachmentsView *atts_view)
630 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
632 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS) {
633 unselect_all (atts_view);
637 /* If grabbed (for example rubber banding), escape leaves the rubberbanding mode */
638 if (gtk_grab_get_current () == widget) {
639 if (event->keyval == GDK_Escape) {
640 set_selected (MODEST_ATTACHMENTS_VIEW (widget),
641 MODEST_ATTACHMENT_VIEW (priv->rubber_start));
642 priv->rubber_start = NULL;
643 gtk_grab_remove (widget);
649 if (event->keyval == GDK_Up) {
650 ModestAttachmentView *current_sel = NULL;
651 gboolean move_out = FALSE;
652 GList * box_children, *new_sel, *first_child;
654 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
655 if (box_children == NULL) {
658 first_child = box_children;
659 first_child = find_prev_or_next_not_purged (box_children, FALSE, TRUE);
660 if (priv->selected != NULL && first_child != NULL) {
661 if (priv->selected->data != first_child->data)
662 current_sel = (ModestAttachmentView *) priv->selected->data;
671 GtkWidget *toplevel = NULL;
672 /* move cursor outside */
673 toplevel = gtk_widget_get_toplevel (widget);
674 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
675 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_UP);
676 unselect_all (atts_view);
678 new_sel = g_list_find (box_children, (gpointer) current_sel);
679 new_sel = find_prev_or_next_not_purged (new_sel, TRUE, FALSE);
680 /* We assume that we detected properly that
681 there is a not purge attachment so we don't
682 need to check NULL */
683 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
685 g_list_free (box_children);
689 if (event->keyval == GDK_Down) {
690 ModestAttachmentView *current_sel = NULL;
691 gboolean move_out = FALSE;
692 GList * box_children, *new_sel, *last_child = NULL;
694 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
696 if (box_children == NULL) {
699 last_child = g_list_last (box_children);
700 last_child = find_prev_or_next_not_purged (last_child, TRUE, TRUE);
701 if (priv->selected != NULL && last_child != NULL) {
702 GList *last_selected = g_list_last (priv->selected);
703 if (last_selected->data != last_child->data)
704 current_sel = (ModestAttachmentView *) last_selected->data;
713 GtkWidget *toplevel = NULL;
714 /* move cursor outside */
715 toplevel = gtk_widget_get_toplevel (widget);
716 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
717 g_signal_emit_by_name (toplevel, "move-focus", GTK_DIR_DOWN);
718 unselect_all (atts_view);
720 new_sel = g_list_find (box_children, (gpointer) current_sel);
721 new_sel = find_prev_or_next_not_purged (new_sel, FALSE, FALSE);
722 set_selected (MODEST_ATTACHMENTS_VIEW (atts_view), MODEST_ATTACHMENT_VIEW (new_sel->data));
724 g_list_free (box_children);
728 if (event->keyval == GDK_BackSpace) {
729 g_signal_emit (G_OBJECT (widget), signals[DELETE_SIGNAL], 0);
733 /* Activates selected item */
734 if (g_list_length (priv->selected) == 1) {
735 ModestAttachmentView *att_view = (ModestAttachmentView *) priv->selected->data;
736 if ((event->keyval == GDK_Return)) {
737 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
738 if (TNY_IS_MIME_PART (mime_part)) {
739 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0, mime_part);
740 g_object_unref (mime_part);
751 get_att_view_at_coords (ModestAttachmentsView *atts_view,
752 gdouble x, gdouble y)
754 ModestAttachmentsViewPrivate *priv = NULL;
755 GList *att_view_list, *node;
756 GtkWidget *result = NULL;
758 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
759 att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
761 for (node = att_view_list; node != NULL; node = g_list_next (node)) {
762 GtkWidget *att_view = (GtkWidget *) node->data;
763 gint pos_x, pos_y, w, h, int_x, int_y;
764 gint widget_x, widget_y;
766 gdk_window_get_origin (att_view->window, &widget_x, &widget_y);
770 w = att_view->allocation.width;
771 h = att_view->allocation.height;
773 int_x = (gint) x - GTK_WIDGET (atts_view)->allocation.x;
774 int_y = (gint) y - GTK_WIDGET (atts_view)->allocation.y;
776 if ((x >= pos_x) && (x <= (pos_x + w)) && (y >= pos_y) && (y <= (pos_y + h))) {
782 g_list_free (att_view_list);
787 unselect_all (ModestAttachmentsView *atts_view)
789 ModestAttachmentsViewPrivate *priv = NULL;
790 GList *att_view_list, *node;
792 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
793 att_view_list = gtk_container_get_children (GTK_CONTAINER (priv->box));
795 for (node = att_view_list; node != NULL; node = g_list_next (node)) {
796 GtkWidget *att_view = (GtkWidget *) node->data;
798 if (GTK_WIDGET_STATE (att_view) == GTK_STATE_SELECTED)
799 gtk_widget_set_state (att_view, GTK_STATE_NORMAL);
802 g_list_free (priv->selected);
803 priv->selected = NULL;
805 g_list_free (att_view_list);
809 set_selected (ModestAttachmentsView *atts_view, ModestAttachmentView *att_view)
811 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
814 unselect_all (atts_view);
815 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
817 g_list_free (priv->selected);
818 priv->selected = NULL;
819 if (TNY_IS_MIME_PART (part) && !tny_mime_part_is_purged (part)) {
820 gtk_widget_set_state (GTK_WIDGET (att_view), GTK_STATE_SELECTED);
821 priv->selected = g_list_append (priv->selected, att_view);
824 g_object_unref (part);
826 own_clipboard (atts_view);
830 select_range (ModestAttachmentsView *atts_view, ModestAttachmentView *att1, ModestAttachmentView *att2)
832 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
833 GList *children = NULL;
835 gboolean selecting = FALSE;
838 unselect_all (atts_view);
841 set_selected (atts_view, att1);
845 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
846 g_list_free (priv->selected);
847 priv->selected = NULL;
850 for (node = children; node != NULL; node = g_list_next (node)) {
851 if ((node->data == att1) || (node->data == att2)) {
852 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (node->data));
853 if (!tny_mime_part_is_purged (part)) {
854 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
855 priv->selected = g_list_append (priv->selected, node->data);
857 g_object_unref (part);
858 selecting = !selecting;
859 } else if (selecting) {
860 part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (node->data));
861 if (!tny_mime_part_is_purged (part)) {
862 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
863 priv->selected = g_list_append (priv->selected, node->data);
865 g_object_unref (part);
869 g_list_free (children);
871 own_clipboard (atts_view);
874 static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data,
875 guint info, gpointer userdata)
877 ModestAttachmentsView *atts_view = (ModestAttachmentsView *) userdata;
878 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
880 if ((priv->selected != NULL)&&(priv->selected->next == NULL)) {
881 if (info == MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX) {
882 /* MODEST_ATTACHMENT requested. As the content id is not filled in all the case, we'll
883 * use an internal index. This index is simply the index of the attachment in the vbox */
884 GList *box_children = NULL;
886 box_children = gtk_container_get_children (GTK_CONTAINER (priv->box));
887 index = g_list_index (box_children, priv->selected);
889 gchar *index_str = g_strdup_printf("%d", index);
890 gtk_selection_data_set_text (selection_data, index_str, -1);
898 modest_attachments_view_get_selection (ModestAttachmentsView *atts_view)
900 ModestAttachmentsViewPrivate *priv;
904 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
905 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
907 selection = tny_simple_list_new ();
908 for (node = priv->selected; node != NULL; node = g_list_next (node)) {
909 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
910 TnyMimePart *part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
911 tny_list_append (selection, (GObject *) part);
912 g_object_unref (part);
919 modest_attachments_view_get_attachments (ModestAttachmentsView *atts_view)
921 ModestAttachmentsViewPrivate *priv;
923 GList *children, *node= NULL;
925 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), NULL);
926 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
928 att_list = TNY_LIST (tny_simple_list_new ());
930 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
931 for (node = children; node != NULL; node = g_list_next (node)) {
932 GtkWidget *att_view = GTK_WIDGET (node->data);
933 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
934 tny_list_append (att_list, (GObject *) mime_part);
935 g_object_unref (mime_part);
937 g_list_free (children);
943 modest_attachments_view_select_all (ModestAttachmentsView *atts_view)
945 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
946 GList *children = NULL;
949 unselect_all (atts_view);
951 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_LINKS)
954 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
955 g_list_free (priv->selected);
956 priv->selected = NULL;
958 for (node = children; node != NULL; node = g_list_next (node)) {
959 ModestAttachmentView *att_view = (ModestAttachmentView *) node->data;
960 TnyMimePart *mime_part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
962 /* Do not select purged attachments */
963 if (TNY_IS_MIME_PART (mime_part) && !tny_mime_part_is_purged (mime_part)) {
964 gtk_widget_set_state (GTK_WIDGET (node->data), GTK_STATE_SELECTED);
965 priv->selected = g_list_append (priv->selected, node->data);
967 g_object_unref (mime_part);
969 g_list_free (children);
971 own_clipboard (atts_view);
975 modest_attachments_view_has_attachments (ModestAttachmentsView *atts_view)
977 ModestAttachmentsViewPrivate *priv;
981 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), FALSE);
982 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
984 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
985 result = (children != NULL);
986 g_list_free (children);
992 modest_attachments_view_get_sizes (ModestAttachmentsView *attachments_view,
993 gint *attachments_count,
994 guint64 *attachments_size)
996 ModestAttachmentsViewPrivate *priv;
997 GList *children, *node;
999 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (attachments_view));
1000 g_return_if_fail (attachments_count != NULL && attachments_size != NULL);
1002 *attachments_count = 0;
1003 *attachments_size = 0;
1005 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (attachments_view);
1007 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
1008 for (node = children; node != NULL; node = g_list_next (node)) {
1009 GtkWidget *att_view = (GtkWidget *) node->data;
1010 TnyMimePart *part = tny_mime_part_view_get_part (TNY_MIME_PART_VIEW (att_view));
1012 if (!tny_mime_part_is_purged (part)) {
1014 (*attachments_count) ++;
1015 size = modest_attachment_view_get_size (MODEST_ATTACHMENT_VIEW (att_view));
1017 /* we do a random estimation of the size of an attachment */
1020 *attachments_size += size;
1022 g_object_unref (part);
1024 g_list_free (children);
1028 dummy_clear_func (GtkClipboard *clipboard,
1029 gpointer user_data_or_owner)
1035 own_clipboard (ModestAttachmentsView *atts_view)
1037 GtkTargetEntry targets[] = {
1038 {MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE, 0, MODEST_ATTACHMENTS_VIEW_CLIPBOARD_TYPE_INDEX},
1041 gtk_clipboard_set_with_owner (gtk_widget_get_clipboard (GTK_WIDGET (atts_view), GDK_SELECTION_PRIMARY),
1042 targets, G_N_ELEMENTS (targets),
1043 clipboard_get, dummy_clear_func, G_OBJECT(atts_view));
1047 focus_out_event (GtkWidget *widget, GdkEventFocus *event, ModestAttachmentsView *atts_view)
1049 if (!gtk_widget_is_focus (widget))
1050 unselect_all (atts_view);
1056 focus (GtkWidget *widget, GtkDirectionType direction, ModestAttachmentsView *atts_view)
1058 ModestAttachmentsViewPrivate *priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
1059 GList *children = NULL;
1060 GtkWidget *toplevel = NULL;
1062 toplevel = gtk_widget_get_toplevel (widget);
1063 if (!gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)))
1066 if (priv->style != MODEST_ATTACHMENTS_VIEW_STYLE_NO_FOCUS) {
1067 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
1068 if (children != NULL) {
1069 set_selected (atts_view, MODEST_ATTACHMENT_VIEW (children->data));
1071 g_list_free (children);
1078 modest_attachments_view_set_style (ModestAttachmentsView *self,
1079 ModestAttachmentsViewStyle style)
1081 ModestAttachmentsViewPrivate *priv;
1083 g_return_if_fail (MODEST_IS_ATTACHMENTS_VIEW (self));
1084 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (self);
1086 if (priv->style != style) {
1087 priv->style = style;
1088 gtk_widget_queue_draw (GTK_WIDGET (self));
1089 if (priv->style == MODEST_ATTACHMENTS_VIEW_STYLE_SELECTABLE) {
1090 GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
1092 GTK_WIDGET_UNSET_FLAGS (self, GTK_CAN_FOCUS);
1099 modest_attachments_view_get_num_attachments (ModestAttachmentsView *atts_view)
1101 ModestAttachmentsViewPrivate *priv;
1105 g_return_val_if_fail (MODEST_IS_ATTACHMENTS_VIEW (atts_view), 0);
1106 priv = MODEST_ATTACHMENTS_VIEW_GET_PRIVATE (atts_view);
1108 children = gtk_container_get_children (GTK_CONTAINER (priv->box));
1109 result = g_list_length (children);
1110 g_list_free (children);